initial work

This commit is contained in:
Bill Burke 2018-01-25 10:08:26 -05:00
parent 64b75d2806
commit 8a17b61f4e
21 changed files with 914 additions and 218 deletions

View file

@ -54,7 +54,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,7 +66,7 @@ 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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -94,7 +94,8 @@ 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;
@ -134,16 +135,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) {
@ -319,7 +329,7 @@ public class RealmCacheSession implements CacheRealmProvider {
@Override
public void commit() {
try {
if (delegate == null) return;
if (realmDelegate == null) return;
if (clearAll) {
cache.clear();
}
@ -360,14 +370,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 +391,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 +430,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 +454,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>();
@ -463,19 +473,19 @@ public class RealmCacheSession implements CacheRealmProvider {
cache.invalidateObject(id);
invalidationEvents.add(RealmRemovedEvent.create(id, realm.getName()));
cache.realmRemoval(id, realm.getName(), invalidations);
return getDelegate().removeRealm(id);
return getRealmDelegate().removeRealm(id);
}
@Override
public ClientModel addClient(RealmModel realm, String clientId) {
ClientModel client = getDelegate().addClient(realm, clientId);
ClientModel client = getClientDelegate().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 = getClientDelegate().addClient(realm, id, clientId);
return addedClient(realm, client);
}
@ -515,7 +525,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 +535,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 +550,7 @@ public class RealmCacheSession implements CacheRealmProvider {
if (client == null) {
// TODO: Handle with cluster invalidations too
invalidations.add(cacheKey);
return getDelegate().getClients(realm);
return getClientDelegate().getClients(realm);
}
list.add(client);
}
@ -563,13 +573,14 @@ public class RealmCacheSession implements CacheRealmProvider {
for (RoleModel role : client.getRoles()) {
roleRemovalInvalidations(role.getId(), role.getName(), client.getId());
}
return getDelegate().removeClient(id, realm);
return getClientDelegate().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 +590,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 +600,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 +610,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 +624,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 +636,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 getClientDelegate().getClientRoles(realm, client);
}
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
@ -635,7 +646,7 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
Set<RoleModel> model = getDelegate().getClientRoles(realm, client);
Set<RoleModel> model = getClientDelegate().getClientRoles(realm, client);
if (model == null) return null;
Set<String> ids = new HashSet<>();
for (RoleModel role : model) ids.add(role.getId());
@ -649,7 +660,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 getClientDelegate().getClientRoles(realm, client);
}
list.add(role);
}
@ -663,7 +674,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 = getClientDelegate().addClientRole(realm, client, id, name);
addedRole(role.getId(), client.getId());
return role;
}
@ -673,7 +684,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 +694,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 +704,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 +714,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 getClientDelegate().getClientRole(realm, client, name);
}
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
@ -713,7 +724,7 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
RoleModel model = getDelegate().getClientRole(realm, client, name);
RoleModel model = getClientDelegate().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 +734,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 getClientDelegate().getClientRole(realm, client, name);
}
return role;
}
@ -736,7 +747,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 +759,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 +770,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 +788,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 +811,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 +819,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 +829,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 +843,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 +855,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 +868,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 +878,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 +892,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 +907,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 +917,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 +931,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 +943,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 +957,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 +976,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 +989,7 @@ public class RealmCacheSession implements CacheRealmProvider {
addGroupEventIfAbsent(GroupMovedEvent.create(subGroup, null, realm.getId()));
getDelegate().addTopLevelGroup(realm, subGroup);
getRealmDelegate().addTopLevelGroup(realm, subGroup);
}
@ -1007,14 +1018,14 @@ 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);
} else if (invalidations.contains(id)) {
return getDelegate().getClientById(id, realm);
return getClientDelegate().getClientById(id, realm);
} else if (managedApplications.containsKey(id)) {
return managedApplications.get(id);
}
@ -1035,7 +1046,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 +1054,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 +1077,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 +1095,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);
}
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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 {
ClientModel addClient(RealmModel realm, String clientId);
ClientModel addClient(RealmModel realm, String id, String clientId);
List<ClientModel> getClients(RealmModel realm);
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);
}

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -0,0 +1,147 @@
/*
* 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.component.ComponentModel;
import org.keycloak.component.PrioritizedComponentModel;
/**
* @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";
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 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 static enum CachePolicy {
NO_CACHE,
DEFAULT,
EVICT_DAILY,
EVICT_WEEKLY,
MAX_LIFESPAN
}
}

View file

@ -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;

View file

@ -26,28 +26,14 @@ 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());
}
@ -61,104 +47,6 @@ public class UserStorageProviderModel extends PrioritizedComponentModel {
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) {

View file

@ -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);
}

View file

@ -0,0 +1,69 @@
/*
* 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
*
* @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) {
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 false;
}
@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;
}
}

View file

@ -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) {

View file

@ -0,0 +1,210 @@
/*
* 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.models.UserModel;
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 org.keycloak.storage.user.UserLookupProvider;
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 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 session.clientLocalStorage().getClientRoles(realm, client);
}
@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);
}
}