Merge pull request #4949 from patriot1burke/client-storage-spi
KEYCLOAK-6228
This commit is contained in:
commit
8f09efab9d
72 changed files with 3547 additions and 715 deletions
|
@ -67,6 +67,7 @@ public class ClientRepresentation {
|
|||
private Boolean useTemplateMappers;
|
||||
private ResourceServerRepresentation authorizationSettings;
|
||||
private Map<String, Boolean> access;
|
||||
protected String origin;
|
||||
|
||||
|
||||
public String getId() {
|
||||
|
@ -384,4 +385,19 @@ public class ClientRepresentation {
|
|||
public void setAccess(Map<String, Boolean> access) {
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns id of ClientStorageProvider that loaded this user
|
||||
*
|
||||
* @return NULL if user stored locally
|
||||
*/
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CachedObject;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
@ -36,17 +37,15 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientAdapter implements ClientModel {
|
||||
public class ClientAdapter implements ClientModel, CachedObject {
|
||||
protected RealmCacheSession cacheSession;
|
||||
protected RealmModel cachedRealm;
|
||||
protected RealmCache cache;
|
||||
|
||||
protected ClientModel updated;
|
||||
protected CachedClient cached;
|
||||
|
||||
public ClientAdapter(RealmModel cachedRealm, CachedClient cached, RealmCacheSession cacheSession, RealmCache cache) {
|
||||
public ClientAdapter(RealmModel cachedRealm, CachedClient cached, RealmCacheSession cacheSession) {
|
||||
this.cachedRealm = cachedRealm;
|
||||
this.cache = cache;
|
||||
this.cacheSession = cacheSession;
|
||||
this.cached = cached;
|
||||
}
|
||||
|
@ -54,7 +53,7 @@ public class ClientAdapter implements ClientModel {
|
|||
private void getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerClientInvalidation(cached.getId(), cached.getClientId(), cachedRealm.getId());
|
||||
updated = cacheSession.getDelegate().getClientById(cached.getId(), cachedRealm);
|
||||
updated = cacheSession.getRealmDelegate().getClientById(cached.getId(), cachedRealm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
}
|
||||
|
@ -66,11 +65,16 @@ public class ClientAdapter implements ClientModel {
|
|||
protected boolean isUpdated() {
|
||||
if (updated != null) return true;
|
||||
if (!invalidated) return false;
|
||||
updated = cacheSession.getDelegate().getClientById(cached.getId(), cachedRealm);
|
||||
updated = cacheSession.getRealmDelegate().getClientById(cached.getId(), cachedRealm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCacheTimestamp() {
|
||||
return cached.getCacheTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateClient() {
|
||||
if (updated != null) updated.updateClient();
|
||||
|
|
|
@ -50,7 +50,7 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
|
|||
private void getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerClientTemplateInvalidation(cached.getId());
|
||||
updated = cacheSession.getDelegate().getClientTemplateById(cached.getId(), cachedRealm);
|
||||
updated = cacheSession.getRealmDelegate().getClientTemplateById(cached.getId(), cachedRealm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
|
|||
protected boolean isUpdated() {
|
||||
if (updated != null) return true;
|
||||
if (!invalidated) return false;
|
||||
updated = cacheSession.getDelegate().getClientTemplateById(cached.getId(), cachedRealm);
|
||||
updated = cacheSession.getRealmDelegate().getClientTemplateById(cached.getId(), cachedRealm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public class GroupAdapter implements GroupModel {
|
|||
protected void getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerGroupInvalidation(cached.getId());
|
||||
updated = cacheSession.getDelegate().getGroupById(cached.getId(), realm);
|
||||
updated = cacheSession.getRealmDelegate().getGroupById(cached.getId(), realm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class GroupAdapter implements GroupModel {
|
|||
protected boolean isUpdated() {
|
||||
if (updated != null) return true;
|
||||
if (!invalidated) return false;
|
||||
updated = cacheSession.getDelegate().getGroupById(cached.getId(), realm);
|
||||
updated = cacheSession.getRealmDelegate().getGroupById(cached.getId(), realm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.models.*;
|
|||
import org.keycloak.models.cache.CachedRealmModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -36,7 +37,6 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
protected CachedRealm cached;
|
||||
protected RealmCacheSession cacheSession;
|
||||
protected volatile RealmModel updated;
|
||||
protected RealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
|
||||
public RealmAdapter(KeycloakSession session, CachedRealm cached, RealmCacheSession cacheSession) {
|
||||
|
@ -49,7 +49,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
public RealmModel getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerRealmInvalidation(cached.getId(), cached.getName());
|
||||
updated = cacheSession.getDelegate().getRealm(cached.getId());
|
||||
updated = cacheSession.getRealmDelegate().getRealm(cached.getId());
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
return updated;
|
||||
|
@ -81,7 +81,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
protected boolean isUpdated() {
|
||||
if (updated != null) return true;
|
||||
if (!invalidated) return false;
|
||||
updated = cacheSession.getDelegate().getRealm(cached.getId());
|
||||
updated = cacheSession.getRealmDelegate().getRealm(cached.getId());
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
return true;
|
||||
}
|
||||
|
@ -1323,35 +1323,43 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public ComponentModel addComponentModel(ComponentModel model) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(model);
|
||||
executeEvictions(model);
|
||||
return updated.addComponentModel(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentModel importComponentModel(ComponentModel model) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(model);
|
||||
executeEvictions(model);
|
||||
return updated.importComponentModel(model);
|
||||
}
|
||||
|
||||
public void evictUsers(ComponentModel model) {
|
||||
String parentId = model.getParentId();
|
||||
evictUsers(parentId);
|
||||
}
|
||||
|
||||
public void evictUsers(String parentId) {
|
||||
if (parentId != null && !parentId.equals(getId())) {
|
||||
ComponentModel parent = getComponent(parentId);
|
||||
public void executeEvictions(ComponentModel model) {
|
||||
if (model == null) return;
|
||||
// If not realm component, check to see if it is a user storage provider child component (i.e. LDAP mapper)
|
||||
if (model.getParentId() != null && !model.getParentId().equals(getId())) {
|
||||
ComponentModel parent = getComponent(model.getParentId());
|
||||
if (parent != null && UserStorageProvider.class.getName().equals(parent.getProviderType())) {
|
||||
session.userCache().evict(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// invalidate entire user cache if we're dealing with user storage SPI
|
||||
if (UserStorageProvider.class.getName().equals(model.getProviderType())) {
|
||||
session.userCache().evict(this);
|
||||
}
|
||||
// invalidate entire realm if we're dealing with client storage SPI
|
||||
// entire realm because of client roles, client lists, and clients
|
||||
if (ClientStorageProvider.class.getName().equals(model.getProviderType())) {
|
||||
cacheSession.evictRealmOnRemoval(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateComponent(ComponentModel component) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(component);
|
||||
executeEvictions(component);
|
||||
updated.updateComponent(component);
|
||||
|
||||
}
|
||||
|
@ -1359,7 +1367,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public void removeComponent(ComponentModel component) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(component);
|
||||
executeEvictions(component);
|
||||
updated.removeComponent(component);
|
||||
|
||||
}
|
||||
|
@ -1367,7 +1375,6 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public void removeComponents(String parentId) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(parentId);
|
||||
updated.removeComponents(parentId);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface RealmCache {
|
||||
void clear();
|
||||
|
||||
CachedRealm getRealm(String id);
|
||||
|
||||
void invalidateRealm(CachedRealm realm);
|
||||
|
||||
void addRealm(CachedRealm realm);
|
||||
|
||||
CachedRealm getRealmByName(String name);
|
||||
|
||||
void invalidateRealmById(String id);
|
||||
|
||||
CachedClient getClient(String id);
|
||||
|
||||
void invalidateClient(CachedClient app);
|
||||
|
||||
void evictClientById(String id);
|
||||
|
||||
void addClient(CachedClient app);
|
||||
|
||||
void invalidateClientById(String id);
|
||||
|
||||
CachedRole getRole(String id);
|
||||
|
||||
void invalidateRole(CachedRole role);
|
||||
|
||||
void evictRoleById(String id);
|
||||
|
||||
void addRole(CachedRole role);
|
||||
|
||||
void invalidateRoleById(String id);
|
||||
|
||||
CachedGroup getGroup(String id);
|
||||
|
||||
void invalidateGroup(CachedGroup role);
|
||||
|
||||
void addGroup(CachedGroup role);
|
||||
|
||||
void invalidateGroupById(String id);
|
||||
|
||||
CachedClientTemplate getClientTemplate(String id);
|
||||
|
||||
void invalidateClientTemplate(CachedClientTemplate app);
|
||||
|
||||
void evictClientTemplateById(String id);
|
||||
|
||||
void addClientTemplate(CachedClientTemplate app);
|
||||
|
||||
void invalidateClientTemplateById(String id);
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
|
@ -26,6 +27,9 @@ import org.keycloak.models.cache.CachedRealmModel;
|
|||
import org.keycloak.models.cache.infinispan.entities.*;
|
||||
import org.keycloak.models.cache.infinispan.events.*;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -94,12 +98,13 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
public static final String ROLES_QUERY_SUFFIX = ".roles";
|
||||
protected RealmCacheManager cache;
|
||||
protected KeycloakSession session;
|
||||
protected RealmProvider delegate;
|
||||
protected RealmProvider realmDelegate;
|
||||
protected ClientProvider clientDelegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
protected Map<String, RealmAdapter> managedRealms = new HashMap<>();
|
||||
protected Map<String, ClientAdapter> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientTemplateAdapter> managedClientTemplates = new HashMap<>();
|
||||
protected Map<String, RoleAdapter> managedRoles = new HashMap<>();
|
||||
protected Map<String, GroupAdapter> managedGroups = new HashMap<>();
|
||||
|
@ -134,16 +139,25 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
return getRealmDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider getDelegate() {
|
||||
public RealmProvider getRealmDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(RealmProvider.class);
|
||||
return delegate;
|
||||
if (realmDelegate != null) return realmDelegate;
|
||||
realmDelegate = session.realmLocalStorage();
|
||||
return realmDelegate;
|
||||
}
|
||||
public ClientProvider getClientDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (clientDelegate != null) return clientDelegate;
|
||||
clientDelegate = session.clientStorageManager();
|
||||
return clientDelegate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id, String name) {
|
||||
|
@ -163,8 +177,8 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
private void invalidateClient(String id) {
|
||||
invalidations.add(id);
|
||||
ClientAdapter adapter = managedApplications.get(id);
|
||||
if (adapter != null) adapter.invalidate();
|
||||
ClientModel adapter = managedApplications.get(id);
|
||||
if (adapter != null && adapter instanceof ClientAdapter) ((ClientAdapter)adapter).invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -194,9 +208,9 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
invalidations.addAll(newInvalidations);
|
||||
// need to make sure that scope and group mapping clients and groups are invalidated
|
||||
for (String id : newInvalidations) {
|
||||
ClientAdapter adapter = managedApplications.get(id);
|
||||
if (adapter != null) {
|
||||
adapter.invalidate();
|
||||
ClientModel adapter = managedApplications.get(id);
|
||||
if (adapter != null && adapter instanceof ClientAdapter){
|
||||
((ClientAdapter)adapter).invalidate();
|
||||
continue;
|
||||
}
|
||||
GroupAdapter group = managedGroups.get(id);
|
||||
|
@ -319,7 +333,6 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
@Override
|
||||
public void commit() {
|
||||
try {
|
||||
if (delegate == null) return;
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
|
@ -360,14 +373,14 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmModel realm = getDelegate().createRealm(name);
|
||||
RealmModel realm = getRealmDelegate().createRealm(name);
|
||||
registerRealmInvalidation(realm.getId(), realm.getName());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmModel realm = getDelegate().createRealm(id, name);
|
||||
RealmModel realm = getRealmDelegate().createRealm(id, name);
|
||||
registerRealmInvalidation(realm.getId(), realm.getName());
|
||||
return realm;
|
||||
}
|
||||
|
@ -381,14 +394,14 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
boolean wasCached = false;
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
RealmModel model = getRealmDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedRealm(loaded, model);
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
wasCached =true;
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getRealm(id);
|
||||
return getRealmDelegate().getRealm(id);
|
||||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
|
@ -420,18 +433,18 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
}
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
RealmModel model = getRealmDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(model.getId())) return model;
|
||||
query = new RealmListQuery(loaded, cacheKey, model.getId());
|
||||
cache.addRevisioned(query, startupRevision);
|
||||
return model;
|
||||
} else if (invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
return getRealmDelegate().getRealmByName(name);
|
||||
} else {
|
||||
String realmId = query.getRealms().iterator().next();
|
||||
if (invalidations.contains(realmId)) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
return getRealmDelegate().getRealmByName(name);
|
||||
}
|
||||
return getRealm(realmId);
|
||||
}
|
||||
|
@ -444,7 +457,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
// Retrieve realms from backend
|
||||
List<RealmModel> backendRealms = getDelegate().getRealms();
|
||||
List<RealmModel> backendRealms = getRealmDelegate().getRealms();
|
||||
|
||||
// Return cache delegates to ensure cache invalidated during write operations
|
||||
List<RealmModel> cachedRealms = new LinkedList<RealmModel>();
|
||||
|
@ -460,22 +473,26 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RealmModel realm = getRealm(id);
|
||||
if (realm == null) return false;
|
||||
|
||||
cache.invalidateObject(id);
|
||||
invalidationEvents.add(RealmRemovedEvent.create(id, realm.getName()));
|
||||
cache.realmRemoval(id, realm.getName(), invalidations);
|
||||
return getDelegate().removeRealm(id);
|
||||
evictRealmOnRemoval(realm);
|
||||
return getRealmDelegate().removeRealm(id);
|
||||
}
|
||||
|
||||
public void evictRealmOnRemoval(RealmModel realm) {
|
||||
cache.invalidateObject(realm.getId());
|
||||
invalidationEvents.add(RealmRemovedEvent.create(realm.getId(), realm.getName()));
|
||||
cache.realmRemoval(realm.getId(), realm.getName(), invalidations);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||
ClientModel client = getDelegate().addClient(realm, clientId);
|
||||
ClientModel client = getRealmDelegate().addClient(realm, clientId);
|
||||
return addedClient(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
ClientModel client = getDelegate().addClient(realm, id, clientId);
|
||||
ClientModel client = getRealmDelegate().addClient(realm, id, clientId);
|
||||
return addedClient(realm, client);
|
||||
}
|
||||
|
||||
|
@ -515,7 +532,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRealmClientsQueryCacheKey(realm.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getClients(realm);
|
||||
return getClientDelegate().getClients(realm);
|
||||
}
|
||||
|
||||
ClientListQuery query = cache.get(cacheKey, ClientListQuery.class);
|
||||
|
@ -525,7 +542,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
List<ClientModel> model = getDelegate().getClients(realm);
|
||||
List<ClientModel> model = getClientDelegate().getClients(realm);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (ClientModel client : model) ids.add(client.getId());
|
||||
|
@ -540,7 +557,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
if (client == null) {
|
||||
// TODO: Handle with cluster invalidations too
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getClients(realm);
|
||||
return getRealmDelegate().getClients(realm);
|
||||
}
|
||||
list.add(client);
|
||||
}
|
||||
|
@ -563,13 +580,14 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
for (RoleModel role : client.getRoles()) {
|
||||
roleRemovalInvalidations(role.getId(), role.getName(), client.getId());
|
||||
}
|
||||
return getDelegate().removeClient(id, realm);
|
||||
return getRealmDelegate().removeClient(id, realm);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null) delegate.close();
|
||||
if (realmDelegate != null) realmDelegate.close();
|
||||
if (clientDelegate != null) clientDelegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -579,7 +597,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
|
||||
RoleModel role = getDelegate().addRealmRole(realm, id, name);
|
||||
RoleModel role = getRealmDelegate().addRealmRole(realm, id, name);
|
||||
addedRole(role.getId(), realm.getId());
|
||||
return role;
|
||||
}
|
||||
|
@ -589,7 +607,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRolesCacheKey(realm.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getRealmRoles(realm);
|
||||
return getRealmDelegate().getRealmRoles(realm);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
|
@ -599,7 +617,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
Set<RoleModel> model = getDelegate().getRealmRoles(realm);
|
||||
Set<RoleModel> model = getRealmDelegate().getRealmRoles(realm);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (RoleModel role : model) ids.add(role.getId());
|
||||
|
@ -613,7 +631,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RoleModel role = session.realms().getRoleById(id, realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getRealmRoles(realm);
|
||||
return getRealmDelegate().getRealmRoles(realm);
|
||||
}
|
||||
list.add(role);
|
||||
}
|
||||
|
@ -625,7 +643,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRolesCacheKey(client.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getClientRoles(realm, client);
|
||||
return getRealmDelegate().getClientRoles(realm, client);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
|
@ -635,7 +653,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
Set<RoleModel> model = getDelegate().getClientRoles(realm, client);
|
||||
Set<RoleModel> model = getRealmDelegate().getClientRoles(realm, client);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (RoleModel role : model) ids.add(role.getId());
|
||||
|
@ -649,7 +667,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RoleModel role = session.realms().getRoleById(id, realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getClientRoles(realm, client);
|
||||
return getRealmDelegate().getClientRoles(realm, client);
|
||||
}
|
||||
list.add(role);
|
||||
}
|
||||
|
@ -663,7 +681,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
|
||||
RoleModel role = getDelegate().addClientRole(realm, client, id, name);
|
||||
RoleModel role = getRealmDelegate().addClientRole(realm, client, id, name);
|
||||
addedRole(role.getId(), client.getId());
|
||||
return role;
|
||||
}
|
||||
|
@ -673,7 +691,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRoleByNameCacheKey(realm.getId(), name);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getRealmRole(realm, name);
|
||||
return getRealmDelegate().getRealmRole(realm, name);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
|
@ -683,7 +701,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RoleModel model = getDelegate().getRealmRole(realm, name);
|
||||
RoleModel model = getRealmDelegate().getRealmRole(realm, name);
|
||||
if (model == null) return null;
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, model.getId());
|
||||
logger.tracev("adding realm role cache miss: client {0} key {1}", realm.getName(), cacheKey);
|
||||
|
@ -693,7 +711,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getRealmRole(realm, name);
|
||||
return getRealmDelegate().getRealmRole(realm, name);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
@ -703,7 +721,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getRoleByNameCacheKey(client.getId(), name);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getClientRole(realm, client, name);
|
||||
return getRealmDelegate().getClientRole(realm, client, name);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
|
@ -713,7 +731,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RoleModel model = getDelegate().getClientRole(realm, client, name);
|
||||
RoleModel model = getRealmDelegate().getClientRole(realm, client, name);
|
||||
if (model == null) return null;
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, model.getId(), client.getClientId());
|
||||
logger.tracev("adding client role cache miss: client {0} key {1}", client.getClientId(), cacheKey);
|
||||
|
@ -723,7 +741,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getClientRole(realm, client, name);
|
||||
return getRealmDelegate().getClientRole(realm, client, name);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
@ -736,7 +754,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
invalidationEvents.add(RoleRemovedEvent.create(role.getId(), role.getName(), role.getContainer().getId()));
|
||||
roleRemovalInvalidations(role.getId(), role.getName(), role.getContainer().getId());
|
||||
|
||||
return getDelegate().removeRole(realm, role);
|
||||
return getRealmDelegate().removeRole(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -748,7 +766,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
RoleModel model = getRealmDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
if (model.isClientRole()) {
|
||||
|
@ -759,7 +777,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
cache.addRevisioned(cached, startupRevision);
|
||||
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getRoleById(id, realm);
|
||||
return getRealmDelegate().getRoleById(id, realm);
|
||||
} else if (managedRoles.containsKey(id)) {
|
||||
return managedRoles.get(id);
|
||||
}
|
||||
|
@ -777,14 +795,14 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
GroupModel model = getRealmDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedGroup(loaded, realm, model);
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getGroupById(id, realm);
|
||||
return getRealmDelegate().getGroupById(id, realm);
|
||||
} else if (managedGroups.containsKey(id)) {
|
||||
return managedGroups.get(id);
|
||||
}
|
||||
|
@ -800,7 +818,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
listInvalidations.add(realm.getId());
|
||||
|
||||
invalidationEvents.add(GroupMovedEvent.create(group, toParent, realm.getId()));
|
||||
getDelegate().moveGroup(realm, group, toParent);
|
||||
getRealmDelegate().moveGroup(realm, group, toParent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -808,7 +826,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getGroupsQueryCacheKey(realm.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getGroups(realm);
|
||||
return getRealmDelegate().getGroups(realm);
|
||||
}
|
||||
|
||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||
|
@ -818,7 +836,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
List<GroupModel> model = getDelegate().getGroups(realm);
|
||||
List<GroupModel> model = getRealmDelegate().getGroups(realm);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (GroupModel client : model) ids.add(client.getId());
|
||||
|
@ -832,7 +850,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
GroupModel group = session.realms().getGroupById(id, realm);
|
||||
if (group == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getGroups(realm);
|
||||
return getRealmDelegate().getGroups(realm);
|
||||
}
|
||||
list.add(group);
|
||||
}
|
||||
|
@ -844,12 +862,12 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups) {
|
||||
return getDelegate().getGroupsCount(realm, onlyTopGroups);
|
||||
return getRealmDelegate().getGroupsCount(realm, onlyTopGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getGroupsCountByNameContaining(RealmModel realm, String search) {
|
||||
return getDelegate().getGroupsCountByNameContaining(realm, search);
|
||||
return getRealmDelegate().getGroupsCountByNameContaining(realm, search);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -857,7 +875,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getTopGroupsQueryCacheKey(realm.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getTopLevelGroups(realm);
|
||||
return getRealmDelegate().getTopLevelGroups(realm);
|
||||
}
|
||||
|
||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||
|
@ -867,7 +885,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
List<GroupModel> model = getDelegate().getTopLevelGroups(realm);
|
||||
List<GroupModel> model = getRealmDelegate().getTopLevelGroups(realm);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (GroupModel client : model) ids.add(client.getId());
|
||||
|
@ -881,7 +899,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
GroupModel group = session.realms().getGroupById(id, realm);
|
||||
if (group == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getTopLevelGroups(realm);
|
||||
return getRealmDelegate().getTopLevelGroups(realm);
|
||||
}
|
||||
list.add(group);
|
||||
}
|
||||
|
@ -896,7 +914,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
String cacheKey = getTopGroupsQueryCacheKey(realm.getId() + first + max);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId() + first + max);
|
||||
if (queryDB) {
|
||||
return getDelegate().getTopLevelGroups(realm, first, max);
|
||||
return getRealmDelegate().getTopLevelGroups(realm, first, max);
|
||||
}
|
||||
|
||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||
|
@ -906,7 +924,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (Objects.isNull(query)) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
List<GroupModel> model = getDelegate().getTopLevelGroups(realm, first, max);
|
||||
List<GroupModel> model = getRealmDelegate().getTopLevelGroups(realm, first, max);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (GroupModel client : model) ids.add(client.getId());
|
||||
|
@ -920,7 +938,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
GroupModel group = session.realms().getGroupById(id, realm);
|
||||
if (Objects.isNull(group)) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getTopLevelGroups(realm);
|
||||
return getRealmDelegate().getTopLevelGroups(realm);
|
||||
}
|
||||
list.add(group);
|
||||
}
|
||||
|
@ -932,7 +950,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
|
||||
return getDelegate().searchForGroupByName(realm, search, first, max);
|
||||
return getRealmDelegate().searchForGroupByName(realm, search, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -946,12 +964,12 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
invalidationEvents.add(GroupRemovedEvent.create(group, realm.getId()));
|
||||
|
||||
return getDelegate().removeGroup(realm, group);
|
||||
return getRealmDelegate().removeGroup(realm, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupModel createGroup(RealmModel realm, String name) {
|
||||
GroupModel group = getDelegate().createGroup(realm, name);
|
||||
GroupModel group = getRealmDelegate().createGroup(realm, name);
|
||||
return groupAdded(realm, group);
|
||||
}
|
||||
|
||||
|
@ -965,7 +983,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
public GroupModel createGroup(RealmModel realm, String id, String name) {
|
||||
GroupModel group = getDelegate().createGroup(realm, id, name);
|
||||
GroupModel group = getRealmDelegate().createGroup(realm, id, name);
|
||||
return groupAdded(realm, group);
|
||||
}
|
||||
|
||||
|
@ -978,7 +996,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
addGroupEventIfAbsent(GroupMovedEvent.create(subGroup, null, realm.getId()));
|
||||
|
||||
getDelegate().addTopLevelGroup(realm, subGroup);
|
||||
getRealmDelegate().addTopLevelGroup(realm, subGroup);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1007,22 +1025,80 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
ClientModel model = getClientDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedClient(loaded, realm, model);
|
||||
logger.tracev("adding client by id cache miss: {0}", cached.getClientId());
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
ClientModel adapter = cacheClient(realm, model, loaded);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
return getRealmDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, null);
|
||||
ClientModel adapter = validateCache(realm, cached);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
protected ClientModel cacheClient(RealmModel realm, ClientModel delegate, Long revision) {
|
||||
if (invalidations.contains(delegate.getId())) return delegate;
|
||||
StorageId storageId = new StorageId(delegate.getId());
|
||||
CachedClient cached = null;
|
||||
ClientAdapter adapter = null;
|
||||
|
||||
if (!storageId.isLocal()) {
|
||||
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||
ClientStorageProviderModel model = new ClientStorageProviderModel(component);
|
||||
if (!model.isEnabled()) {
|
||||
return delegate;
|
||||
}
|
||||
ClientStorageProviderModel.CachePolicy policy = model.getCachePolicy();
|
||||
if (policy != null && policy == ClientStorageProviderModel.CachePolicy.NO_CACHE) {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
cached = new CachedClient(revision, realm, delegate);
|
||||
adapter = new ClientAdapter(realm, cached, this);
|
||||
|
||||
long lifespan = model.getLifespan();
|
||||
if (lifespan > 0) {
|
||||
cache.addRevisioned(cached, startupRevision, lifespan);
|
||||
} else {
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
}
|
||||
} else {
|
||||
cached = new CachedClient(revision, realm, delegate);
|
||||
adapter = new ClientAdapter(realm, cached, this);
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
|
||||
protected ClientModel validateCache(RealmModel realm, CachedClient cached) {
|
||||
if (!realm.getId().equals(cached.getRealm())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StorageId storageId = new StorageId(cached.getId());
|
||||
if (!storageId.isLocal()) {
|
||||
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||
ClientStorageProviderModel model = new ClientStorageProviderModel(component);
|
||||
|
||||
// although we do set a timeout, Infinispan has no guarantees when the user will be evicted
|
||||
// its also hard to test stuff
|
||||
if (model.shouldInvalidate(cached)) {
|
||||
registerClientInvalidation(cached.getId(), cached.getClientId(), realm.getId());
|
||||
return getClientDelegate().getClientById(cached.getId(), realm);
|
||||
}
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this);
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
String cacheKey = getClientByClientIdCacheKey(clientId, realm.getId());
|
||||
|
@ -1035,7 +1111,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
ClientModel model = getDelegate().getClientByClientId(clientId, realm);
|
||||
ClientModel model = getClientDelegate().getClientByClientId(clientId, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(model.getId())) return model;
|
||||
id = model.getId();
|
||||
|
@ -1043,11 +1119,11 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
logger.tracev("adding client by name cache miss: {0}", clientId);
|
||||
cache.addRevisioned(query, startupRevision);
|
||||
} else if (invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getClientByClientId(clientId, realm);
|
||||
return getClientDelegate().getClientByClientId(clientId, realm);
|
||||
} else {
|
||||
id = query.getClients().iterator().next();
|
||||
if (invalidations.contains(id)) {
|
||||
return getDelegate().getClientByClientId(clientId, realm);
|
||||
return getClientDelegate().getClientByClientId(clientId, realm);
|
||||
}
|
||||
}
|
||||
return getClientById(id, realm);
|
||||
|
@ -1066,13 +1142,13 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
ClientTemplateModel model = getRealmDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedClientTemplate(loaded, realm, model);
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getClientTemplateById(id, realm);
|
||||
return getRealmDelegate().getClientTemplateById(id, realm);
|
||||
} else if (managedClientTemplates.containsKey(id)) {
|
||||
return managedClientTemplates.get(id);
|
||||
}
|
||||
|
@ -1084,31 +1160,31 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
// Don't cache ClientInitialAccessModel for now
|
||||
@Override
|
||||
public ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count) {
|
||||
return getDelegate().createClientInitialAccessModel(realm, expiration, count);
|
||||
return getRealmDelegate().createClientInitialAccessModel(realm, expiration, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id) {
|
||||
return getDelegate().getClientInitialAccessModel(realm, id);
|
||||
return getRealmDelegate().getClientInitialAccessModel(realm, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeClientInitialAccessModel(RealmModel realm, String id) {
|
||||
getDelegate().removeClientInitialAccessModel(realm, id);
|
||||
getRealmDelegate().removeClientInitialAccessModel(realm, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
|
||||
return getDelegate().listClientInitialAccess(realm);
|
||||
return getRealmDelegate().listClientInitialAccess(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExpiredClientInitialAccess() {
|
||||
getDelegate().removeExpiredClientInitialAccess();
|
||||
getRealmDelegate().removeExpiredClientInitialAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess) {
|
||||
getDelegate().decreaseRemainingCount(realm, clientInitialAccess);
|
||||
getRealmDelegate().decreaseRemainingCount(realm, clientInitialAccess);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class RoleAdapter implements RoleModel {
|
|||
protected void getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerRoleInvalidation(cached.getId(), cached.getName(), getContainerId());
|
||||
updated = cacheSession.getDelegate().getRoleById(cached.getId(), realm);
|
||||
updated = cacheSession.getRealmDelegate().getRoleById(cached.getId(), realm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class RoleAdapter implements RoleModel {
|
|||
protected boolean isUpdated() {
|
||||
if (updated != null) return true;
|
||||
if (!invalidated) return false;
|
||||
updated = cacheSession.getDelegate().getRoleById(cached.getId(), realm);
|
||||
updated = cacheSession.getRealmDelegate().getRoleById(cached.getId(), realm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.models.cache.CachedObject;
|
||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
import org.keycloak.common.util.Time;
|
||||
|
@ -49,9 +50,11 @@ import org.keycloak.models.cache.infinispan.events.UserFederationLinkUpdatedEven
|
|||
import org.keycloak.models.cache.infinispan.events.UserFullInvalidationEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.UserUpdatedEvent;
|
||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
|
@ -144,7 +147,6 @@ public class UserCacheSession implements UserCache {
|
|||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
@ -296,46 +298,11 @@ public class UserCacheSession implements UserCache {
|
|||
|
||||
if (!storageId.isLocal()) {
|
||||
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||
UserStorageProviderModel model = new UserStorageProviderModel(component);
|
||||
CacheableStorageProviderModel model = new CacheableStorageProviderModel(component);
|
||||
|
||||
// although we do set a timeout, Infinispan has no guarantees when the user will be evicted
|
||||
// its also hard to test stuff
|
||||
boolean invalidate = false;
|
||||
if (!model.isEnabled()) {
|
||||
invalidate = true;
|
||||
} else {
|
||||
UserStorageProviderModel.CachePolicy policy = model.getCachePolicy();
|
||||
if (policy != null) {
|
||||
//String currentTime = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(Time.currentTimeMillis()));
|
||||
if (policy == UserStorageProviderModel.CachePolicy.NO_CACHE) {
|
||||
invalidate = true;
|
||||
} else if (cached.getCacheTimestamp() < model.getCacheInvalidBefore()) {
|
||||
invalidate = true;
|
||||
} else if (policy == UserStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
||||
if (cached.getCacheTimestamp() + model.getMaxLifespan() < Time.currentTimeMillis()) {
|
||||
invalidate = true;
|
||||
}
|
||||
} else if (policy == UserStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
||||
long dailyTimeout = dailyTimeout(model.getEvictionHour(), model.getEvictionMinute());
|
||||
dailyTimeout = dailyTimeout - (24 * 60 * 60 * 1000);
|
||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(dailyTimeout));
|
||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||
if (cached.getCacheTimestamp() <= dailyTimeout) {
|
||||
invalidate = true;
|
||||
}
|
||||
} else if (policy == UserStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
||||
int oneWeek = 7 * 24 * 60 * 60 * 1000;
|
||||
long weeklyTimeout = weeklyTimeout(model.getEvictionDay(), model.getEvictionHour(), model.getEvictionMinute());
|
||||
long lastTimeout = weeklyTimeout - oneWeek;
|
||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(weeklyTimeout));
|
||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||
if (cached.getCacheTimestamp() <= lastTimeout) {
|
||||
invalidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invalidate) {
|
||||
if (model.shouldInvalidate(cached)) {
|
||||
registerUserInvalidation(realm, cached);
|
||||
return getDelegate().getUserById(cached.getId(), realm);
|
||||
}
|
||||
|
@ -371,26 +338,11 @@ public class UserCacheSession implements UserCache {
|
|||
adapter = new UserAdapter(cached, this, session, realm);
|
||||
onCache(realm, adapter, delegate);
|
||||
|
||||
if (policy == null || policy == UserStorageProviderModel.CachePolicy.DEFAULT) {
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
long lifespan = model.getLifespan();
|
||||
if (lifespan > 0) {
|
||||
cache.addRevisioned(cached, startupRevision, lifespan);
|
||||
} else {
|
||||
long lifespan = -1;
|
||||
if (policy == UserStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
||||
if (model.getEvictionHour() > -1 && model.getEvictionMinute() > -1) {
|
||||
lifespan = dailyTimeout(model.getEvictionHour(), model.getEvictionMinute()) - Time.currentTimeMillis();
|
||||
}
|
||||
} else if (policy == UserStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
||||
if (model.getEvictionDay() > 0 && model.getEvictionHour() > -1 && model.getEvictionMinute() > -1) {
|
||||
lifespan = weeklyTimeout(model.getEvictionDay(), model.getEvictionHour(), model.getEvictionMinute()) - Time.currentTimeMillis();
|
||||
}
|
||||
} else if (policy == UserStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
||||
lifespan = model.getMaxLifespan();
|
||||
}
|
||||
if (lifespan > 0) {
|
||||
cache.addRevisioned(cached, startupRevision, lifespan);
|
||||
} else {
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
}
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
}
|
||||
} else {
|
||||
cached = new CachedUser(revision, realm, delegate, notBefore);
|
||||
|
@ -402,39 +354,6 @@ public class UserCacheSession implements UserCache {
|
|||
return adapter;
|
||||
}
|
||||
|
||||
|
||||
public static long dailyTimeout(int hour, int minute) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal2.set(Calendar.MINUTE, minute);
|
||||
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
||||
int add = (24 * 60 * 60 * 1000);
|
||||
cal.add(Calendar.MILLISECOND, add);
|
||||
} else {
|
||||
cal.add(Calendar.MILLISECOND, (int)(cal2.getTimeInMillis() - cal.getTimeInMillis()));
|
||||
}
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static long weeklyTimeout(int day, int hour, int minute) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal2.set(Calendar.MINUTE, minute);
|
||||
cal2.set(Calendar.DAY_OF_WEEK, day);
|
||||
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
||||
int add = (7 * 24 * 60 * 60 * 1000);
|
||||
cal2.add(Calendar.MILLISECOND, add);
|
||||
}
|
||||
|
||||
return cal2.getTimeInMillis();
|
||||
}
|
||||
|
||||
private void onCache(RealmModel realm, UserAdapter adapter, UserModel delegate) {
|
||||
((OnUserCache)getDelegate()).onCache(realm, adapter, delegate);
|
||||
((OnUserCache)session.userCredentialManager()).onCache(realm, adapter, delegate);
|
||||
|
@ -935,7 +854,7 @@ public class UserCacheSession implements UserCache {
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||
if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
|
||||
if (!component.getProviderType().equals(UserStorageProvider.class.getName()) && !component.getProviderType().equals(ClientStorageProvider.class.getName())) return;
|
||||
addRealmInvalidation(realm.getId()); // easier to just invalidate whole realm
|
||||
getDelegate().preRemove(realm, component);
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.keycloak.authorization.store.ScopeStore;
|
|||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
|
||||
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource;
|
||||
|
@ -64,6 +65,7 @@ import org.keycloak.models.cache.infinispan.authorization.events.ScopeRemovedEve
|
|||
import org.keycloak.models.cache.infinispan.authorization.events.ScopeUpdatedEvent;
|
||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -348,6 +350,9 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
protected class ResourceServerCache implements ResourceServerStore {
|
||||
@Override
|
||||
public ResourceServer create(String clientId) {
|
||||
if (!StorageId.isLocalStorage(clientId)) {
|
||||
throw new ModelException("Creating resource server from federated ClientModel not supported");
|
||||
}
|
||||
ResourceServer server = getResourceServerStoreDelegate().create(clientId);
|
||||
registerResourceServerInvalidation(server.getId());
|
||||
return server;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.cache.CachedObject;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
@ -8,7 +9,7 @@ import java.io.Serializable;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AbstractRevisioned implements Revisioned, Serializable {
|
||||
public class AbstractRevisioned implements Revisioned, Serializable, CachedObject {
|
||||
private String id;
|
||||
private Long revision;
|
||||
private final long cacheTimestamp = Time.currentTimeMillis();
|
||||
|
@ -38,6 +39,7 @@ public class AbstractRevisioned implements Revisioned, Serializable {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public long getCacheTimestamp() {
|
||||
return cacheTimestamp;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.keycloak.models.sessions.infinispan;
|
|||
import org.infinispan.Cache;
|
||||
import org.infinispan.client.hotrod.RemoteCache;
|
||||
import org.infinispan.context.Flag;
|
||||
import org.infinispan.stream.CacheCollectors;
|
||||
import org.infinispan.stream.SerializableSupplier;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.common.util.Time;
|
||||
|
@ -59,16 +61,21 @@ import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
|
|||
import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
|
||||
import org.keycloak.models.utils.SessionTimeoutHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
|
@ -297,16 +304,21 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
protected List<UserSessionModel> getUserSessions(final RealmModel realm, ClientModel client, int firstResult, int maxResults, final boolean offline) {
|
||||
final String clientUuid = client.getId();
|
||||
UserSessionPredicate predicate = UserSessionPredicate.create(realm.getId()).client(clientUuid);
|
||||
|
||||
return getUserSessionModels(realm, firstResult, maxResults, offline, predicate);
|
||||
}
|
||||
|
||||
protected List<UserSessionModel> getUserSessionModels(RealmModel realm, int firstResult, int maxResults, boolean offline, UserSessionPredicate predicate) {
|
||||
Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = getCache(offline);
|
||||
cache = CacheDecorators.skipCacheLoaders(cache);
|
||||
|
||||
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache = getClientSessionCache(offline);
|
||||
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCacheDecorated = CacheDecorators.skipCacheLoaders(clientSessionCache);
|
||||
|
||||
final String clientUuid = client.getId();
|
||||
|
||||
Stream<UserSessionEntity> stream = cache.entrySet().stream()
|
||||
.filter(UserSessionPredicate.create(realm.getId()).client(clientUuid))
|
||||
.filter(predicate)
|
||||
.map(Mappers.userSessionEntity())
|
||||
.sorted(Comparators.userSessionLastSessionRefresh());
|
||||
|
||||
|
@ -330,7 +342,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return sessions;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserSessionModel getUserSessionWithPredicate(RealmModel realm, String id, boolean offline, Predicate<UserSessionModel> predicate) {
|
||||
UserSessionModel userSession = getUserSession(realm, id, offline);
|
||||
|
@ -398,7 +409,22 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return getUserSessionsCount(realm, client, false);
|
||||
}
|
||||
|
||||
protected long getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
|
||||
@Override
|
||||
public Map<String, Long> getActiveClientSessionStats(RealmModel realm, boolean offline) {
|
||||
Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = getCache(offline);
|
||||
cache = CacheDecorators.skipCacheLoaders(cache);
|
||||
return cache.entrySet().stream()
|
||||
.filter(UserSessionPredicate.create(realm.getId()))
|
||||
.map(Mappers.authClientSessionSetMapper())
|
||||
.flatMap((Serializable & Function<Set<String>, Stream<? extends String>>)Mappers::toStream)
|
||||
.collect(
|
||||
CacheCollectors.serializableCollector(
|
||||
() -> Collectors.groupingBy(Function.identity(), Collectors.counting())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected long getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
|
||||
Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = getCache(offline);
|
||||
cache = CacheDecorators.skipCacheLoaders(cache);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.models.sessions.infinispan.stream;
|
||||
|
||||
import org.infinispan.stream.SerializableSupplier;
|
||||
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
|
||||
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
|
||||
|
@ -25,9 +26,15 @@ import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
|||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -125,4 +132,22 @@ public class Mappers {
|
|||
}
|
||||
}
|
||||
|
||||
private static class AuthClientSessionSetMapper implements Function<Map.Entry<String, SessionEntityWrapper<UserSessionEntity>>, Set<String>>, Serializable {
|
||||
|
||||
@Override
|
||||
public Set<String> apply(Map.Entry<String, SessionEntityWrapper<UserSessionEntity>> entry) {
|
||||
UserSessionEntity entity = entry.getValue().getEntity();
|
||||
return entity.getAuthenticatedClientSessions().keySet();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Stream<T> toStream(Collection<T> collection) {
|
||||
return collection.stream();
|
||||
}
|
||||
|
||||
public static Function<Map.Entry<String, SessionEntityWrapper<UserSessionEntity>>, Set<String>> authClientSessionSetMapper() {
|
||||
return new AuthClientSessionSetMapper();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.models.sessions.infinispan.initializer;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.cache.infinispan.UserCacheSession;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
|
@ -65,13 +66,13 @@ public class InitializerStateTest {
|
|||
|
||||
@Test
|
||||
public void testDailyTimeout() throws Exception {
|
||||
Date date = new Date(UserCacheSession.dailyTimeout(10, 30));
|
||||
Date date = new Date(CacheableStorageProviderModel.dailyTimeout(10, 30));
|
||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||
date = new Date(UserCacheSession.dailyTimeout(17, 45));
|
||||
date = new Date(CacheableStorageProviderModel.dailyTimeout(17, 45));
|
||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||
date = new Date(UserCacheSession.weeklyTimeout(Calendar.MONDAY, 13, 45));
|
||||
date = new Date(CacheableStorageProviderModel.weeklyTimeout(Calendar.MONDAY, 13, 45));
|
||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||
date = new Date(UserCacheSession.weeklyTimeout(Calendar.THURSDAY, 13, 45));
|
||||
date = new Date(CacheableStorageProviderModel.weeklyTimeout(Calendar.THURSDAY, 13, 45));
|
||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||
System.out.println("----");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
@ -80,7 +81,7 @@ public class InitializerStateTest {
|
|||
int min = cal.get(Calendar.MINUTE);
|
||||
date = new Date(cal.getTimeInMillis());
|
||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||
date = new Date(UserCacheSession.dailyTimeout(hour, min));
|
||||
date = new Date(CacheableStorageProviderModel.dailyTimeout(hour, min));
|
||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||
cal = Calendar.getInstance();
|
||||
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
|||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
@ -46,6 +48,9 @@ public class JPAResourceServerStore implements ResourceServerStore {
|
|||
|
||||
@Override
|
||||
public ResourceServer create(String clientId) {
|
||||
if (!StorageId.isLocalStorage(clientId)) {
|
||||
throw new ModelException("Creating resource server from federated ClientModel not supported");
|
||||
}
|
||||
ResourceServerEntity entity = new ResourceServerEntity();
|
||||
|
||||
entity.setId(clientId);
|
||||
|
|
|
@ -45,7 +45,9 @@ import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
|
|||
import org.keycloak.models.jpa.entities.UserEntity;
|
||||
import org.keycloak.models.utils.DefaultRoles;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
@ -194,7 +196,14 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
consentEntity = new UserConsentEntity();
|
||||
consentEntity.setId(KeycloakModelUtils.generateId());
|
||||
consentEntity.setUser(em.getReference(UserEntity.class, userId));
|
||||
consentEntity.setClientId(clientId);
|
||||
StorageId clientStorageId = new StorageId(clientId);
|
||||
if (clientStorageId.isLocal()) {
|
||||
consentEntity.setClientId(clientId);
|
||||
} else {
|
||||
consentEntity.setClientStorageProvider(clientStorageId.getProviderId());
|
||||
consentEntity.setExternalClientId(clientStorageId.getExternalId());
|
||||
}
|
||||
|
||||
consentEntity.setCreatedDate(currentTime);
|
||||
consentEntity.setLastUpdatedDate(currentTime);
|
||||
em.persist(consentEntity);
|
||||
|
@ -246,9 +255,16 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
|
||||
|
||||
private UserConsentEntity getGrantedConsentEntity(String userId, String clientId) {
|
||||
TypedQuery<UserConsentEntity> query = em.createNamedQuery("userConsentByUserAndClient", UserConsentEntity.class);
|
||||
StorageId clientStorageId = new StorageId(clientId);
|
||||
String queryName = clientStorageId.isLocal() ? "userConsentByUserAndClient" : "userConsentByUserAndExternalClient";
|
||||
TypedQuery<UserConsentEntity> query = em.createNamedQuery(queryName, UserConsentEntity.class);
|
||||
query.setParameter("userId", userId);
|
||||
query.setParameter("clientId", clientId);
|
||||
if (clientStorageId.isLocal()) {
|
||||
query.setParameter("clientId", clientId);
|
||||
} else {
|
||||
query.setParameter("clientStorageProvider", clientStorageId.getProviderId());
|
||||
query.setParameter("externalClientId", clientStorageId.getExternalId());
|
||||
}
|
||||
List<UserConsentEntity> results = query.getResultList();
|
||||
if (results.size() > 1) {
|
||||
throw new ModelException("More results found for user [" + userId + "] and client [" + clientId + "]");
|
||||
|
@ -257,6 +273,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private UserConsentModel toConsentModel(RealmModel realm, UserConsentEntity entity) {
|
||||
|
@ -264,9 +281,16 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
return null;
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientById(entity.getClientId());
|
||||
StorageId clientStorageId = null;
|
||||
if ( entity.getClientId() == null) {
|
||||
clientStorageId = new StorageId(entity.getClientStorageProvider(), entity.getExternalClientId());
|
||||
} else {
|
||||
clientStorageId = new StorageId(entity.getClientId());
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientById(clientStorageId.getId());
|
||||
if (client == null) {
|
||||
throw new ModelException("Client with id " + entity.getClientId() + " is not available");
|
||||
throw new ModelException("Client with id " + clientStorageId.getId() + " is not available");
|
||||
}
|
||||
UserConsentModel model = new UserConsentModel(client);
|
||||
model.setCreatedDate(entity.getCreatedDate());
|
||||
|
@ -472,9 +496,32 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
em.createNamedQuery("deleteUserConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentsByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
StorageId clientStorageId = new StorageId(client.getId());
|
||||
if (clientStorageId.isLocal()) {
|
||||
em.createNamedQuery("deleteUserConsentProtMappersByClient")
|
||||
.setParameter("clientId", client.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentRolesByClient")
|
||||
.setParameter("clientId", client.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentsByClient")
|
||||
.setParameter("clientId", client.getId())
|
||||
.executeUpdate();
|
||||
} else {
|
||||
em.createNamedQuery("deleteUserConsentProtMappersByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId",clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentRolesByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId", clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentsByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId", clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -806,8 +853,24 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||
if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
|
||||
removeImportedUsers(realm, component.getId());
|
||||
if (component.getProviderType().equals(UserStorageProvider.class.getName())) {
|
||||
removeImportedUsers(realm, component.getId());
|
||||
}
|
||||
if (component.getProviderType().equals(ClientStorageProvider.class.getName())) {
|
||||
removeConsentByClientStorageProvider(realm, component.getId());
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeConsentByClientStorageProvider(RealmModel realm, String providerId) {
|
||||
em.createNamedQuery("deleteUserConsentProtMappersByClientStorageProvider")
|
||||
.setParameter("clientStorageProvider", providerId)
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentRolesByClientStorageProvider")
|
||||
.setParameter("clientStorageProvider", providerId)
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteUserConsentsByClientStorageProvider")
|
||||
.setParameter("clientStorageProvider", providerId)
|
||||
.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -125,9 +125,6 @@ public class ClientEntity {
|
|||
@CollectionTable(name="CLIENT_AUTH_FLOW_BINDINGS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
|
||||
protected Map<String, String> authFlowBindings = new HashMap<String, String>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
|
||||
Collection<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "client")
|
||||
Collection<ProtocolMapperEntity> protocolMappers = new ArrayList<ProtocolMapperEntity>();
|
||||
|
||||
|
@ -322,14 +319,6 @@ public class ClientEntity {
|
|||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public Collection<ClientIdentityProviderMappingEntity> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setIdentityProviders(Collection<ClientIdentityProviderMappingEntity> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public Collection<ProtocolMapperEntity> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
@Table(name="CLIENT_IDENTITY_PROV_MAPPING")
|
||||
@Entity
|
||||
@IdClass(ClientIdentityProviderMappingEntity.Key.class)
|
||||
public class ClientIdentityProviderMappingEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "CLIENT_ID")
|
||||
private ClientEntity client;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "IDENTITY_PROVIDER_ID")
|
||||
private IdentityProviderEntity identityProvider;
|
||||
|
||||
@Column(name = "RETRIEVE_TOKEN")
|
||||
private boolean retrieveToken;
|
||||
|
||||
public ClientEntity getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public void setClient(ClientEntity client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public IdentityProviderEntity getIdentityProvider() {
|
||||
return this.identityProvider;
|
||||
}
|
||||
|
||||
public void setIdentityProvider(IdentityProviderEntity identityProvider) {
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
public void setRetrieveToken(boolean retrieveToken) {
|
||||
this.retrieveToken = retrieveToken;
|
||||
}
|
||||
|
||||
public boolean isRetrieveToken() {
|
||||
return retrieveToken;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
private ClientEntity client;
|
||||
private IdentityProviderEntity identityProvider;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(ClientEntity client, IdentityProviderEntity identityProvider) {
|
||||
this.client = client;
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
public ClientEntity getUser() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public IdentityProviderEntity getIdentityProvider() {
|
||||
return identityProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (identityProvider != null ? !identityProvider.getAlias().equals(key.identityProvider.getAlias()) : key.identityProvider != null)
|
||||
return false;
|
||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = client != null ? client.getId().hashCode() : 0;
|
||||
result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null) return false;
|
||||
if (!(o instanceof ClientIdentityProviderMappingEntity)) return false;
|
||||
|
||||
ClientIdentityProviderMappingEntity key = (ClientIdentityProviderMappingEntity) o;
|
||||
|
||||
if (identityProvider != null ? !identityProvider.getAlias().equals(key.identityProvider.getAlias()) : key.identityProvider != null)
|
||||
return false;
|
||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = client != null ? client.getId().hashCode() : 0;
|
||||
result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -43,11 +43,14 @@ import java.util.Collection;
|
|||
})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="userConsentByUserAndClient", query="select consent from UserConsentEntity consent where consent.user.id = :userId and consent.clientId = :clientId"),
|
||||
@NamedQuery(name="userConsentByUserAndExternalClient", query="select consent from UserConsentEntity consent where consent.user.id = :userId and consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId"),
|
||||
@NamedQuery(name="userConsentsByUser", query="select consent from UserConsentEntity consent where consent.user.id = :userId"),
|
||||
@NamedQuery(name="deleteUserConsentsByRealm", query="delete from UserConsentEntity consent where consent.user IN (select user from UserEntity user where user.realmId = :realmId)"),
|
||||
@NamedQuery(name="deleteUserConsentsByRealmAndLink", query="delete from UserConsentEntity consent where consent.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
|
||||
@NamedQuery(name="deleteUserConsentsByUser", query="delete from UserConsentEntity consent where consent.user = :user"),
|
||||
@NamedQuery(name="deleteUserConsentsByClient", query="delete from UserConsentEntity consent where consent.clientId = :clientId"),
|
||||
@NamedQuery(name="deleteUserConsentsByExternalClient", query="delete from UserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId"),
|
||||
@NamedQuery(name="deleteUserConsentsByClientStorageProvider", query="delete from UserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider"),
|
||||
})
|
||||
public class UserConsentEntity {
|
||||
|
||||
|
@ -63,6 +66,12 @@ public class UserConsentEntity {
|
|||
@Column(name="CLIENT_ID")
|
||||
protected String clientId;
|
||||
|
||||
@Column(name="CLIENT_STORAGE_PROVIDER")
|
||||
protected String clientStorageProvider;
|
||||
|
||||
@Column(name="EXTERNAL_CLIENT_ID")
|
||||
protected String externalClientId;
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
|
||||
Collection<UserConsentRoleEntity> grantedRoles = new ArrayList<UserConsentRoleEntity>();
|
||||
|
||||
|
@ -91,14 +100,6 @@ public class UserConsentEntity {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public Collection<UserConsentRoleEntity> getGrantedRoles() {
|
||||
return grantedRoles;
|
||||
}
|
||||
|
@ -131,6 +132,30 @@ public class UserConsentEntity {
|
|||
this.lastUpdatedDate = lastUpdatedDate;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientStorageProvider() {
|
||||
return clientStorageProvider;
|
||||
}
|
||||
|
||||
public void setClientStorageProvider(String clientStorageProvider) {
|
||||
this.clientStorageProvider = clientStorageProvider;
|
||||
}
|
||||
|
||||
public String getExternalClientId() {
|
||||
return externalClientId;
|
||||
}
|
||||
|
||||
public void setExternalClientId(String externalClientId) {
|
||||
this.externalClientId = externalClientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -38,7 +38,9 @@ import java.io.Serializable;
|
|||
@NamedQuery(name="deleteUserConsentProtMappersByUser", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user = :user)"),
|
||||
@NamedQuery(name="deleteUserConsentProtMappersByRealmAndLink", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
|
||||
@NamedQuery(name="deleteUserConsentProtMappersByProtocolMapper", query="delete from UserConsentProtocolMapperEntity csm where csm.protocolMapperId = :protocolMapperId)"),
|
||||
@NamedQuery(name="deleteUserConsentProtMappersByClient", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId))"),
|
||||
@NamedQuery(name="deleteUserConsentProtMappersByClient", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId)"),
|
||||
@NamedQuery(name="deleteUserConsentProtMappersByExternalClient", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId)"),
|
||||
@NamedQuery(name="deleteUserConsentProtMappersByClientStorageProvider", query="delete from UserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from UserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider)"),
|
||||
})
|
||||
@Entity
|
||||
@Table(name="USER_CONSENT_PROT_MAPPER")
|
||||
|
|
|
@ -38,6 +38,8 @@ import java.io.Serializable;
|
|||
@NamedQuery(name="deleteUserConsentRolesByUser", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.user = :user)"),
|
||||
@NamedQuery(name="deleteUserConsentRolesByRole", query="delete from UserConsentRoleEntity grantedRole where grantedRole.roleId = :roleId)"),
|
||||
@NamedQuery(name="deleteUserConsentRolesByClient", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.clientId = :clientId)"),
|
||||
@NamedQuery(name="deleteUserConsentRolesByExternalClient", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId)"),
|
||||
@NamedQuery(name="deleteUserConsentRolesByClientStorageProvider", query="delete from UserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from UserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider)"),
|
||||
})
|
||||
@Entity
|
||||
@Table(name="USER_CONSENT_ROLE")
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.models.session.PersistentClientSessionModel;
|
|||
import org.keycloak.models.session.PersistentUserSessionAdapter;
|
||||
import org.keycloak.models.session.PersistentUserSessionModel;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
@ -78,7 +79,17 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
PersistentClientSessionModel model = adapter.getUpdatedModel();
|
||||
|
||||
PersistentClientSessionEntity entity = new PersistentClientSessionEntity();
|
||||
entity.setClientId(clientSession.getClient().getId());
|
||||
StorageId clientStorageId = new StorageId(clientSession.getClient().getId());
|
||||
if (clientStorageId.isLocal()) {
|
||||
entity.setClientId(clientStorageId.getId());
|
||||
entity.setClientStorageProvider(PersistentClientSessionEntity.LOCAL);
|
||||
entity.setExternalClientId(PersistentClientSessionEntity.LOCAL);
|
||||
|
||||
} else {
|
||||
entity.setClientId(PersistentClientSessionEntity.EXTERNAL);
|
||||
entity.setClientStorageProvider(clientStorageId.getProviderId());
|
||||
entity.setExternalClientId(clientStorageId.getExternalId());
|
||||
}
|
||||
entity.setTimestamp(clientSession.getTimestamp());
|
||||
String offlineStr = offlineToString(offline);
|
||||
entity.setOffline(offlineStr);
|
||||
|
@ -127,7 +138,18 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
@Override
|
||||
public void removeClientSession(String userSessionId, String clientUUID, boolean offline) {
|
||||
String offlineStr = offlineToString(offline);
|
||||
PersistentClientSessionEntity sessionEntity = em.find(PersistentClientSessionEntity.class, new PersistentClientSessionEntity.Key(userSessionId, clientUUID, offlineStr));
|
||||
StorageId clientStorageId = new StorageId(clientUUID);
|
||||
String clientId = PersistentClientSessionEntity.EXTERNAL;
|
||||
String clientStorageProvider = PersistentClientSessionEntity.LOCAL;
|
||||
String externalId = PersistentClientSessionEntity.LOCAL;
|
||||
if (clientStorageId.isLocal()) {
|
||||
clientId = clientUUID;
|
||||
} else {
|
||||
clientStorageProvider = clientStorageId.getProviderId();
|
||||
externalId = clientStorageId.getExternalId();
|
||||
|
||||
}
|
||||
PersistentClientSessionEntity sessionEntity = em.find(PersistentClientSessionEntity.class, new PersistentClientSessionEntity.Key(userSessionId, clientId, clientStorageProvider, externalId, offlineStr));
|
||||
if (sessionEntity != null) {
|
||||
em.remove(sessionEntity);
|
||||
|
||||
|
@ -168,7 +190,16 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
}
|
||||
|
||||
private void onClientRemoved(String clientUUID) {
|
||||
int num = em.createNamedQuery("deleteClientSessionsByClient").setParameter("clientId", clientUUID).executeUpdate();
|
||||
int num = 0;
|
||||
StorageId clientStorageId = new StorageId(clientUUID);
|
||||
if (clientStorageId.isLocal()) {
|
||||
num = em.createNamedQuery("deleteClientSessionsByClient").setParameter("clientId", clientUUID).executeUpdate();
|
||||
} else {
|
||||
num = em.createNamedQuery("deleteClientSessionsByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId", clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
}
|
||||
num = em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
|
||||
}
|
||||
|
||||
|
@ -282,10 +313,14 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
}
|
||||
|
||||
private PersistentAuthenticatedClientSessionAdapter toAdapter(RealmModel realm, PersistentUserSessionAdapter userSession, PersistentClientSessionEntity entity) {
|
||||
ClientModel client = realm.getClientById(entity.getClientId());
|
||||
String clientId = entity.getClientId();
|
||||
if (!entity.getExternalClientId().equals("local")) {
|
||||
clientId = new StorageId(entity.getClientId(), entity.getExternalClientId()).getId();
|
||||
}
|
||||
ClientModel client = realm.getClientById(clientId);
|
||||
|
||||
PersistentClientSessionModel model = new PersistentClientSessionModel();
|
||||
model.setClientId(entity.getClientId());
|
||||
model.setClientId(clientId);
|
||||
model.setUserSessionId(userSession.getId());
|
||||
model.setUserId(userSession.getUserId());
|
||||
model.setTimestamp(entity.getTimestamp());
|
||||
|
|
|
@ -32,6 +32,8 @@ import java.io.Serializable;
|
|||
@NamedQueries({
|
||||
@NamedQuery(name="deleteClientSessionsByRealm", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.realmId = :realmId)"),
|
||||
@NamedQuery(name="deleteClientSessionsByClient", query="delete from PersistentClientSessionEntity sess where sess.clientId = :clientId"),
|
||||
@NamedQuery(name="deleteClientSessionsByExternalClient", query="delete from PersistentClientSessionEntity sess where sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId"),
|
||||
@NamedQuery(name="deleteClientSessionsByClientStorageProvider", query="delete from PersistentClientSessionEntity sess where sess.clientStorageProvider = :clientStorageProvider"),
|
||||
@NamedQuery(name="deleteClientSessionsByUser", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.userId = :userId)"),
|
||||
@NamedQuery(name="deleteClientSessionsByUserSession", query="delete from PersistentClientSessionEntity sess where sess.userSessionId = :userSessionId and sess.offline = :offline"),
|
||||
@NamedQuery(name="deleteDetachedClientSessions", query="delete from PersistentClientSessionEntity sess where NOT EXISTS (select u.userSessionId from PersistentUserSessionEntity u where u.userSessionId = sess.userSessionId )"),
|
||||
|
@ -44,6 +46,8 @@ import java.io.Serializable;
|
|||
@IdClass(PersistentClientSessionEntity.Key.class)
|
||||
public class PersistentClientSessionEntity {
|
||||
|
||||
public static final String LOCAL = "local";
|
||||
public static final String EXTERNAL = "external";
|
||||
@Id
|
||||
@Column(name = "USER_SESSION_ID", length = 36)
|
||||
protected String userSessionId;
|
||||
|
@ -52,6 +56,14 @@ public class PersistentClientSessionEntity {
|
|||
@Column(name="CLIENT_ID", length = 36)
|
||||
protected String clientId;
|
||||
|
||||
@Id
|
||||
@Column(name="CLIENT_STORAGE_PROVIDER", length = 36)
|
||||
protected String clientStorageProvider;
|
||||
|
||||
@Id
|
||||
@Column(name="EXTERNAL_CLIENT_ID", length = 255)
|
||||
protected String externalClientId;
|
||||
|
||||
@Column(name="TIMESTAMP")
|
||||
protected int timestamp;
|
||||
|
||||
|
@ -78,6 +90,22 @@ public class PersistentClientSessionEntity {
|
|||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientStorageProvider() {
|
||||
return clientStorageProvider;
|
||||
}
|
||||
|
||||
public void setClientStorageProvider(String clientStorageProvider) {
|
||||
this.clientStorageProvider = clientStorageProvider;
|
||||
}
|
||||
|
||||
public String getExternalClientId() {
|
||||
return externalClientId;
|
||||
}
|
||||
|
||||
public void setExternalClientId(String externalClientId) {
|
||||
this.externalClientId = externalClientId;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
@ -107,15 +135,19 @@ public class PersistentClientSessionEntity {
|
|||
protected String userSessionId;
|
||||
|
||||
protected String clientId;
|
||||
protected String clientStorageProvider;
|
||||
protected String externalClientId;
|
||||
|
||||
protected String offline;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(String userSessionId, String clientId, String offline) {
|
||||
public Key(String userSessionId, String clientId, String clientStorageProvider, String externalClientId, String offline) {
|
||||
this.userSessionId = userSessionId;
|
||||
this.clientId = clientId;
|
||||
this.externalClientId = externalClientId;
|
||||
this.clientStorageProvider = clientStorageProvider;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
|
@ -131,6 +163,14 @@ public class PersistentClientSessionEntity {
|
|||
return offline;
|
||||
}
|
||||
|
||||
public String getClientStorageProvider() {
|
||||
return clientStorageProvider;
|
||||
}
|
||||
|
||||
public String getExternalClientId() {
|
||||
return externalClientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -140,6 +180,8 @@ public class PersistentClientSessionEntity {
|
|||
|
||||
if (this.userSessionId != null ? !this.userSessionId.equals(key.userSessionId) : key.userSessionId != null) return false;
|
||||
if (this.clientId != null ? !this.clientId.equals(key.clientId) : key.clientId != null) return false;
|
||||
if (this.externalClientId != null ? !this.externalClientId.equals(key.clientId) : key.externalClientId != null) return false;
|
||||
if (this.clientStorageProvider != null ? !this.clientStorageProvider.equals(key.clientId) : key.clientStorageProvider != null) return false;
|
||||
if (this.offline != null ? !this.offline.equals(key.offline) : key.offline != null) return false;
|
||||
|
||||
return true;
|
||||
|
@ -149,6 +191,8 @@ public class PersistentClientSessionEntity {
|
|||
public int hashCode() {
|
||||
int result = this.userSessionId != null ? this.userSessionId.hashCode() : 0;
|
||||
result = 37 * result + (this.clientId != null ? this.clientId.hashCode() : 0);
|
||||
result = 37 * result + (this.externalClientId != null ? this.externalClientId.hashCode() : 0);
|
||||
result = 37 * result + (this.clientStorageProvider != null ? this.clientStorageProvider.hashCode() : 0);
|
||||
result = 31 * result + (this.offline != null ? this.offline.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -32,9 +32,11 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.jpa.entities.UserConsentEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.storage.jpa.entity.BrokerLinkEntity;
|
||||
import org.keycloak.storage.jpa.entity.FederatedUser;
|
||||
|
@ -257,7 +259,13 @@ public class JpaUserFederatedStorageProvider implements
|
|||
consentEntity = new FederatedUserConsentEntity();
|
||||
consentEntity.setId(KeycloakModelUtils.generateId());
|
||||
consentEntity.setUserId(userId);
|
||||
consentEntity.setClientId(clientId);
|
||||
StorageId clientStorageId = new StorageId(clientId);
|
||||
if (clientStorageId.isLocal()) {
|
||||
consentEntity.setClientId(clientId);
|
||||
} else {
|
||||
consentEntity.setClientStorageProvider(clientStorageId.getProviderId());
|
||||
consentEntity.setExternalClientId(clientStorageId.getExternalId());
|
||||
}
|
||||
consentEntity.setRealmId(realm.getId());
|
||||
consentEntity.setStorageProviderId(new StorageId(userId).getProviderId());
|
||||
long currentTime = Time.currentTimeMillis();
|
||||
|
@ -315,9 +323,16 @@ public class JpaUserFederatedStorageProvider implements
|
|||
}
|
||||
|
||||
private FederatedUserConsentEntity getGrantedConsentEntity(String userId, String clientId) {
|
||||
TypedQuery<FederatedUserConsentEntity> query = em.createNamedQuery("userFederatedConsentByUserAndClient", FederatedUserConsentEntity.class);
|
||||
StorageId clientStorageId = new StorageId(clientId);
|
||||
String queryName = clientStorageId.isLocal() ? "userFederatedConsentByUserAndClient" : "userFederatedConsentByUserAndExternalClient";
|
||||
TypedQuery<FederatedUserConsentEntity> query = em.createNamedQuery(queryName, FederatedUserConsentEntity.class);
|
||||
query.setParameter("userId", userId);
|
||||
query.setParameter("clientId", clientId);
|
||||
if (clientStorageId.isLocal()) {
|
||||
query.setParameter("clientId", clientId);
|
||||
} else {
|
||||
query.setParameter("clientStorageProvider", clientStorageId.getProviderId());
|
||||
query.setParameter("externalClientId", clientStorageId.getExternalId());
|
||||
}
|
||||
List<FederatedUserConsentEntity> results = query.getResultList();
|
||||
if (results.size() > 1) {
|
||||
throw new ModelException("More results found for user [" + userId + "] and client [" + clientId + "]");
|
||||
|
@ -334,10 +349,14 @@ public class JpaUserFederatedStorageProvider implements
|
|||
return null;
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientById(entity.getClientId());
|
||||
if (client == null) {
|
||||
throw new ModelException("Client with id " + entity.getClientId() + " is not available");
|
||||
StorageId clientStorageId = null;
|
||||
if ( entity.getClientId() == null) {
|
||||
clientStorageId = new StorageId(entity.getClientStorageProvider(), entity.getExternalClientId());
|
||||
} else {
|
||||
clientStorageId = new StorageId(entity.getClientId());
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientById(clientStorageId.getId());
|
||||
UserConsentModel model = new UserConsentModel(client);
|
||||
model.setCreatedDate(entity.getCreatedDate());
|
||||
model.setLastUpdatedDate(entity.getLastUpdatedDate());
|
||||
|
@ -822,9 +841,26 @@ public class JpaUserFederatedStorageProvider implements
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ClientModel client) {
|
||||
em.createNamedQuery("deleteFederatedUserConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentsByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
StorageId clientStorageId = new StorageId(client.getId());
|
||||
if (clientStorageId.isLocal()) {
|
||||
em.createNamedQuery("deleteFederatedUserConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentsByClient").setParameter("clientId", client.getId()).executeUpdate();
|
||||
} else {
|
||||
em.createNamedQuery("deleteFederatedUserConsentProtMappersByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId",clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentRolesByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId",clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentsByExternalClient")
|
||||
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
|
||||
.setParameter("externalClientId",clientStorageId.getExternalId())
|
||||
.executeUpdate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -885,41 +921,53 @@ public class JpaUserFederatedStorageProvider implements
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ComponentModel model) {
|
||||
if (!model.getProviderType().equals(UserStorageProvider.class.getName())) return;
|
||||
if (model.getProviderType().equals(UserStorageProvider.class.getName())) {
|
||||
|
||||
em.createNamedQuery("deleteBrokerLinkByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedAttributesByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentProtMappersByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserRoleMappingsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedCredentialAttributeByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserCredentialsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserGroupMembershipByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserRequiredActionsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserRoleMappingsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUsersByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteBrokerLinkByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedAttributesByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentProtMappersByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserRoleMappingsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedCredentialAttributeByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserCredentialsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserGroupMembershipByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserRequiredActionsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserRoleMappingsByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUsersByStorageProvider")
|
||||
.setParameter("storageProviderId", model.getId())
|
||||
.executeUpdate();
|
||||
} else if (model.getProviderType().equals(ClientStorageProvider.class.getName())) {
|
||||
em.createNamedQuery("deleteFederatedUserConsentProtMappersByClientStorageProvider")
|
||||
.setParameter("clientStorageProvider", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentRolesByClientStorageProvider")
|
||||
.setParameter("clientStorageProvider", model.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("deleteFederatedUserConsentsByClientStorageProvider")
|
||||
.setParameter("clientStorageProvider", model.getId())
|
||||
.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,11 +40,14 @@ import java.util.Collection;
|
|||
})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="userFederatedConsentByUserAndClient", query="select consent from FederatedUserConsentEntity consent where consent.userId = :userId and consent.clientId = :clientId"),
|
||||
@NamedQuery(name="userFederatedConsentByUserAndExternalClient", query="select consent from FederatedUserConsentEntity consent where consent.userId = :userId and consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId"),
|
||||
@NamedQuery(name="userFederatedConsentsByUser", query="select consent from FederatedUserConsentEntity consent where consent.userId = :userId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentsByRealm", query="delete from FederatedUserConsentEntity consent where consent.realmId=:realmId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentsByStorageProvider", query="delete from FederatedUserConsentEntity e where e.storageProviderId=:storageProviderId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentsByUser", query="delete from FederatedUserConsentEntity consent where consent.userId = :userId and consent.realmId = :realmId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentsByClient", query="delete from FederatedUserConsentEntity consent where consent.clientId = :clientId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentsByExternalClient", query="delete from FederatedUserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentsByClientStorageProvider", query="delete from FederatedUserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider"),
|
||||
})
|
||||
public class FederatedUserConsentEntity {
|
||||
|
||||
|
@ -65,6 +68,12 @@ public class FederatedUserConsentEntity {
|
|||
@Column(name="CLIENT_ID")
|
||||
protected String clientId;
|
||||
|
||||
@Column(name="CLIENT_STORAGE_PROVIDER")
|
||||
protected String clientStorageProvider;
|
||||
|
||||
@Column(name="EXTERNAL_CLIENT_ID")
|
||||
protected String externalClientId;
|
||||
|
||||
@Column(name = "CREATED_DATE")
|
||||
private Long createdDate;
|
||||
|
||||
|
@ -119,6 +128,22 @@ public class FederatedUserConsentEntity {
|
|||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientStorageProvider() {
|
||||
return clientStorageProvider;
|
||||
}
|
||||
|
||||
public void setClientStorageProvider(String clientStorageProvider) {
|
||||
this.clientStorageProvider = clientStorageProvider;
|
||||
}
|
||||
|
||||
public String getExternalClientId() {
|
||||
return externalClientId;
|
||||
}
|
||||
|
||||
public void setExternalClientId(String externalClientId) {
|
||||
this.externalClientId = externalClientId;
|
||||
}
|
||||
|
||||
public Collection<FederatedUserConsentRoleEntity> getGrantedRoles() {
|
||||
return grantedRoles;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ import java.io.Serializable;
|
|||
@NamedQuery(name="deleteFederatedUserConsentProtMappersByStorageProvider", query="delete from FederatedUserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.storageProviderId = :storageProviderId)"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentProtMappersByProtocolMapper", query="delete from FederatedUserConsentProtocolMapperEntity csm where csm.protocolMapperId = :protocolMapperId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentProtMappersByClient", query="delete from FederatedUserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.clientId = :clientId)"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentProtMappersByExternalClient", query="delete from FederatedUserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId)"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentProtMappersByClientStorageProvider", query="delete from FederatedUserConsentProtocolMapperEntity csm where csm.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider)"),
|
||||
})
|
||||
@Entity
|
||||
@Table(name="FED_USER_CONSENT_PROT_MAPPER")
|
||||
|
|
|
@ -38,6 +38,8 @@ import java.io.Serializable;
|
|||
@NamedQuery(name="deleteFederatedUserConsentRolesByStorageProvider", query="delete from FederatedUserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.storageProviderId = :storageProviderId)"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentRolesByRole", query="delete from FederatedUserConsentRoleEntity grantedRole where grantedRole.roleId = :roleId"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentRolesByClient", query="delete from FederatedUserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.clientId = :clientId)"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentRolesByExternalClient", query="delete from FederatedUserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider and consent.externalClientId = :externalClientId)"),
|
||||
@NamedQuery(name="deleteFederatedUserConsentRolesByClientStorageProvider", query="delete from FederatedUserConsentRoleEntity grantedRole where grantedRole.userConsent IN (select consent from FederatedUserConsentEntity consent where consent.clientStorageProvider = :clientStorageProvider)"),
|
||||
})
|
||||
@Entity
|
||||
@Table(name="FED_USER_CONSENT_ROLE")
|
||||
|
|
|
@ -29,4 +29,62 @@
|
|||
</createTable>
|
||||
<addPrimaryKey columnNames="CLIENT_ID, BINDING_NAME" constraintName="C_CLI_FLOW_BIND" tableName="CLIENT_AUTH_FLOW_BINDINGS"/>
|
||||
</changeSet>
|
||||
<changeSet author="bburke@redhat.com" id="4.0.0-CLEANUP-UNUSED-TABLE">
|
||||
<dropIndex tableName="CLIENT_IDENTITY_PROV_MAPPING" indexName="IDX_CLIENT_ID_PROV_MAP_CLIENT"/>
|
||||
<dropPrimaryKey tableName="CLIENT_IDENTITY_PROV_MAPPING" constraintName="CONSTR_CLIENT_IDEN_PROV_MAP"/>
|
||||
<dropUniqueConstraint tableName="CLIENT_IDENTITY_PROV_MAPPING" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12"/>
|
||||
<dropTable tableName="CLIENT_IDENTITY_PROV_MAPPING"/>
|
||||
</changeSet>
|
||||
<changeSet author="bburke@redhat.com" id="4.0.0-KEYCLOAK-6228">
|
||||
<!-- Modifying some columns so that CLIENT_ID is 255. Drop foreign key constraints too that referenced CLIENT tablename.
|
||||
This is needed for client storage SPI but only needed for tables that might reference a federated client -->
|
||||
|
||||
<!-- Modify USER_CONSENT -->
|
||||
<dropUniqueConstraint constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="USER_CONSENT"/>
|
||||
<dropNotNullConstraint tableName="USER_CONSENT" columnName="CLIENT_ID" columnDataType="VARCHAR(36)"/>
|
||||
<addColumn tableName="USER_CONSENT">
|
||||
<column name="CLIENT_STORAGE_PROVIDER" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="EXTERNAL_CLIENT_ID" type="VARCHAR(255)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addUniqueConstraint columnNames="CLIENT_ID, CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="USER_CONSENT"/>
|
||||
|
||||
<!-- FED_USER_CONSENT -->
|
||||
<addColumn tableName="FED_USER_CONSENT">
|
||||
<column name="CLIENT_STORAGE_PROVIDER" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="EXTERNAL_CLIENT_ID" type="VARCHAR(255)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<dropNotNullConstraint tableName="FED_USER_CONSENT" columnName="CLIENT_ID" columnDataType="VARCHAR(36)"/>
|
||||
<createIndex tableName="FED_USER_CONSENT" indexName="IDX_FU_CNSNT_EXT">
|
||||
<column name="USER_ID" type="VARCHAR(255)" />
|
||||
<column name="CLIENT_STORAGE_PROVIDER" type="VARCHAR(36)" />
|
||||
<column name="EXTERNAL_CLIENT_ID" type="VARCHAR(255)" />
|
||||
</createIndex>
|
||||
|
||||
<!-- Modify OFFLINE_CLIENT_SESSION -->
|
||||
<addColumn tableName="OFFLINE_CLIENT_SESSION">
|
||||
<column name="CLIENT_STORAGE_PROVIDER" type="VARCHAR(36)" defaultValue="local">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="EXTERNAL_CLIENT_ID" type="VARCHAR(255)" defaultValue="local">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<update tableName="OFFLINE_CLIENT_SESSION">
|
||||
<column name="CLIENT_STORAGE_PROVIDER" value="local"/>
|
||||
</update>
|
||||
<update tableName="OFFLINE_CLIENT_SESSION">
|
||||
<column name="EXTERNAL_CLIENT_ID" value="local"/>
|
||||
</update>
|
||||
<dropPrimaryKey tableName="OFFLINE_CLIENT_SESSION" constraintName="CONSTRAINT_OFFL_CL_SES_PK3"/>
|
||||
<addPrimaryKey columnNames="USER_SESSION_ID,CLIENT_ID, CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK3" tableName="OFFLINE_CLIENT_SESSION"/>
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.keycloak.models.RealmProvider;
|
|||
*/
|
||||
public interface CacheRealmProvider extends RealmProvider {
|
||||
void clear();
|
||||
RealmProvider getDelegate();
|
||||
RealmProvider getRealmDelegate();
|
||||
|
||||
void registerRealmInvalidation(String id, String name);
|
||||
|
||||
|
|
|
@ -485,6 +485,8 @@ public class ModelToRepresentation {
|
|||
public static ClientRepresentation toRepresentation(ClientModel clientModel) {
|
||||
ClientRepresentation rep = new ClientRepresentation();
|
||||
rep.setId(clientModel.getId());
|
||||
String providerId = StorageId.resolveProviderId(clientModel);
|
||||
rep.setOrigin(providerId);
|
||||
rep.setClientId(clientModel.getClientId());
|
||||
rep.setName(clientModel.getName());
|
||||
rep.setDescription(clientModel.getDescription());
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper base class for ClientModel implementations for ClientStorageProvider implementations.
|
||||
*
|
||||
* Contains default implementations of some methods
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractClientStorageAdapter extends UnsupportedOperationsClientStorageAdapter {
|
||||
protected KeycloakSession session;
|
||||
protected RealmModel realm;
|
||||
protected ClientStorageProviderModel component;
|
||||
private StorageId storageId;
|
||||
|
||||
|
||||
public AbstractClientStorageAdapter(KeycloakSession session, RealmModel realm, ClientStorageProviderModel component) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates federated id based on getClientId() method
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
if (storageId == null) {
|
||||
storageId = new StorageId(component.getId(), getClientId());
|
||||
}
|
||||
return storageId.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method really isn't used by anybody anywhere. Legacy feature never supported.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method really isn't used by anybody anywhere. Legacy feature never supported.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
|
||||
// do nothing, we don't do anything with this.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for logout. Empty implementation for now. Can override if you can store this information somewhere.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Integer> getRegisteredNodes() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for logout. Empty implementation for now. Can override if you can store this information somewhere.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void registerNode(String nodeHost, int registrationTime) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for logout. Empty implementation for now. Can override if you can store this information somewhere.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void unregisterNode(String nodeHost) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding implementations should call super.updateClient() as this fires off an update event.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void updateClient() {
|
||||
session.getKeycloakSessionFactory().publish(new RealmModel.ClientUpdatedEvent() {
|
||||
|
||||
@Override
|
||||
public ClientModel getUpdatedClient() {
|
||||
return AbstractClientStorageAdapter.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSession getKeycloakSession() {
|
||||
return session;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractReadOnlyClientStorageAdapter extends AbstractClientStorageAdapter {
|
||||
public AbstractReadOnlyClientStorageAdapter(KeycloakSession session, RealmModel realm, ClientStorageProviderModel component) {
|
||||
super(session, realm, component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientId(String clientId) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWebOrigins(Set<String> webOrigins) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWebOrigin(String webOrigin) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWebOrigin(String webOrigin) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectUris(Set<String> redirectUris) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRedirectUri(String redirectUri) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRedirectUri(String redirectUri) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManagementUrl(String url) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootUrl(String url) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBaseUrl(String url) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBearerOnly(boolean only) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNodeReRegistrationTimeout(int timeout) {
|
||||
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientAuthenticatorType(String clientAuthenticatorType) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecret(String secret) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegistrationToken(String registrationToken) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProtocol(String protocol) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, String value) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthenticationFlowBindingOverride(String binding) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrontchannelLogout(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicClient(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConsentRequired(boolean consentRequired) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStandardFlowEnabled(boolean standardFlowEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientTemplate(ClientTemplateModel template) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseTemplateScope(boolean flag) {
|
||||
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseTemplateMappers(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseTemplateConfig(boolean flag) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotBefore(int notBefore) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProtocolMapper(ProtocolMapperModel mapping) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProtocolMapper(ProtocolMapperModel mapping) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullScopeAllowed(boolean value) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScopeMapping(RoleModel role) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteScopeMapping(RoleModel role) {
|
||||
throw new ReadOnlyException("client is read only for this update");
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientStorageProviderFactory<T extends ClientStorageProvider> extends ComponentFactory<T, ClientStorageProvider> {
|
||||
|
||||
|
||||
/**
|
||||
* called per Keycloak transaction.
|
||||
*
|
||||
* @param session
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
T create(KeycloakSession session, ComponentModel model);
|
||||
|
||||
/**
|
||||
* This is the name of the provider and will be showed in the admin console as an option.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
String getId();
|
||||
|
||||
@Override
|
||||
default void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default String getHelpText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
default List<ProviderConfigProperty> getConfigProperties() {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when ClientStorageProviderModel is created. This allows you to do initialization of any additional configuration
|
||||
* you need to add.
|
||||
*
|
||||
* @param session
|
||||
* @param realm
|
||||
* @param model
|
||||
*/
|
||||
@Override
|
||||
default void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* configuration properties that are common across all UserStorageProvider implementations
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
default
|
||||
List<ProviderConfigProperty> getCommonProviderConfigProperties() {
|
||||
return ClientStorageProviderSpi.commonConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
default
|
||||
Map<String, Object> getTypeMetadata() {
|
||||
Map<String, Object> metadata = new HashMap<>();
|
||||
return metadata;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientStorageProviderSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "client-storage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return ClientStorageProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return ClientStorageProviderFactory.class;
|
||||
}
|
||||
|
||||
private static final List<ProviderConfigProperty> commonConfig;
|
||||
|
||||
static {
|
||||
List<ProviderConfigProperty> config = ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name("enabled").type(ProviderConfigProperty.BOOLEAN_TYPE).add()
|
||||
.property()
|
||||
.name("priority").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.property()
|
||||
.name("cachePolicy").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.property()
|
||||
.name("maxLifespan").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.property()
|
||||
.name("evictionHour").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.property()
|
||||
.name("evictionMinute").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.property()
|
||||
.name("evictionDay").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.property()
|
||||
.name("cacheInvalidBefore").type(ProviderConfigProperty.STRING_TYPE).add()
|
||||
.build();
|
||||
commonConfig = Collections.unmodifiableList(config);
|
||||
}
|
||||
|
||||
public static List<ProviderConfigProperty> commonConfig() {
|
||||
return commonConfig;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Base helper class. Unsupported operations are implemented here that throw exception on invocation.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class UnsupportedOperationsClientStorageAdapter implements ClientModel {
|
||||
@Override
|
||||
public final RoleModel getRole(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RoleModel addRole(String name) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RoleModel addRole(String id, String name) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean removeRole(RoleModel role) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<RoleModel> getRoles() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<String> getDefaultRoles() {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void addDefaultRole(String name) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void updateDefaultRoles(String... defaultRoles) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removeDefaultRoles(String... defaultRoles) {
|
||||
throw new ModelException("Unsupported operation");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -70,4 +70,5 @@ org.keycloak.transaction.TransactionManagerLookupSpi
|
|||
org.keycloak.credential.hash.PasswordHashSpi
|
||||
org.keycloak.credential.CredentialSpi
|
||||
org.keycloak.keys.PublicKeyStorageSpi
|
||||
org.keycloak.keys.KeySpi
|
||||
org.keycloak.keys.KeySpi
|
||||
org.keycloak.storage.client.ClientStorageProviderSpi
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.models;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.storage.client.ClientLookupProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientProvider extends ClientLookupProvider, Provider {
|
||||
List<ClientModel> getClients(RealmModel realm);
|
||||
|
||||
ClientModel addClient(RealmModel realm, String clientId);
|
||||
|
||||
ClientModel addClient(RealmModel realm, String id, String clientId);
|
||||
|
||||
RoleModel addClientRole(RealmModel realm, ClientModel client, String name);
|
||||
|
||||
RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name);
|
||||
|
||||
RoleModel getClientRole(RealmModel realm, ClientModel client, String name);
|
||||
|
||||
Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client);
|
||||
|
||||
boolean removeClient(String id, RealmModel realm);
|
||||
}
|
|
@ -124,6 +124,8 @@ public interface KeycloakSession {
|
|||
UserProvider users();
|
||||
|
||||
|
||||
ClientProvider clientStorageManager();
|
||||
|
||||
/**
|
||||
* Un-cached view of all users in system including users loaded by UserStorageProviders
|
||||
*
|
||||
|
@ -145,6 +147,15 @@ public interface KeycloakSession {
|
|||
*/
|
||||
UserProvider userLocalStorage();
|
||||
|
||||
RealmProvider realmLocalStorage();
|
||||
|
||||
/**
|
||||
* Keycloak specific local storage for clients. No cache in front, this api talks directly to database configured for Keycloak
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ClientProvider clientLocalStorage();
|
||||
|
||||
/**
|
||||
* Hybrid storage for UserStorageProviders that can't store a specific piece of keycloak data in their external storage.
|
||||
* No cache in front.
|
||||
|
|
|
@ -22,6 +22,8 @@ import org.keycloak.component.ComponentModel;
|
|||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -341,6 +343,16 @@ public interface RealmModel extends RoleContainerModel {
|
|||
return list;
|
||||
}
|
||||
|
||||
default
|
||||
List<ClientStorageProviderModel> getClientStorageProviders() {
|
||||
List<ClientStorageProviderModel> list = new LinkedList<>();
|
||||
for (ComponentModel component : getComponents(getId(), ClientStorageProvider.class.getName())) {
|
||||
list.add(new ClientStorageProviderModel(component));
|
||||
}
|
||||
Collections.sort(list, ClientStorageProviderModel.comparator);
|
||||
return list;
|
||||
}
|
||||
|
||||
String getLoginTheme();
|
||||
|
||||
void setLoginTheme(String name);
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.models;
|
|||
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.storage.client.ClientLookupProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -27,7 +28,7 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface RealmProvider extends Provider {
|
||||
public interface RealmProvider extends Provider, ClientProvider {
|
||||
|
||||
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
|
||||
MigrationModel getMigrationModel();
|
||||
|
@ -58,15 +59,6 @@ public interface RealmProvider extends Provider {
|
|||
|
||||
void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
|
||||
|
||||
ClientModel addClient(RealmModel realm, String clientId);
|
||||
|
||||
ClientModel addClient(RealmModel realm, String id, String clientId);
|
||||
|
||||
List<ClientModel> getClients(RealmModel realm);
|
||||
|
||||
ClientModel getClientById(String id, RealmModel realm);
|
||||
ClientModel getClientByClientId(String clientId, RealmModel realm);
|
||||
|
||||
|
||||
RoleModel addRealmRole(RealmModel realm, String name);
|
||||
|
||||
|
@ -74,22 +66,12 @@ public interface RealmProvider extends Provider {
|
|||
|
||||
RoleModel getRealmRole(RealmModel realm, String name);
|
||||
|
||||
RoleModel addClientRole(RealmModel realm, ClientModel client, String name);
|
||||
|
||||
RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name);
|
||||
|
||||
Set<RoleModel> getRealmRoles(RealmModel realm);
|
||||
|
||||
RoleModel getClientRole(RealmModel realm, ClientModel client, String name);
|
||||
|
||||
Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client);
|
||||
|
||||
boolean removeRole(RealmModel realm, RoleModel role);
|
||||
|
||||
RoleModel getRoleById(String id, RealmModel realm);
|
||||
|
||||
boolean removeClient(String id, RealmModel realm);
|
||||
|
||||
ClientTemplateModel getClientTemplateById(String id, RealmModel realm);
|
||||
GroupModel getGroupById(String id, RealmModel realm);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.models;
|
|||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
@ -49,6 +50,15 @@ public interface UserSessionProvider extends Provider {
|
|||
|
||||
long getActiveUserSessions(RealmModel realm, ClientModel client);
|
||||
|
||||
/**
|
||||
* Returns a summary of client sessions key is client.getId()
|
||||
*
|
||||
* @param realm
|
||||
* @param offline
|
||||
* @return
|
||||
*/
|
||||
Map<String, Long> getActiveClientSessionStats(RealmModel realm, boolean offline);
|
||||
|
||||
/** This will remove attached ClientLoginSessionModels too **/
|
||||
void removeUserSession(RealmModel realm, UserSessionModel session);
|
||||
void removeUserSessions(RealmModel realm, UserModel user);
|
||||
|
|
25
server-spi/src/main/java/org/keycloak/models/cache/CachedObject.java
vendored
Normal file
25
server-spi/src/main/java/org/keycloak/models/cache/CachedObject.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.models.cache;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface CachedObject {
|
||||
long getCacheTimestamp();
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage;
|
||||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.PrioritizedComponentModel;
|
||||
import org.keycloak.models.cache.CachedObject;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CacheableStorageProviderModel extends PrioritizedComponentModel {
|
||||
public static final String CACHE_POLICY = "cachePolicy";
|
||||
public static final String MAX_LIFESPAN = "maxLifespan";
|
||||
public static final String EVICTION_HOUR = "evictionHour";
|
||||
public static final String EVICTION_MINUTE = "evictionMinute";
|
||||
public static final String EVICTION_DAY = "evictionDay";
|
||||
public static final String CACHE_INVALID_BEFORE = "cacheInvalidBefore";
|
||||
public static final String ENABLED = "enabled";
|
||||
|
||||
private transient CachePolicy cachePolicy;
|
||||
private transient long maxLifespan = -1;
|
||||
private transient int evictionHour = -1;
|
||||
private transient int evictionMinute = -1;
|
||||
private transient int evictionDay = -1;
|
||||
private transient long cacheInvalidBefore = -1;
|
||||
private transient Boolean enabled;
|
||||
|
||||
public CacheableStorageProviderModel() {
|
||||
}
|
||||
|
||||
public CacheableStorageProviderModel(ComponentModel copy) {
|
||||
super(copy);
|
||||
}
|
||||
|
||||
public CachePolicy getCachePolicy() {
|
||||
if (cachePolicy == null) {
|
||||
String str = getConfig().getFirst(CACHE_POLICY);
|
||||
if (str == null) return null;
|
||||
cachePolicy = CachePolicy.valueOf(str);
|
||||
}
|
||||
return cachePolicy;
|
||||
}
|
||||
|
||||
public void setCachePolicy(CachePolicy cachePolicy) {
|
||||
this.cachePolicy = cachePolicy;
|
||||
if (cachePolicy == null) {
|
||||
getConfig().remove(CACHE_POLICY);
|
||||
|
||||
} else {
|
||||
getConfig().putSingle(CACHE_POLICY, cachePolicy.name());
|
||||
}
|
||||
}
|
||||
|
||||
public long getMaxLifespan() {
|
||||
if (maxLifespan < 0) {
|
||||
String str = getConfig().getFirst(MAX_LIFESPAN);
|
||||
if (str == null) return -1;
|
||||
maxLifespan = Long.valueOf(str);
|
||||
}
|
||||
return maxLifespan;
|
||||
}
|
||||
|
||||
public void setMaxLifespan(long maxLifespan) {
|
||||
this.maxLifespan = maxLifespan;
|
||||
getConfig().putSingle(MAX_LIFESPAN, Long.toString(maxLifespan));
|
||||
}
|
||||
|
||||
public int getEvictionHour() {
|
||||
if (evictionHour < 0) {
|
||||
String str = getConfig().getFirst(EVICTION_HOUR);
|
||||
if (str == null) return -1;
|
||||
evictionHour = Integer.valueOf(str);
|
||||
}
|
||||
return evictionHour;
|
||||
}
|
||||
|
||||
public void setEvictionHour(int evictionHour) {
|
||||
if (evictionHour > 23 || evictionHour < 0) throw new IllegalArgumentException("Must be between 0 and 23");
|
||||
this.evictionHour = evictionHour;
|
||||
getConfig().putSingle(EVICTION_HOUR, Integer.toString(evictionHour));
|
||||
}
|
||||
|
||||
public int getEvictionMinute() {
|
||||
if (evictionMinute < 0) {
|
||||
String str = getConfig().getFirst(EVICTION_MINUTE);
|
||||
if (str == null) return -1;
|
||||
evictionMinute = Integer.valueOf(str);
|
||||
}
|
||||
return evictionMinute;
|
||||
}
|
||||
|
||||
public void setEvictionMinute(int evictionMinute) {
|
||||
if (evictionMinute > 59 || evictionMinute < 0) throw new IllegalArgumentException("Must be between 0 and 59");
|
||||
this.evictionMinute = evictionMinute;
|
||||
getConfig().putSingle(EVICTION_MINUTE, Integer.toString(evictionMinute));
|
||||
}
|
||||
|
||||
public int getEvictionDay() {
|
||||
if (evictionDay < 0) {
|
||||
String str = getConfig().getFirst(EVICTION_DAY);
|
||||
if (str == null) return -1;
|
||||
evictionDay = Integer.valueOf(str);
|
||||
}
|
||||
return evictionDay;
|
||||
}
|
||||
|
||||
public void setEvictionDay(int evictionDay) {
|
||||
if (evictionDay > 7 || evictionDay < 1) throw new IllegalArgumentException("Must be between 1 and 7");
|
||||
this.evictionDay = evictionDay;
|
||||
getConfig().putSingle(EVICTION_DAY, Integer.toString(evictionDay));
|
||||
}
|
||||
|
||||
public long getCacheInvalidBefore() {
|
||||
if (cacheInvalidBefore < 0) {
|
||||
String str = getConfig().getFirst(CACHE_INVALID_BEFORE);
|
||||
if (str == null) return -1;
|
||||
cacheInvalidBefore = Long.valueOf(str);
|
||||
}
|
||||
return cacheInvalidBefore;
|
||||
}
|
||||
|
||||
public void setCacheInvalidBefore(long cacheInvalidBefore) {
|
||||
this.cacheInvalidBefore = cacheInvalidBefore;
|
||||
getConfig().putSingle(CACHE_INVALID_BEFORE, Long.toString(cacheInvalidBefore));
|
||||
}
|
||||
|
||||
public void setEnabled(boolean flag) {
|
||||
enabled = flag;
|
||||
getConfig().putSingle(ENABLED, Boolean.toString(flag));
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
if (enabled == null) {
|
||||
String val = getConfig().getFirst(ENABLED);
|
||||
if (val == null) {
|
||||
enabled = true;
|
||||
} else {
|
||||
enabled = Boolean.valueOf(val);
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
|
||||
}
|
||||
|
||||
public long getLifespan() {
|
||||
UserStorageProviderModel.CachePolicy policy = getCachePolicy();
|
||||
long lifespan = -1;
|
||||
if (policy == null || policy == UserStorageProviderModel.CachePolicy.DEFAULT) {
|
||||
lifespan = -1;
|
||||
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
||||
if (getEvictionHour() > -1 && getEvictionMinute() > -1) {
|
||||
lifespan = dailyTimeout(getEvictionHour(), getEvictionMinute()) - Time.currentTimeMillis();
|
||||
}
|
||||
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
||||
if (getEvictionDay() > 0 && getEvictionHour() > -1 && getEvictionMinute() > -1) {
|
||||
lifespan = weeklyTimeout(getEvictionDay(), getEvictionHour(), getEvictionMinute()) - Time.currentTimeMillis();
|
||||
}
|
||||
} else if (policy == CacheableStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
||||
lifespan = getMaxLifespan();
|
||||
}
|
||||
return lifespan;
|
||||
}
|
||||
|
||||
public boolean shouldInvalidate(CachedObject cached) {
|
||||
boolean invalidate = false;
|
||||
if (!isEnabled()) {
|
||||
invalidate = true;
|
||||
} else {
|
||||
CacheableStorageProviderModel.CachePolicy policy = getCachePolicy();
|
||||
if (policy != null) {
|
||||
//String currentTime = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(Time.currentTimeMillis()));
|
||||
if (policy == CacheableStorageProviderModel.CachePolicy.NO_CACHE) {
|
||||
invalidate = true;
|
||||
} else if (cached.getCacheTimestamp() < getCacheInvalidBefore()) {
|
||||
invalidate = true;
|
||||
} else if (policy == CacheableStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
||||
if (cached.getCacheTimestamp() + getMaxLifespan() < Time.currentTimeMillis()) {
|
||||
invalidate = true;
|
||||
}
|
||||
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
||||
long dailyTimeout = dailyTimeout(getEvictionHour(), getEvictionMinute());
|
||||
dailyTimeout = dailyTimeout - (24 * 60 * 60 * 1000);
|
||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(dailyTimeout));
|
||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||
if (cached.getCacheTimestamp() <= dailyTimeout) {
|
||||
invalidate = true;
|
||||
}
|
||||
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
||||
int oneWeek = 7 * 24 * 60 * 60 * 1000;
|
||||
long weeklyTimeout = weeklyTimeout(getEvictionDay(), getEvictionHour(), getEvictionMinute());
|
||||
long lastTimeout = weeklyTimeout - oneWeek;
|
||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(weeklyTimeout));
|
||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||
if (cached.getCacheTimestamp() <= lastTimeout) {
|
||||
invalidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return invalidate;
|
||||
}
|
||||
|
||||
|
||||
public static long dailyTimeout(int hour, int minute) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal2.set(Calendar.MINUTE, minute);
|
||||
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
||||
int add = (24 * 60 * 60 * 1000);
|
||||
cal.add(Calendar.MILLISECOND, add);
|
||||
} else {
|
||||
cal.add(Calendar.MILLISECOND, (int)(cal2.getTimeInMillis() - cal.getTimeInMillis()));
|
||||
}
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static long weeklyTimeout(int day, int hour, int minute) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.setTimeInMillis(Time.currentTimeMillis());
|
||||
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal2.set(Calendar.MINUTE, minute);
|
||||
cal2.set(Calendar.DAY_OF_WEEK, day);
|
||||
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
||||
int add = (7 * 24 * 60 * 60 * 1000);
|
||||
cal2.add(Calendar.MILLISECOND, add);
|
||||
}
|
||||
|
||||
return cal2.getTimeInMillis();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum CachePolicy {
|
||||
NO_CACHE,
|
||||
DEFAULT,
|
||||
EVICT_DAILY,
|
||||
EVICT_WEEKLY,
|
||||
MAX_LIFESPAN
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package org.keycloak.storage;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -75,8 +76,15 @@ public class StorageId implements Serializable {
|
|||
public static boolean isLocalStorage(UserModel user) {
|
||||
return new StorageId(user.getId()).getProviderId() == null;
|
||||
}
|
||||
public static boolean isLocalStorage(String userId) {
|
||||
return new StorageId(userId).getProviderId() == null;
|
||||
public static boolean isLocalStorage(String id) {
|
||||
return new StorageId(id).getProviderId() == null;
|
||||
}
|
||||
|
||||
public static String resolveProviderId(ClientModel client) {
|
||||
return new StorageId(client.getId()).getProviderId();
|
||||
}
|
||||
public static boolean isLocalStorage(ClientModel client) {
|
||||
return new StorageId(client.getId()).getProviderId() == null;
|
||||
}
|
||||
public boolean isLocal() {
|
||||
return getProviderId() == null;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package org.keycloak.storage;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.PrioritizedComponentModel;
|
||||
|
||||
/**
|
||||
* Stored configuration of a User Storage provider instance.
|
||||
|
@ -26,27 +25,12 @@ import org.keycloak.component.PrioritizedComponentModel;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class UserStorageProviderModel extends PrioritizedComponentModel {
|
||||
public class UserStorageProviderModel extends CacheableStorageProviderModel {
|
||||
|
||||
public static final String CACHE_POLICY = "cachePolicy";
|
||||
public static final String MAX_LIFESPAN = "maxLifespan";
|
||||
public static final String EVICTION_HOUR = "evictionHour";
|
||||
public static final String EVICTION_MINUTE = "evictionMinute";
|
||||
public static final String EVICTION_DAY = "evictionDay";
|
||||
public static final String CACHE_INVALID_BEFORE = "cacheInvalidBefore";
|
||||
public static final String IMPORT_ENABLED = "importEnabled";
|
||||
public static final String FULL_SYNC_PERIOD = "fullSyncPeriod";
|
||||
public static final String CHANGED_SYNC_PERIOD = "changedSyncPeriod";
|
||||
public static final String LAST_SYNC = "lastSync";
|
||||
public static final String ENABLED = "enabled";
|
||||
|
||||
public static enum CachePolicy {
|
||||
NO_CACHE,
|
||||
DEFAULT,
|
||||
EVICT_DAILY,
|
||||
EVICT_WEEKLY,
|
||||
MAX_LIFESPAN
|
||||
}
|
||||
|
||||
public UserStorageProviderModel() {
|
||||
setProviderType(UserStorageProvider.class.getName());
|
||||
|
@ -60,105 +44,6 @@ public class UserStorageProviderModel extends PrioritizedComponentModel {
|
|||
private transient Integer changedSyncPeriod;
|
||||
private transient Integer lastSync;
|
||||
private transient Boolean importEnabled;
|
||||
private transient Boolean enabled;
|
||||
private transient CachePolicy cachePolicy;
|
||||
private transient long maxLifespan = -1;
|
||||
private transient int evictionHour = -1;
|
||||
private transient int evictionMinute = -1;
|
||||
private transient int evictionDay = -1;
|
||||
private transient long cacheInvalidBefore = -1;
|
||||
|
||||
public CachePolicy getCachePolicy() {
|
||||
if (cachePolicy == null) {
|
||||
String str = getConfig().getFirst(CACHE_POLICY);
|
||||
if (str == null) return null;
|
||||
cachePolicy = CachePolicy.valueOf(str);
|
||||
}
|
||||
return cachePolicy;
|
||||
}
|
||||
|
||||
public void setCachePolicy(CachePolicy cachePolicy) {
|
||||
this.cachePolicy = cachePolicy;
|
||||
if (cachePolicy == null) {
|
||||
getConfig().remove(CACHE_POLICY);
|
||||
|
||||
} else {
|
||||
getConfig().putSingle(CACHE_POLICY, cachePolicy.name());
|
||||
}
|
||||
}
|
||||
|
||||
public long getMaxLifespan() {
|
||||
if (maxLifespan < 0) {
|
||||
String str = getConfig().getFirst(MAX_LIFESPAN);
|
||||
if (str == null) return -1;
|
||||
maxLifespan = Long.valueOf(str);
|
||||
}
|
||||
return maxLifespan;
|
||||
}
|
||||
|
||||
public void setMaxLifespan(long maxLifespan) {
|
||||
this.maxLifespan = maxLifespan;
|
||||
getConfig().putSingle(MAX_LIFESPAN, Long.toString(maxLifespan));
|
||||
}
|
||||
|
||||
public int getEvictionHour() {
|
||||
if (evictionHour < 0) {
|
||||
String str = getConfig().getFirst(EVICTION_HOUR);
|
||||
if (str == null) return -1;
|
||||
evictionHour = Integer.valueOf(str);
|
||||
}
|
||||
return evictionHour;
|
||||
}
|
||||
|
||||
public void setEvictionHour(int evictionHour) {
|
||||
if (evictionHour > 23 || evictionHour < 0) throw new IllegalArgumentException("Must be between 0 and 23");
|
||||
this.evictionHour = evictionHour;
|
||||
getConfig().putSingle(EVICTION_HOUR, Integer.toString(evictionHour));
|
||||
}
|
||||
|
||||
public int getEvictionMinute() {
|
||||
if (evictionMinute < 0) {
|
||||
String str = getConfig().getFirst(EVICTION_MINUTE);
|
||||
if (str == null) return -1;
|
||||
evictionMinute = Integer.valueOf(str);
|
||||
}
|
||||
return evictionMinute;
|
||||
}
|
||||
|
||||
public void setEvictionMinute(int evictionMinute) {
|
||||
if (evictionMinute > 59 || evictionMinute < 0) throw new IllegalArgumentException("Must be between 0 and 59");
|
||||
this.evictionMinute = evictionMinute;
|
||||
getConfig().putSingle(EVICTION_MINUTE, Integer.toString(evictionMinute));
|
||||
}
|
||||
|
||||
public int getEvictionDay() {
|
||||
if (evictionDay < 0) {
|
||||
String str = getConfig().getFirst(EVICTION_DAY);
|
||||
if (str == null) return -1;
|
||||
evictionDay = Integer.valueOf(str);
|
||||
}
|
||||
return evictionDay;
|
||||
}
|
||||
|
||||
public void setEvictionDay(int evictionDay) {
|
||||
if (evictionDay > 7 || evictionDay < 1) throw new IllegalArgumentException("Must be between 1 and 7");
|
||||
this.evictionDay = evictionDay;
|
||||
getConfig().putSingle(EVICTION_DAY, Integer.toString(evictionDay));
|
||||
}
|
||||
|
||||
public long getCacheInvalidBefore() {
|
||||
if (cacheInvalidBefore < 0) {
|
||||
String str = getConfig().getFirst(CACHE_INVALID_BEFORE);
|
||||
if (str == null) return -1;
|
||||
cacheInvalidBefore = Long.valueOf(str);
|
||||
}
|
||||
return cacheInvalidBefore;
|
||||
}
|
||||
|
||||
public void setCacheInvalidBefore(long cacheInvalidBefore) {
|
||||
this.cacheInvalidBefore = cacheInvalidBefore;
|
||||
getConfig().putSingle(CACHE_INVALID_BEFORE, Long.toString(cacheInvalidBefore));
|
||||
}
|
||||
|
||||
public boolean isImportEnabled() {
|
||||
if (importEnabled == null) {
|
||||
|
@ -178,24 +63,6 @@ public class UserStorageProviderModel extends PrioritizedComponentModel {
|
|||
getConfig().putSingle(IMPORT_ENABLED, Boolean.toString(flag));
|
||||
}
|
||||
|
||||
public void setEnabled(boolean flag) {
|
||||
enabled = flag;
|
||||
getConfig().putSingle(ENABLED, Boolean.toString(flag));
|
||||
}
|
||||
|
||||
|
||||
public boolean isEnabled() {
|
||||
if (enabled == null) {
|
||||
String val = getConfig().getFirst(ENABLED);
|
||||
if (val == null) {
|
||||
enabled = true;
|
||||
} else {
|
||||
enabled = Boolean.valueOf(val);
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
|
||||
}
|
||||
|
||||
public int getFullSyncPeriod() {
|
||||
if (fullSyncPeriod == null) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
/**
|
||||
* Abstraction interface for lookoup of clients by id and clientId. These methods required for participating in login flows.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientLookupProvider {
|
||||
ClientModel getClientById(String id, RealmModel realm);
|
||||
ClientModel getClientByClientId(String clientId, RealmModel realm);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.storage.client.ClientLookupProvider;
|
||||
|
||||
/**
|
||||
* Base interface for components that want to provide an alternative storage mechanism for clients
|
||||
*
|
||||
* This is currently a private incomplete SPI. Please discuss on dev list if you want us to complete it or want to do the work yourself.
|
||||
* This work is described in KEYCLOAK-6408 JIRA issue.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientStorageProvider extends Provider, ClientLookupProvider {
|
||||
|
||||
|
||||
/**
|
||||
* Callback when a realm is removed. Implement this if, for example, you want to do some
|
||||
* cleanup in your user storage when a realm is removed
|
||||
*
|
||||
* @param realm
|
||||
*/
|
||||
default
|
||||
void preRemove(RealmModel realm) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when a group is removed. Allows you to do things like remove a user
|
||||
* group mapping in your external store if appropriate
|
||||
*
|
||||
* @param realm
|
||||
* @param group
|
||||
*/
|
||||
default
|
||||
void preRemove(RealmModel realm, GroupModel group) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when a role is removed. Allows you to do things like remove a user
|
||||
* role mapping in your external store if appropriate
|
||||
|
||||
* @param realm
|
||||
* @param role
|
||||
*/
|
||||
default
|
||||
void preRemove(RealmModel realm, RoleModel role) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.storage.client;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||
|
||||
/**
|
||||
* Stored configuration of a Client Storage provider instance.
|
||||
*
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class ClientStorageProviderModel extends CacheableStorageProviderModel {
|
||||
|
||||
public static final String ENABLED = "enabled";
|
||||
|
||||
public ClientStorageProviderModel() {
|
||||
setProviderType(ClientStorageProvider.class.getName());
|
||||
}
|
||||
|
||||
public ClientStorageProviderModel(ComponentModel copy) {
|
||||
super(copy);
|
||||
}
|
||||
|
||||
private transient Boolean enabled;
|
||||
|
||||
public void setEnabled(boolean flag) {
|
||||
enabled = flag;
|
||||
getConfig().putSingle(ENABLED, Boolean.toString(flag));
|
||||
}
|
||||
|
||||
|
||||
public boolean isEnabled() {
|
||||
if (enabled == null) {
|
||||
String val = getConfig().getFirst(ENABLED);
|
||||
if (val == null) {
|
||||
enabled = true;
|
||||
} else {
|
||||
enabled = Boolean.valueOf(val);
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import org.keycloak.component.ComponentFactory;
|
|||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.UserCredentialStoreManager;
|
||||
import org.keycloak.keys.DefaultKeyManager;
|
||||
import org.keycloak.models.ClientProvider;
|
||||
import org.keycloak.models.KeycloakContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
@ -35,6 +36,7 @@ import org.keycloak.models.cache.UserCache;
|
|||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
import org.keycloak.storage.ClientStorageManager;
|
||||
import org.keycloak.storage.UserStorageManager;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.theme.DefaultThemeManager;
|
||||
|
@ -58,6 +60,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
private RealmProvider model;
|
||||
private UserStorageManager userStorageManager;
|
||||
private ClientStorageManager clientStorageManager;
|
||||
private UserCredentialStoreManager userCredentialStorageManager;
|
||||
private UserSessionProvider sessionProvider;
|
||||
private AuthenticationSessionProvider authenticationSessionProvider;
|
||||
|
@ -135,6 +138,23 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
return getProvider(UserProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider realmLocalStorage() {
|
||||
return getProvider(RealmProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientProvider clientLocalStorage() {
|
||||
return realmLocalStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientProvider clientStorageManager() {
|
||||
if (clientStorageManager == null) clientStorageManager = new ClientStorageManager(this);
|
||||
return clientStorageManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserProvider userStorageManager() {
|
||||
if (userStorageManager == null) userStorageManager = new UserStorageManager(this);
|
||||
|
@ -232,6 +252,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
return model;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserSessionProvider sessions() {
|
||||
if (sessionProvider == null) {
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @resource User Storage Provider
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientStorageProviderResource {
|
||||
private static final Logger logger = Logger.getLogger(ClientStorageProviderResource.class);
|
||||
|
||||
protected RealmModel realm;
|
||||
|
||||
protected AdminPermissionEvaluator auth;
|
||||
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected ClientConnection clientConnection;
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
@Context
|
||||
protected HttpHeaders headers;
|
||||
|
||||
public ClientStorageProviderResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.realm = realm;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Need this for admin console to display simple name of provider when displaying client detail
|
||||
*
|
||||
* KEYCLOAK-4328
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Path("{id}/name")
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Map<String, String> getSimpleName(@PathParam("id") String id) {
|
||||
auth.clients().requireList();
|
||||
|
||||
ComponentModel model = realm.getComponent(id);
|
||||
if (model == null) {
|
||||
throw new NotFoundException("Could not find component");
|
||||
}
|
||||
if (!model.getProviderType().equals(ClientStorageProvider.class.getName())) {
|
||||
throw new NotFoundException("found, but not a ClientStorageProvider");
|
||||
}
|
||||
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("id", model.getId());
|
||||
data.put("name", model.getName());
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -98,7 +98,7 @@ public class ClientsResource {
|
|||
public List<ClientRepresentation> getClients(@QueryParam("clientId") String clientId, @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly) {
|
||||
List<ClientRepresentation> rep = new ArrayList<>();
|
||||
|
||||
if (clientId == null) {
|
||||
if (clientId == null || clientId.trim().equals("")) {
|
||||
List<ClientModel> clientModels = realm.getClients();
|
||||
auth.clients().requireList();
|
||||
boolean view = auth.clients().canView();
|
||||
|
|
|
@ -100,6 +100,7 @@ import java.security.cert.X509Certificate;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
@ -504,17 +505,38 @@ public class RealmAdminResource {
|
|||
public List<Map<String, String>> getClientSessionStats() {
|
||||
auth.realm().requireViewRealm();
|
||||
|
||||
List<Map<String, String>> data = new LinkedList<Map<String, String>>();
|
||||
for (ClientModel client : realm.getClients()) {
|
||||
long size = session.sessions().getActiveUserSessions(client.getRealm(), client);
|
||||
if (size == 0) continue;
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("id", client.getId());
|
||||
map.put("clientId", client.getClientId());
|
||||
map.put("active", size + "");
|
||||
data.add(map);
|
||||
Map<String, Map<String, String>> data = new HashMap();
|
||||
{
|
||||
Map<String, Long> activeCount =session.sessions().getActiveClientSessionStats(realm, false);
|
||||
for (Map.Entry<String, Long> entry : activeCount.entrySet()) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
ClientModel client = realm.getClientById(entry.getKey());
|
||||
map.put("id", client.getId());
|
||||
map.put("clientId", client.getClientId());
|
||||
map.put("active", entry.getValue().toString());
|
||||
map.put("offline", "0");
|
||||
data.put(client.getId(), map);
|
||||
|
||||
}
|
||||
}
|
||||
return data;
|
||||
{
|
||||
Map<String, Long> offlineCount = session.sessions().getActiveClientSessionStats(realm, true);
|
||||
for (Map.Entry<String, Long> entry : offlineCount.entrySet()) {
|
||||
Map<String, String> map = data.get(entry.getKey());
|
||||
if (map == null) {
|
||||
map = new HashMap<>();
|
||||
ClientModel client = realm.getClientById(entry.getKey());
|
||||
map.put("id", client.getId());
|
||||
map.put("clientId", client.getClientId());
|
||||
map.put("active", "0");
|
||||
data.put(client.getId(), map);
|
||||
}
|
||||
map.put("offline", entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
List<Map<String, String>> result = new LinkedList<>();
|
||||
for (Map<String, String> item : data.values()) result.add(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.keycloak.models.ClientTemplateModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.ForbiddenException;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -634,8 +635,8 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
|||
public Map<String, Boolean> getAccess(ClientModel client) {
|
||||
Map<String, Boolean> map = new HashMap<>();
|
||||
map.put("view", canView(client));
|
||||
map.put("manage", canManage(client));
|
||||
map.put("configure", canConfigure(client));
|
||||
map.put("manage", StorageId.isLocalStorage(client) && canManage(client));
|
||||
map.put("configure", StorageId.isLocalStorage(client) && canConfigure(client));
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.storage;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.reflections.Types;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.storage.client.ClientLookupProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProviderFactory;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientStorageManager implements ClientProvider {
|
||||
private static final Logger logger = Logger.getLogger(ClientStorageManager.class);
|
||||
|
||||
protected KeycloakSession session;
|
||||
|
||||
public static boolean isStorageProviderEnabled(RealmModel realm, String providerId) {
|
||||
ClientStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
return model.isEnabled();
|
||||
}
|
||||
|
||||
public static ClientStorageProviderModel getStorageProviderModel(RealmModel realm, String componentId) {
|
||||
ComponentModel model = realm.getComponent(componentId);
|
||||
if (model == null) return null;
|
||||
return new ClientStorageProviderModel(model);
|
||||
}
|
||||
|
||||
public static ClientStorageProvider getStorageProvider(KeycloakSession session, RealmModel realm, String componentId) {
|
||||
ComponentModel model = realm.getComponent(componentId);
|
||||
if (model == null) return null;
|
||||
ClientStorageProviderModel storageModel = new ClientStorageProviderModel(model);
|
||||
ClientStorageProviderFactory factory = (ClientStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(ClientStorageProvider.class, model.getProviderId());
|
||||
if (factory == null) {
|
||||
throw new ModelException("Could not find ClientStorageProviderFactory for: " + model.getProviderId());
|
||||
}
|
||||
return getStorageProviderInstance(session, storageModel, factory);
|
||||
}
|
||||
|
||||
|
||||
public static List<ClientStorageProviderModel> getStorageProviders(RealmModel realm) {
|
||||
return realm.getClientStorageProviders();
|
||||
}
|
||||
|
||||
public static ClientStorageProvider getStorageProviderInstance(KeycloakSession session, ClientStorageProviderModel model, ClientStorageProviderFactory factory) {
|
||||
ClientStorageProvider instance = (ClientStorageProvider)session.getAttribute(model.getId());
|
||||
if (instance != null) return instance;
|
||||
instance = factory.create(session, model);
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("ClientStorageProvideFactory (of type " + factory.getClass().getName() + ") produced a null instance");
|
||||
}
|
||||
session.enlistForClose(instance);
|
||||
session.setAttribute(model.getId(), instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
public static <T> List<T> getStorageProviders(KeycloakSession session, RealmModel realm, Class<T> type) {
|
||||
List<T> list = new LinkedList<>();
|
||||
for (ClientStorageProviderModel model : getStorageProviders(realm)) {
|
||||
ClientStorageProviderFactory factory = (ClientStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(ClientStorageProvider.class, model.getProviderId());
|
||||
if (factory == null) {
|
||||
logger.warnv("Configured ClientStorageProvider {0} of provider id {1} does not exist in realm {2}", model.getName(), model.getProviderId(), realm.getName());
|
||||
continue;
|
||||
}
|
||||
if (Types.supports(type, factory, ClientStorageProviderFactory.class)) {
|
||||
list.add(type.cast(getStorageProviderInstance(session, model, factory)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public static <T> List<T> getEnabledStorageProviders(KeycloakSession session, RealmModel realm, Class<T> type) {
|
||||
List<T> list = new LinkedList<>();
|
||||
for (ClientStorageProviderModel model : getStorageProviders(realm)) {
|
||||
if (!model.isEnabled()) continue;
|
||||
ClientStorageProviderFactory factory = (ClientStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(ClientStorageProvider.class, model.getProviderId());
|
||||
if (factory == null) {
|
||||
logger.warnv("Configured ClientStorageProvider {0} of provider id {1} does not exist in realm {2}", model.getName(), model.getProviderId(), realm.getName());
|
||||
continue;
|
||||
}
|
||||
if (Types.supports(type, factory, ClientStorageProviderFactory.class)) {
|
||||
list.add(type.cast(getStorageProviderInstance(session, model, factory)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public ClientStorageManager(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
if (storageId.getProviderId() == null) {
|
||||
return session.clientLocalStorage().getClientById(id, realm);
|
||||
}
|
||||
ClientLookupProvider provider = (ClientLookupProvider)getStorageProvider(session, realm, storageId.getProviderId());
|
||||
if (provider == null) return null;
|
||||
if (!isStorageProviderEnabled(realm, storageId.getProviderId())) return null;
|
||||
return provider.getClientById(id, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
ClientModel client = session.clientLocalStorage().getClientByClientId(clientId, realm);
|
||||
if (client != null) {
|
||||
return client;
|
||||
}
|
||||
for (ClientLookupProvider provider : getEnabledStorageProviders(session, realm, ClientLookupProvider.class)) {
|
||||
client = provider.getClientByClientId(clientId, realm);
|
||||
if (client != null) return client;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||
return session.clientLocalStorage().addClient(realm, clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
return session.clientLocalStorage().addClient(realm, id, clientId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<ClientModel> getClients(RealmModel realm) {
|
||||
return session.clientLocalStorage().getClients(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
if (!StorageId.isLocalStorage(client.getId())) {
|
||||
throw new RuntimeException("Federated clients do not support this operation");
|
||||
}
|
||||
return session.clientLocalStorage().addClientRole(realm, client, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
|
||||
if (!StorageId.isLocalStorage(client.getId())) {
|
||||
throw new RuntimeException("Federated clients do not support this operation");
|
||||
}
|
||||
return session.clientLocalStorage().addClientRole(realm, client, id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
if (!StorageId.isLocalStorage(client.getId())) {
|
||||
//throw new RuntimeException("Federated clients do not support this operation");
|
||||
return null;
|
||||
}
|
||||
return session.clientLocalStorage().getClientRole(realm, client, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
|
||||
if (!StorageId.isLocalStorage(client.getId())) {
|
||||
//throw new RuntimeException("Federated clients do not support this operation");
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
return session.clientLocalStorage().getClientRoles(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id, RealmModel realm) {
|
||||
if (!StorageId.isLocalStorage(id)) {
|
||||
throw new RuntimeException("Federated clients do not support this operation");
|
||||
}
|
||||
return session.clientLocalStorage().removeClient(id, realm);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -40,6 +40,7 @@ import org.keycloak.models.cache.UserCache;
|
|||
import org.keycloak.models.utils.ComponentUtil;
|
||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.storage.user.ImportedUserValidation;
|
||||
import org.keycloak.storage.user.UserBulkUpdateProvider;
|
||||
|
@ -696,6 +697,11 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo
|
|||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||
if (component.getProviderType().equals(ClientStorageProvider.class.getName())) {
|
||||
localStorage().preRemove(realm, component);
|
||||
if (getFederatedStorage() != null) getFederatedStorage().preRemove(realm, component);
|
||||
return;
|
||||
}
|
||||
if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
|
||||
localStorage().preRemove(realm, component);
|
||||
if (getFederatedStorage() != null) getFederatedStorage().preRemove(realm, component);
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.client.AbstractReadOnlyClientStorageAdapter;
|
||||
import org.keycloak.storage.client.ClientLookupProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HardcodedClientStorageProvider implements ClientStorageProvider, ClientLookupProvider {
|
||||
protected KeycloakSession session;
|
||||
protected ClientStorageProviderModel component;
|
||||
protected String clientId;
|
||||
protected String redirectUri;
|
||||
protected boolean consent;
|
||||
|
||||
public HardcodedClientStorageProvider(KeycloakSession session, ClientStorageProviderModel component) {
|
||||
this.session = session;
|
||||
this.component = component;
|
||||
this.clientId = component.getConfig().getFirst(HardcodedClientStorageProviderFactory.CLIENT_ID);
|
||||
this.redirectUri = component.getConfig().getFirst(HardcodedClientStorageProviderFactory.REDIRECT_URI);
|
||||
this.consent = "true".equals(component.getConfig().getFirst(HardcodedClientStorageProviderFactory.CONSENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
StorageId storageId = new StorageId(id);
|
||||
final String clientId = storageId.getExternalId();
|
||||
if (this.clientId.equals(clientId)) return new ClientAdapter(realm);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
if (this.clientId.equals(clientId)) return new ClientAdapter(realm);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public class ClientAdapter extends AbstractReadOnlyClientStorageAdapter {
|
||||
|
||||
public ClientAdapter(RealmModel realm) {
|
||||
super(HardcodedClientStorageProvider.this.session, realm, HardcodedClientStorageProvider.this.component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Federated Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Pulled in from client storage provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getWebOrigins() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRedirectUris() {
|
||||
HashSet<String> set = new HashSet<>();
|
||||
set.add(redirectUri);
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManagementUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBearerOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientAuthenticatorType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateSecret(String secret) {
|
||||
return "password".equals(secret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecret() {
|
||||
return "password";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRegistrationToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return "openid-connect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticationFlowBindingOverride(String binding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAuthenticationFlowBindingOverrides() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFrontchannelLogout() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublicClient() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsentRequired() {
|
||||
return consent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStandardFlowEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImplicitFlowEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectAccessGrantsEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTemplateScope() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTemplateMappers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTemplateConfig() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNotBefore() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtocolMapperModel> getProtocolMappers() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolMapperModel getProtocolMapperById(String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullScopeAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getScopeMappings() {
|
||||
RoleModel offlineAccess = realm.getRole("offline_access");
|
||||
Set<RoleModel> set = new HashSet<>();
|
||||
set.add(offlineAccess);
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmScopeMappings() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasScope(RoleModel role) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.client.ClientStorageProviderFactory;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HardcodedClientStorageProviderFactory implements ClientStorageProviderFactory<HardcodedClientStorageProvider> {
|
||||
@Override
|
||||
public HardcodedClientStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new HardcodedClientStorageProvider(session, new ClientStorageProviderModel(model));
|
||||
}
|
||||
|
||||
|
||||
public static final String PROVIDER_ID = "hardcoded-client";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
protected static final List<ProviderConfigProperty> CONFIG_PROPERTIES;
|
||||
|
||||
public static final String CLIENT_ID = "client_id";
|
||||
|
||||
public static final String REDIRECT_URI = "redirect_uri";
|
||||
public static final String CONSENT = "consent";
|
||||
|
||||
static {
|
||||
CONFIG_PROPERTIES = ProviderConfigurationBuilder.create()
|
||||
.property().name(CLIENT_ID)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Hardcoded Client Id")
|
||||
.helpText("Only this client id is available for lookup")
|
||||
.defaultValue("hardcoded-client")
|
||||
.add()
|
||||
.property().name(REDIRECT_URI)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Redirect Uri")
|
||||
.helpText("Valid redirect uri. Only one allowed")
|
||||
.defaultValue("http://localhost:8180/*")
|
||||
.add()
|
||||
.property().name(CONSENT)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Consent Required")
|
||||
.helpText("Is consent required")
|
||||
.defaultValue("false")
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory
|
|
@ -0,0 +1,481 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.federation.storage;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowBindings;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.cache.infinispan.ClientAdapter;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
|
||||
import org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory;
|
||||
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
||||
import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
|
||||
import org.keycloak.testsuite.forms.UsernameOnlyAuthenticator;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.TokenUtil;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Calendar.DAY_OF_WEEK;
|
||||
import static java.util.Calendar.HOUR_OF_DAY;
|
||||
import static java.util.Calendar.MINUTE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.storage.CacheableStorageProviderModel.CACHE_POLICY;
|
||||
import static org.keycloak.storage.CacheableStorageProviderModel.EVICTION_DAY;
|
||||
import static org.keycloak.storage.CacheableStorageProviderModel.EVICTION_HOUR;
|
||||
import static org.keycloak.storage.CacheableStorageProviderModel.EVICTION_MINUTE;
|
||||
import static org.keycloak.storage.CacheableStorageProviderModel.MAX_LIFESPAN;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsername;
|
||||
|
||||
/**
|
||||
* Test that clients can override auth flows
|
||||
*
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class ClientStorageTest extends AbstractTestRealmKeycloakTest {
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
}
|
||||
|
||||
protected String providerId;
|
||||
|
||||
@Deployment
|
||||
public static WebArchive deploy() {
|
||||
return RunOnServerDeployment.create(UserResource.class)
|
||||
.addPackages(true, "org.keycloak.testsuite");
|
||||
}
|
||||
|
||||
protected String addComponent(ComponentRepresentation component) {
|
||||
Response resp = adminClient.realm("test").components().add(component);
|
||||
resp.close();
|
||||
String id = ApiUtil.getCreatedId(resp);
|
||||
getCleanup().addComponentId(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addProvidersBeforeTest() throws URISyntaxException, IOException {
|
||||
ComponentRepresentation provider = new ComponentRepresentation();
|
||||
provider.setName("client-storage-hardcoded");
|
||||
provider.setProviderId(HardcodedClientStorageProviderFactory.PROVIDER_ID);
|
||||
provider.setProviderType(ClientStorageProvider.class.getName());
|
||||
provider.setConfig(new MultivaluedHashMap<>());
|
||||
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
|
||||
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, oauth.getRedirectUri());
|
||||
|
||||
providerId = addComponent(provider);
|
||||
}
|
||||
|
||||
protected String userId;
|
||||
|
||||
@Before
|
||||
public void clientConfiguration() {
|
||||
userId = findUserByUsername(adminClient.realm("test"), "test-user@localhost").getId();
|
||||
oauth.clientId("hardcoded-client");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testClientStats() throws Exception {
|
||||
testDirectGrant("hardcoded-client");
|
||||
testDirectGrant("hardcoded-client");
|
||||
testBrowser("test-app");
|
||||
offlineTokenDirectGrantFlowNoRefresh();
|
||||
List<Map<String, String>> list = adminClient.realm("test").getClientSessionStats();
|
||||
boolean hardTested = false;
|
||||
boolean testAppTested = false;
|
||||
for (Map<String, String> entry : list) {
|
||||
if (entry.get("clientId").equals("hardcoded-client")) {
|
||||
Assert.assertEquals("3", entry.get("active"));
|
||||
Assert.assertEquals("1", entry.get("offline"));
|
||||
hardTested = true;
|
||||
} else if (entry.get("clientId").equals("test-app")) {
|
||||
Assert.assertEquals("1", entry.get("active"));
|
||||
Assert.assertEquals("0", entry.get("offline"));
|
||||
testAppTested = true;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue(hardTested && testAppTested);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBrowser() throws Exception {
|
||||
String clientId = "hardcoded-client";
|
||||
testBrowser(clientId);
|
||||
//Thread.sleep(10000000);
|
||||
}
|
||||
|
||||
private void testBrowser(String clientId) {
|
||||
oauth.clientId(clientId);
|
||||
String loginFormUrl = oauth.getLoginFormUrl();
|
||||
//log.info("loginFormUrl: " + loginFormUrl);
|
||||
|
||||
//Thread.sleep(10000000);
|
||||
|
||||
driver.navigate().to(loginFormUrl);
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
// Fill username+password. I am successfully authenticated
|
||||
oauth.fillLoginForm("test-user@localhost", "password");
|
||||
appPage.assertCurrent();
|
||||
|
||||
events.expectLogin().client(clientId).detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
Assert.assertNotNull(tokenResponse.getAccessToken());
|
||||
Assert.assertNotNull(tokenResponse.getRefreshToken());
|
||||
|
||||
events.clear();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrantAccessTokenNoOverride() throws Exception {
|
||||
testDirectGrant("hardcoded-client");
|
||||
}
|
||||
|
||||
private void testDirectGrant(String clientId) {
|
||||
Client httpClient = javax.ws.rs.client.ClientBuilder.newClient();
|
||||
String grantUri = oauth.getResourceOwnerPasswordCredentialGrantUrl();
|
||||
WebTarget grantTarget = httpClient.target(grantUri);
|
||||
|
||||
{ // test no password
|
||||
String header = BasicAuthHelper.createHeader(clientId, "password");
|
||||
Form form = new Form();
|
||||
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
|
||||
form.param("username", "test-user@localhost");
|
||||
Response response = grantTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, header)
|
||||
.post(Entity.form(form));
|
||||
assertEquals(401, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
{ // test invalid password
|
||||
String header = BasicAuthHelper.createHeader(clientId, "password");
|
||||
Form form = new Form();
|
||||
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
|
||||
form.param("username", "test-user@localhost");
|
||||
form.param("password", "invalid");
|
||||
Response response = grantTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, header)
|
||||
.post(Entity.form(form));
|
||||
assertEquals(401, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
{ // test valid password
|
||||
String header = BasicAuthHelper.createHeader(clientId, "password");
|
||||
Form form = new Form();
|
||||
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
|
||||
form.param("username", "test-user@localhost");
|
||||
form.param("password", "password");
|
||||
Response response = grantTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, header)
|
||||
.post(Entity.form(form));
|
||||
assertEquals(200, response.getStatus());
|
||||
response.close();
|
||||
}
|
||||
|
||||
httpClient.close();
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDailyEviction() {
|
||||
testIsCached();
|
||||
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||
Calendar eviction = Calendar.getInstance();
|
||||
eviction.add(Calendar.HOUR, 1);
|
||||
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.EVICT_DAILY);
|
||||
model.setEvictionHour(eviction.get(HOUR_OF_DAY));
|
||||
model.setEvictionMinute(eviction.get(MINUTE));
|
||||
realm.updateComponent(model);
|
||||
});
|
||||
testIsCached();
|
||||
setTimeOffset(2 * 60 * 60); // 2 hours in future
|
||||
testNotCached();
|
||||
testIsCached();
|
||||
|
||||
setDefaultCachePolicy();
|
||||
testIsCached();
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testWeeklyEviction() {
|
||||
testIsCached();
|
||||
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||
Calendar eviction = Calendar.getInstance();
|
||||
eviction.add(Calendar.HOUR, 4 * 24);
|
||||
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.EVICT_WEEKLY);
|
||||
model.setEvictionDay(eviction.get(DAY_OF_WEEK));
|
||||
model.setEvictionHour(eviction.get(HOUR_OF_DAY));
|
||||
model.setEvictionMinute(eviction.get(MINUTE));
|
||||
realm.updateComponent(model);
|
||||
});
|
||||
testIsCached();
|
||||
setTimeOffset(2 * 24 * 60 * 60); // 2 days in future
|
||||
testIsCached();
|
||||
setTimeOffset(5 * 24 * 60 * 60); // 5 days in future
|
||||
testNotCached();
|
||||
testIsCached();
|
||||
|
||||
setDefaultCachePolicy();
|
||||
testIsCached();
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testMaxLifespan() {
|
||||
testIsCached();
|
||||
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.MAX_LIFESPAN);
|
||||
model.setMaxLifespan(1 * 60 * 60 * 1000);
|
||||
realm.updateComponent(model);
|
||||
});
|
||||
testIsCached();
|
||||
|
||||
setTimeOffset(1/2 * 60 * 60); // 1/2 hour in future
|
||||
|
||||
testIsCached();
|
||||
|
||||
setTimeOffset(2 * 60 * 60); // 2 hours in future
|
||||
|
||||
testNotCached();
|
||||
testIsCached();
|
||||
|
||||
setDefaultCachePolicy();
|
||||
testIsCached();
|
||||
|
||||
}
|
||||
|
||||
private void testNotCached() {
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientModel hardcoded = realm.getClientByClientId("hardcoded-client");
|
||||
Assert.assertNotNull(hardcoded);
|
||||
Assert.assertFalse(hardcoded instanceof ClientAdapter);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsCached() {
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientModel hardcoded = realm.getClientByClientId("hardcoded-client");
|
||||
Assert.assertNotNull(hardcoded);
|
||||
Assert.assertTrue(hardcoded instanceof org.keycloak.models.cache.infinispan.ClientAdapter);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoCache() {
|
||||
testIsCached();
|
||||
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.NO_CACHE);
|
||||
realm.updateComponent(model);
|
||||
});
|
||||
|
||||
testNotCached();
|
||||
|
||||
// test twice because updating component should evict
|
||||
testNotCached();
|
||||
|
||||
// set it back
|
||||
setDefaultCachePolicy();
|
||||
testIsCached();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void setDefaultCachePolicy() {
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.DEFAULT);
|
||||
realm.updateComponent(model);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void offlineTokenDirectGrantFlow() throws Exception {
|
||||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.clientId("hardcoded-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
Assert.assertNull(tokenResponse.getErrorDescription());
|
||||
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
|
||||
String offlineTokenString = tokenResponse.getRefreshToken();
|
||||
RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
|
||||
|
||||
events.expectLogin()
|
||||
.client("hardcoded-client")
|
||||
.user(userId)
|
||||
.session(token.getSessionState())
|
||||
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||
.detail(Details.TOKEN_ID, token.getId())
|
||||
.detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
|
||||
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
|
||||
.detail(Details.USERNAME, "test-user@localhost")
|
||||
.removeDetail(Details.CODE_ID)
|
||||
.removeDetail(Details.REDIRECT_URI)
|
||||
.removeDetail(Details.CONSENT)
|
||||
.assertEvent();
|
||||
|
||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
||||
Assert.assertEquals(0, offlineToken.getExpiration());
|
||||
|
||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId);
|
||||
|
||||
// Assert same token can be refreshed again
|
||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId);
|
||||
}
|
||||
public void offlineTokenDirectGrantFlowNoRefresh() throws Exception {
|
||||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.clientId("hardcoded-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
Assert.assertNull(tokenResponse.getErrorDescription());
|
||||
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
|
||||
String offlineTokenString = tokenResponse.getRefreshToken();
|
||||
RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
|
||||
}
|
||||
|
||||
private String testRefreshWithOfflineToken(AccessToken oldToken, RefreshToken offlineToken, String offlineTokenString,
|
||||
final String sessionId, String userId) {
|
||||
// Change offset to big value to ensure userSession expired
|
||||
setTimeOffset(99999);
|
||||
Assert.assertFalse(oldToken.isActive());
|
||||
Assert.assertTrue(offlineToken.isActive());
|
||||
|
||||
// Assert userSession expired
|
||||
testingClient.testing().removeExpired("test");
|
||||
try {
|
||||
testingClient.testing().removeUserSession("test", sessionId);
|
||||
} catch (NotFoundException nfe) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(offlineTokenString, "password");
|
||||
AccessToken refreshedToken = oauth.verifyToken(response.getAccessToken());
|
||||
Assert.assertEquals(200, response.getStatusCode());
|
||||
Assert.assertEquals(sessionId, refreshedToken.getSessionState());
|
||||
|
||||
// Assert new refreshToken in the response
|
||||
String newRefreshToken = response.getRefreshToken();
|
||||
Assert.assertNotNull(newRefreshToken);
|
||||
Assert.assertNotEquals(oldToken.getId(), refreshedToken.getId());
|
||||
|
||||
Assert.assertEquals(userId, refreshedToken.getSubject());
|
||||
|
||||
Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole(Constants.OFFLINE_ACCESS_ROLE));
|
||||
|
||||
|
||||
EventRepresentation refreshEvent = events.expectRefresh(offlineToken.getId(), sessionId)
|
||||
.client("hardcoded-client")
|
||||
.user(userId)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
|
||||
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
|
||||
.assertEvent();
|
||||
Assert.assertNotEquals(oldToken.getId(), refreshEvent.getDetails().get(Details.TOKEN_ID));
|
||||
|
||||
setTimeOffset(0);
|
||||
return newRefreshToken;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation.storage;
|
||||
|
||||
import org.infinispan.stream.CacheCollectors;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class MapCollectTest {
|
||||
|
||||
public static class UserSessionObject {
|
||||
public String id;
|
||||
public String realm;
|
||||
public Set<String> clients = new HashSet<>();
|
||||
|
||||
public UserSessionObject(String realm, String... clients) {
|
||||
this.id = UUID.randomUUID().toString();
|
||||
this.realm = realm;
|
||||
for (String c : clients) this.clients.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RealmFilter implements Predicate<UserSessionObject> {
|
||||
protected String realm;
|
||||
|
||||
public RealmFilter(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(UserSessionObject entry) {
|
||||
return entry.realm.equals(realm);
|
||||
}
|
||||
|
||||
public static RealmFilter create(String realm) {
|
||||
return new RealmFilter(realm);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> clients(UserSessionObject s) {
|
||||
return s.clients;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMe() throws Exception {
|
||||
|
||||
List<UserSessionObject> list = Arrays.asList(
|
||||
new UserSessionObject("realm1", "a", "b")
|
||||
, new UserSessionObject("realm1", "a", "c")
|
||||
, new UserSessionObject("realm1", "a", "d")
|
||||
, new UserSessionObject("realm1", "a", "b")
|
||||
, new UserSessionObject("realm2", "a", "b")
|
||||
, new UserSessionObject("realm2", "a", "c")
|
||||
, new UserSessionObject("realm2", "a", "b")
|
||||
|
||||
);
|
||||
|
||||
Map<String, Long> result = list.stream().collect(
|
||||
Collectors.groupingBy(s -> s.realm, Collectors.summingLong(i -> 1)));
|
||||
|
||||
for (Map.Entry<String, Long> entry : result.entrySet()) {
|
||||
System.out.println(entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
|
||||
result = list.stream()
|
||||
.filter(RealmFilter.create("realm1"))
|
||||
.map(s->s.clients)
|
||||
.flatMap(c->c.stream())
|
||||
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
|
||||
|
||||
for (Map.Entry<String, Long> entry : result.entrySet()) {
|
||||
System.out.println(entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
|||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.CACHE_POLICY;
|
||||
import org.keycloak.storage.UserStorageProviderModel.CachePolicy;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel.CachePolicy;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.EVICTION_DAY;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.EVICTION_HOUR;
|
||||
import static org.keycloak.storage.UserStorageProviderModel.EVICTION_MINUTE;
|
||||
|
|
|
@ -167,16 +167,36 @@ public class UserStorageTest {
|
|||
KeycloakSession session = keycloakRule.startSession();
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
long thorTimestamp = thor.getCacheTimestamp();
|
||||
long lastTimestamp = thor.getCacheTimestamp();
|
||||
realm.updateComponent(model);
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
realm.updateComponent(model);
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
// test is cached
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
// thor should be evicted because we changed the model
|
||||
Assert.assertTrue(thor.getCacheTimestamp() > lastTimestamp);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
|
||||
Time.setOffset(60 * 2 * 60); // 2 hours
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
UserModel thor2 = session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertFalse(thor2 instanceof CachedUserModel);
|
||||
// thor should be evicted because we put it 2 hours in the future
|
||||
if (thor2 instanceof CachedUserModel) {
|
||||
Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp);
|
||||
}
|
||||
model.getConfig().remove("cachePolicy");
|
||||
model.getConfig().remove("evictionHour");
|
||||
model.getConfig().remove("evictionMinute");
|
||||
|
@ -199,24 +219,46 @@ public class UserStorageTest {
|
|||
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
realm.updateComponent(model);
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
long lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
Time.setOffset(60 * 60 * 24 * 2); // 2 days in future, should be cached still
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
// test still
|
||||
UserModel thor2 = session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertTrue(thor2 instanceof CachedUserModel);
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertEquals(thor.getCacheTimestamp(), lastTimestamp);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
Time.setOffset(Time.getOffset() + 60 * 60 * 24 * 3); // 3 days into future, cache will be invalidated
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor2 = session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertFalse(thor2 instanceof CachedUserModel);
|
||||
UserModel thor2 = session.users().getUserByUsername("thor", realm);
|
||||
// thor should be evicted because we put it 2 hours in the future
|
||||
if (thor2 instanceof CachedUserModel) {
|
||||
Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp);
|
||||
}
|
||||
model.getConfig().remove("cachePolicy");
|
||||
model.getConfig().remove("evictionHour");
|
||||
model.getConfig().remove("evictionMinute");
|
||||
|
@ -233,24 +275,44 @@ public class UserStorageTest {
|
|||
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
realm.updateComponent(model);
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
Time.setOffset(60 * 5); // 5 minutes in future, should be cached still
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
CachedUserModel thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
long lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
// test still
|
||||
UserModel thor2 = session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertTrue(thor2 instanceof CachedUserModel);
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
|
||||
Time.setOffset(60 * 5); // 5 minutes in future, should be cached still
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor = (CachedUserModel)session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertEquals(thor.getCacheTimestamp(), lastTimestamp);
|
||||
lastTimestamp = thor.getCacheTimestamp();
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
Time.setOffset(60 * 20); // 20 minutes into future, cache will be invalidated
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
thor2 = session.users().getUserByUsername("thor", realm);
|
||||
Assert.assertFalse(thor2 instanceof CachedUserModel);
|
||||
UserModel thor2 = session.users().getUserByUsername("thor", realm);
|
||||
// thor should be evicted because we put it 2 hours in the future
|
||||
if (thor2 instanceof CachedUserModel) {
|
||||
Assert.assertTrue(((CachedUserModel)thor2).getCacheTimestamp() > lastTimestamp);
|
||||
}
|
||||
keycloakRule.stopSession(session, true);
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("test");
|
||||
model.getConfig().remove("cachePolicy");
|
||||
model.getConfig().remove("maxLifespan");
|
||||
realm.updateComponent(model);
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.testsuite.model;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
|
@ -30,6 +31,8 @@ import org.keycloak.models.UserConsentModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
import org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -38,6 +41,8 @@ import java.util.List;
|
|||
*/
|
||||
public class UserConsentModelTest extends AbstractModelTest {
|
||||
|
||||
private ComponentModel clientStorageComponent;
|
||||
|
||||
@Before
|
||||
public void setupEnv() {
|
||||
RealmModel realm = realmManager.createRealm("original");
|
||||
|
@ -87,6 +92,22 @@ public class UserConsentModelTest extends AbstractModelTest {
|
|||
maryFooGrant.addGrantedProtocolMapper(fooMapper);
|
||||
realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
|
||||
|
||||
ClientStorageProviderModel clientStorage = new ClientStorageProviderModel();
|
||||
clientStorage.setProviderId(HardcodedClientStorageProviderFactory.PROVIDER_ID);
|
||||
clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
|
||||
clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, "http://localhost:8081/*");
|
||||
clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CONSENT, "true");
|
||||
clientStorage.setParentId(realm.getId());
|
||||
clientStorageComponent = realm.addComponentModel(clientStorage);
|
||||
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
|
||||
Assert.assertNotNull(hardcodedClient);
|
||||
|
||||
UserConsentModel maryHardcodedGrant = new UserConsentModel(hardcodedClient);
|
||||
realmManager.getSession().users().addConsent(realm, mary.getId(), maryHardcodedGrant);
|
||||
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
|
@ -125,7 +146,15 @@ public class UserConsentModelTest extends AbstractModelTest {
|
|||
Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
|
||||
Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
|
||||
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
UserConsentModel maryHardcodedConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId());
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
|
||||
Assert.assertNotNull("Created Date should be set", maryHardcodedConsent.getCreatedDate());
|
||||
Assert.assertNotNull("Last Updated Date should be set", maryHardcodedConsent.getLastUpdatedDate());
|
||||
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary.getId(), barClient.getId()));
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), hardcodedClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -139,14 +168,26 @@ public class UserConsentModelTest extends AbstractModelTest {
|
|||
List<UserConsentModel> johnConsents = realmManager.getSession().users().getConsents(realm, john.getId());
|
||||
Assert.assertEquals(2, johnConsents.size());
|
||||
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
|
||||
List<UserConsentModel> maryConsents = realmManager.getSession().users().getConsents(realm, mary.getId());
|
||||
Assert.assertEquals(1, maryConsents.size());
|
||||
Assert.assertEquals(2, maryConsents.size());
|
||||
UserConsentModel maryConsent = maryConsents.get(0);
|
||||
UserConsentModel maryHardcodedConsent = maryConsents.get(1);
|
||||
if (maryConsents.get(0).getClient().getId().equals(hardcodedClient.getId())) {
|
||||
maryConsent = maryConsents.get(1);
|
||||
maryHardcodedConsent = maryConsents.get(0);
|
||||
|
||||
}
|
||||
Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
|
||||
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
|
||||
Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
|
||||
|
||||
Assert.assertEquals(maryHardcodedConsent.getClient().getId(), hardcodedClient.getId());
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -190,14 +231,19 @@ public class UserConsentModelTest extends AbstractModelTest {
|
|||
RealmModel realm = realmManager.getRealm("original");
|
||||
ClientModel fooClient = realm.getClientByClientId("foo-client");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
|
||||
realmManager.getSession().users().revokeConsentForClient(realm, john.getId(), fooClient.getId());
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
realmManager.getSession().users().revokeConsentForClient(realm, mary.getId(), hardcodedClient.getId());
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
john = session.users().getUserByUsername("john", realm);
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId()));
|
||||
mary = session.users().getUserByUsername("mary", realm);
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -206,6 +252,8 @@ public class UserConsentModelTest extends AbstractModelTest {
|
|||
RealmModel realm = realmManager.getRealm("original");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
session.users().removeUser(realm, john);
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
session.users().removeUser(realm, mary);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -270,6 +318,24 @@ public class UserConsentModelTest extends AbstractModelTest {
|
|||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientStorageTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
realm.removeComponent(clientStorageComponent);
|
||||
commit();
|
||||
|
||||
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
Assert.assertNull(hardcodedClient);
|
||||
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
|
||||
List<UserConsentModel> maryConsents = realmManager.getSession().users().getConsents(realm, mary.getId());
|
||||
Assert.assertEquals(1, maryConsents.size());
|
||||
}
|
||||
|
||||
private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, UserConsentModel consentModel) {
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
return consentModel.isRoleGranted(role);
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.testsuite.model;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
|
@ -31,6 +32,8 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||
import org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory;
|
||||
import org.keycloak.testsuite.federation.storage.UserMapStorageFactory;
|
||||
import org.keycloak.testsuite.federation.storage.UserPropertyFileStorageFactory;
|
||||
|
||||
|
@ -41,6 +44,8 @@ import java.util.List;
|
|||
*/
|
||||
public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
||||
|
||||
private ComponentModel clientStorageComponent;
|
||||
|
||||
@Before
|
||||
public void setupEnv() {
|
||||
RealmModel realm = realmManager.createRealm("original");
|
||||
|
@ -97,6 +102,22 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
|||
maryFooGrant.addGrantedProtocolMapper(fooMapper);
|
||||
realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
|
||||
|
||||
ClientStorageProviderModel clientStorage = new ClientStorageProviderModel();
|
||||
clientStorage.setProviderId(HardcodedClientStorageProviderFactory.PROVIDER_ID);
|
||||
clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
|
||||
clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, "http://localhost:8081/*");
|
||||
clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CONSENT, "true");
|
||||
clientStorage.setParentId(realm.getId());
|
||||
clientStorageComponent = realm.addComponentModel(clientStorage);
|
||||
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
|
||||
Assert.assertNotNull(hardcodedClient);
|
||||
|
||||
UserConsentModel maryHardcodedGrant = new UserConsentModel(hardcodedClient);
|
||||
realmManager.getSession().users().addConsent(realm, mary.getId(), maryHardcodedGrant);
|
||||
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
|
@ -135,7 +156,15 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
|||
Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
|
||||
Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
|
||||
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
UserConsentModel maryHardcodedConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId());
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
|
||||
Assert.assertNotNull("Created Date should be set", maryHardcodedConsent.getCreatedDate());
|
||||
Assert.assertNotNull("Last Updated Date should be set", maryHardcodedConsent.getLastUpdatedDate());
|
||||
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary.getId(), barClient.getId()));
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), hardcodedClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -149,14 +178,26 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
|||
List<UserConsentModel> johnConsents = realmManager.getSession().users().getConsents(realm, john.getId());
|
||||
Assert.assertEquals(2, johnConsents.size());
|
||||
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
|
||||
List<UserConsentModel> maryConsents = realmManager.getSession().users().getConsents(realm, mary.getId());
|
||||
Assert.assertEquals(1, maryConsents.size());
|
||||
Assert.assertEquals(2, maryConsents.size());
|
||||
UserConsentModel maryConsent = maryConsents.get(0);
|
||||
UserConsentModel maryHardcodedConsent = maryConsents.get(1);
|
||||
if (maryConsents.get(0).getClient().getId().equals(hardcodedClient.getId())) {
|
||||
maryConsent = maryConsents.get(1);
|
||||
maryHardcodedConsent = maryConsents.get(0);
|
||||
|
||||
}
|
||||
Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
|
||||
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
|
||||
Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
|
||||
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
|
||||
Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
|
||||
|
||||
Assert.assertEquals(maryHardcodedConsent.getClient().getId(), hardcodedClient.getId());
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
|
||||
Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -200,14 +241,19 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
|||
RealmModel realm = realmManager.getRealm("original");
|
||||
ClientModel fooClient = realm.getClientByClientId("foo-client");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
|
||||
realmManager.getSession().users().revokeConsentForClient(realm, john.getId(), fooClient.getId());
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
realmManager.getSession().users().revokeConsentForClient(realm, mary.getId(), hardcodedClient.getId());
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
john = session.users().getUserByUsername("john", realm);
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId()));
|
||||
mary = session.users().getUserByUsername("mary", realm);
|
||||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -216,6 +262,8 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
|||
RealmModel realm = realmManager.getRealm("original");
|
||||
UserModel john = session.users().getUserByUsername("john", realm);
|
||||
session.users().removeUser(realm, john);
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
session.users().removeUser(realm, mary);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -280,6 +328,24 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
|
|||
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientStorageTest() {
|
||||
RealmModel realm = realmManager.getRealm("original");
|
||||
realm.removeComponent(clientStorageComponent);
|
||||
commit();
|
||||
|
||||
|
||||
|
||||
realm = realmManager.getRealm("original");
|
||||
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
|
||||
Assert.assertNull(hardcodedClient);
|
||||
|
||||
UserModel mary = session.users().getUserByUsername("mary", realm);
|
||||
|
||||
List<UserConsentModel> maryConsents = realmManager.getSession().users().getConsents(realm, mary.getId());
|
||||
Assert.assertEquals(1, maryConsents.size());
|
||||
}
|
||||
|
||||
private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, UserConsentModel consentModel) {
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
return consentModel.isRoleGranted(role);
|
||||
|
|
|
@ -173,6 +173,7 @@ realm-sessions=Realm Sessions
|
|||
revocation=Revocation
|
||||
logout-all=Logout all
|
||||
active-sessions=Active Sessions
|
||||
offline-sessions=Offline Sessions
|
||||
sessions=Sessions
|
||||
not-before=Not Before
|
||||
not-before.tooltip=Revoke any tokens issued before this date.
|
||||
|
@ -1343,6 +1344,8 @@ userStorage.cachePolicy.maxLifespan.tooltip=Max lifespan of a user cache entry i
|
|||
user-origin-link=Storage Origin
|
||||
user-origin.tooltip=UserStorageProvider the user was loaded from
|
||||
user-link.tooltip=UserStorageProvider this locally stored user was imported from.
|
||||
client-origin-link=Storage Origin
|
||||
client-origin.tooltip=Provider the client was loaded from
|
||||
|
||||
disable=Disable
|
||||
disableable-credential-types=Disableable Types
|
||||
|
|
|
@ -764,6 +764,15 @@ module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo,
|
|||
});
|
||||
};
|
||||
|
||||
$scope.searchClient = function() {
|
||||
console.log('searchQuery!!! ' + $scope.search.clientId);
|
||||
Client.query({realm: realm.realm, viewableOnly: true, clientId: $scope.search.clientId}).$promise.then(function(clients) {
|
||||
$scope.numberOfPages = Math.ceil(clients.length/$scope.pageSize);
|
||||
$scope.clients = clients;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.exportClient = function(client) {
|
||||
var clientCopy = angular.copy(client);
|
||||
delete clientCopy.id;
|
||||
|
@ -819,7 +828,7 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, serv
|
|||
}
|
||||
});
|
||||
|
||||
module.controller('ClientDetailCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
|
||||
module.controller('ClientDetailCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, Components, ClientStorageOperations, $location, $modal, Dialog, Notifications) {
|
||||
|
||||
|
||||
|
||||
|
@ -889,6 +898,25 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
|
|||
$scope.disableServiceAccountRolesTab = !client.serviceAccountsEnabled;
|
||||
$scope.disableCredentialsTab = client.publicClient;
|
||||
|
||||
if(client.origin) {
|
||||
if ($scope.access.viewRealm) {
|
||||
Components.get({realm: realm.realm, componentId: client.origin}, function (link) {
|
||||
$scope.originName = link.name;
|
||||
//$scope.originLink = "#/realms/" + realm.realm + "/user-storage/providers/" + link.providerId + "/" + link.id;
|
||||
})
|
||||
}
|
||||
else {
|
||||
// KEYCLOAK-4328
|
||||
ClientStorageOperations.simpleName.get({realm: realm.realm, componentId: client.origin}, function (link) {
|
||||
$scope.originName = link.name;
|
||||
//$scope.originLink = $location.absUrl();
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log("origin is null");
|
||||
}
|
||||
|
||||
|
||||
function updateProperties() {
|
||||
if (!$scope.client.attributes) {
|
||||
$scope.client.attributes = {};
|
||||
|
|
|
@ -1813,6 +1813,16 @@ module.factory('UserStorageOperations', function($resource) {
|
|||
});
|
||||
|
||||
|
||||
module.factory('ClientStorageOperations', function($resource) {
|
||||
var object = {}
|
||||
object.simpleName = $resource(authUrl + '/admin/realms/:realm/client-storage/:componentId/name', {
|
||||
realm : '@realm',
|
||||
componentId : '@componentId'
|
||||
});
|
||||
return object;
|
||||
});
|
||||
|
||||
|
||||
module.factory('ClientRegistrationPolicyProviders', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/client-registration-policy/providers', {
|
||||
realm : '@realm',
|
||||
|
|
|
@ -37,6 +37,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'client.enabled.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix block" data-ng-show="client.origin">
|
||||
<label class="col-md-2 control-label">{{:: 'client-origin-link' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
{{originName}}
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'client-origin.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix block" data-ng-show="protocol != 'docker-v2'">
|
||||
<label class="col-md-2 control-label" for="consentRequired">{{:: 'consent-required' | translate}}</label>
|
||||
<div class="col-sm-6">
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode === 13){$(this).next('I').click();}">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeydown="if(event.keyCode === 13) document.getElementById('clientSearch').click()">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search" type="submit"></i>
|
||||
<i class="fa fa-search" id="clientSearch" data-ng-click="searchClient()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
<tr>
|
||||
<th>{{:: 'client' | translate}}</th>
|
||||
<th>{{:: 'active-sessions' | translate}}</th>
|
||||
<th>{{:: 'offline-sessions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr data-ng-repeat="data in stats">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{data.id}}/sessions">{{data.clientId}}</a></td>
|
||||
<td>{{data.active}}</td>
|
||||
<td>{{data.offline}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
<ul class="nav nav-tabs" data-ng-hide="create && !path[4]">
|
||||
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'settings' | translate}}</a></li>
|
||||
<li ng-class="{active: path[4] == 'credentials'}"
|
||||
data-ng-show="!client.publicClient && client.protocol == 'openid-connect'">
|
||||
data-ng-show="!client.publicClient && client.protocol == 'openid-connect' && !client.origin">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">{{:: 'credentials' | translate}}</a>
|
||||
</li>
|
||||
<li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">{{:: 'saml-keys' | translate}}</a></li>
|
||||
<li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
|
||||
<li ng-class="{active: path[4] == 'roles'}" data-ng-show="!client.origin"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
|
||||
<li ng-class="{active: path[4] == 'mappers'}" data-ng-show="!client.bearerOnly">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">{{:: 'mappers' | translate}}</a>
|
||||
<kc-tooltip>{{:: 'mappers.tooltip' | translate}}</kc-tooltip>
|
||||
|
@ -23,10 +23,10 @@
|
|||
<kc-tooltip>{{:: 'scope.tooltip' | translate}}</kc-tooltip>
|
||||
</li>
|
||||
<li ng-class="{active: path[4] == 'authz'}"
|
||||
data-ng-show="serverInfo.featureEnabled('AUTHORIZATION') && !disableAuthorizationTab && client.authorizationServicesEnabled">
|
||||
data-ng-show="serverInfo.featureEnabled('AUTHORIZATION') && !disableAuthorizationTab && client.authorizationServicesEnabled && !client.origin">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server">{{:: 'authz-authorization' |
|
||||
translate}}</a></li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}" data-ng-show="client.protocol != 'docker-v2' && client.protocol != 'saml'"><a
|
||||
<li ng-class="{active: path[4] == 'revocation'}" data-ng-show="client.protocol != 'docker-v2' && client.protocol != 'saml' && !client.origin"><a
|
||||
href="#/realms/{{realm.realm}}/clients/{{client.id}}/revocation">{{:: 'revocation' | translate}}</a>
|
||||
</li>
|
||||
<!-- <li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/identity-provider">Identity Provider</a></li> -->
|
||||
|
@ -40,9 +40,9 @@
|
|||
<kc-tooltip>{{:: 'offline-access.tooltip' | translate}}</kc-tooltip>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'clustering' | translate}}</a></li>
|
||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient && !client.origin"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'clustering' | translate}}</a></li>
|
||||
|
||||
<li ng-class="{active: path[4] == 'installation'}">
|
||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="!client.origin">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">{{:: 'installation' | translate}}</a>
|
||||
<kc-tooltip>{{:: 'installation.tooltip' | translate}}</kc-tooltip>
|
||||
</li>
|
||||
|
|
Loading…
Reference in a new issue