/*
* 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.mongo.keycloak.adapters;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoClientTemplateEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoRealmProvider implements RealmProvider {
private final MongoStoreInvocationContext invocationContext;
private final KeycloakSession session;
public MongoRealmProvider(KeycloakSession session, MongoStoreInvocationContext invocationContext) {
this.session = session;
this.invocationContext = invocationContext;
}
@Override
public void close() {
// TODO
}
@Override
public MigrationModel getMigrationModel() {
MongoMigrationModelEntity entity = getMongoStore().loadEntity(MongoMigrationModelEntity.class, MongoMigrationModelEntity.MIGRATION_MODEL_ID, invocationContext);
if (entity == null) {
entity = new MongoMigrationModelEntity();
getMongoStore().insertEntity(entity, invocationContext);
}
return new MigrationModelAdapter(session, entity, invocationContext);
}
@Override
public RealmModel createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
}
@Override
public RealmModel createRealm(String id, String name) {
MongoRealmEntity newRealm = new MongoRealmEntity();
newRealm.setId(id);
newRealm.setName(name);
getMongoStore().insertEntity(newRealm, invocationContext);
final RealmModel model = new RealmAdapter(session, newRealm, invocationContext);
session.getKeycloakSessionFactory().publish(new RealmModel.RealmCreationEvent() {
@Override
public RealmModel getCreatedRealm() {
return model;
}
});
return model;
}
@Override
public RealmModel getRealm(String id) {
MongoRealmEntity realmEntity = getMongoStore().loadEntity(MongoRealmEntity.class, id, invocationContext);
return realmEntity != null ? new RealmAdapter(session, realmEntity, invocationContext) : null;
}
@Override
public List<RealmModel> getRealms() {
DBObject query = new BasicDBObject();
List<MongoRealmEntity> realms = getMongoStore().loadEntities(MongoRealmEntity.class, query, invocationContext);
List<RealmModel> results = new ArrayList<RealmModel>();
for (MongoRealmEntity realmEntity : realms) {
RealmModel realm = session.realms().getRealm(realmEntity.getId());
if (realm != null) results.add(realm);
}
return results;
}
@Override
public RealmModel getRealmByName(String name) {
DBObject query = new QueryBuilder()
.and("name").is(name)
.get();
MongoRealmEntity realm = getMongoStore().loadSingleEntity(MongoRealmEntity.class, query, invocationContext);
if (realm == null) return null;
return session.realms().getRealm(realm.getId());
}
@Override
public boolean removeRealm(String id) {
final RealmModel realm = getRealm(id);
if (realm == null) return false;
session.users().preRemove(realm);
boolean removed = getMongoStore().removeEntity(MongoRealmEntity.class, id, invocationContext);
if (removed) {
session.getKeycloakSessionFactory().publish(new RealmModel.RealmRemovedEvent() {
@Override
public RealmModel getRealm() {
return realm;
}
@Override
public KeycloakSession getKeycloakSession() {
return session;
}
});
}
return removed;
}
protected MongoStore getMongoStore() {
return invocationContext.getMongoStore();
}
@Override
public RoleModel getRoleById(String id, RealmModel realm) {
MongoRoleEntity role = getMongoStore().loadEntity(MongoRoleEntity.class, id, invocationContext);
if (role == null) return null;
if (role.getRealmId() != null && !role.getRealmId().equals(realm.getId())) return null;
if (role.getClientId() != null && realm.getClientById(role.getClientId()) == null) return null;
return new RoleAdapter(session, realm, role, null, invocationContext);
}
@Override
public GroupModel getGroupById(String id, RealmModel realm) {
MongoGroupEntity group = getMongoStore().loadEntity(MongoGroupEntity.class, id, invocationContext);
if (group == null) return null;
if (group.getRealmId() != null && !group.getRealmId().equals(realm.getId())) return null;
return new GroupAdapter(session, realm, group, invocationContext);
}
@Override
public void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent) {
if (toParent != null && group.getId().equals(toParent.getId())) {
return;
}
if (group.getParentId() != null) {
group.getParent().removeChild(group);
}
group.setParent(toParent);
if (toParent != null) toParent.addChild(group);
else session.realms().addTopLevelGroup(realm, group);
}
@Override
public List<GroupModel> getGroups(RealmModel realm) {
DBObject query = new QueryBuilder()
.and("realmId").is(realm.getId())
.get();
List<MongoGroupEntity> groups = getMongoStore().loadEntities(MongoGroupEntity.class, query, invocationContext);
if (groups == null) return Collections.EMPTY_LIST;
List<GroupModel> result = new LinkedList<>();
if (groups == null) return result;
for (MongoGroupEntity group : groups) {
result.add(getGroupById(group.getId(), realm));
}
return Collections.unmodifiableList(result);
}
@Override
public List<GroupModel> getTopLevelGroups(RealmModel realm) {
DBObject query = new QueryBuilder()
.and("realmId").is(realm.getId())
.and("parentId").is(null)
.get();
List<MongoGroupEntity> groups = getMongoStore().loadEntities(MongoGroupEntity.class, query, invocationContext);
if (groups == null) return Collections.EMPTY_LIST;
List<GroupModel> result = new LinkedList<>();
if (groups == null) return result;
for (MongoGroupEntity group : groups) {
result.add(getGroupById(group.getId(), realm));
}
return Collections.unmodifiableList(result);
}
@Override
public boolean removeGroup(RealmModel realm, GroupModel group) {
session.users().preRemove(realm, group);
realm.removeDefaultGroup(group);
for (GroupModel subGroup : group.getSubGroups()) {
removeGroup(realm, subGroup);
}
moveGroup(realm, group, null);
return getMongoStore().removeEntity(MongoGroupEntity.class, group.getId(), invocationContext);
}
@Override
public GroupModel createGroup(RealmModel realm, String name) {
String id = KeycloakModelUtils.generateId();
return createGroup(realm, id, name);
}
@Override
public GroupModel createGroup(RealmModel realm, String id, String name) {
if (id == null) id = KeycloakModelUtils.generateId();
MongoGroupEntity group = new MongoGroupEntity();
group.setId(id);
group.setName(name);
group.setRealmId(realm.getId());
getMongoStore().insertEntity(group, invocationContext);
return new GroupAdapter(session, realm, group, invocationContext);
}
@Override
public void addTopLevelGroup(RealmModel realm, GroupModel subGroup) {
subGroup.setParent(null);
}
@Override
public ClientModel getClientById(String id, RealmModel realm) {
MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, id, invocationContext);
// Check if application belongs to this realm
if (appData == null || !realm.getId().equals(appData.getRealmId())) {
return null;
}
return new ClientAdapter(session, realm, appData, invocationContext);
}
@Override
public ClientModel addClient(RealmModel realm, String clientId) {
return addClient(realm, KeycloakModelUtils.generateId(), clientId);
}
@Override
public ClientModel addClient(RealmModel realm, String id, String clientId) {
MongoClientEntity clientEntity = new MongoClientEntity();
clientEntity.setId(id);
clientEntity.setClientId(clientId);
clientEntity.setRealmId(realm.getId());
clientEntity.setEnabled(true);
clientEntity.setStandardFlowEnabled(true);
getMongoStore().insertEntity(clientEntity, invocationContext);
if (clientId == null) {
clientEntity.setClientId(clientEntity.getId());
getMongoStore().updateEntity(clientEntity, invocationContext);
}
final ClientModel model = new ClientAdapter(session, realm, clientEntity, invocationContext);
session.getKeycloakSessionFactory().publish(new RealmModel.ClientCreationEvent() {
@Override
public ClientModel getCreatedClient() {
return model;
}
});
return model;
}
@Override
public List<ClientModel> getClients(RealmModel realm) {
DBObject query = new QueryBuilder()
.and("realmId").is(realm.getId())
.get();
List<MongoClientEntity> clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
if (clientEntities.isEmpty()) return Collections.EMPTY_LIST;
List<ClientModel> result = new ArrayList<ClientModel>();
for (MongoClientEntity clientEntity : clientEntities) {
result.add(session.realms().getClientById(clientEntity.getId(), realm));
}
return Collections.unmodifiableList(result);
}
@Override
public RoleModel addRealmRole(RealmModel realm, String name) {
return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
}
@Override
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
MongoRoleEntity roleEntity = new MongoRoleEntity();
roleEntity.setId(id);
roleEntity.setName(name);
roleEntity.setRealmId(realm.getId());
getMongoStore().insertEntity(roleEntity, invocationContext);
return new RoleAdapter(session, realm, roleEntity, realm, invocationContext);
}
@Override
public Set<RoleModel> getRealmRoles(RealmModel realm) {
DBObject query = new QueryBuilder()
.and("realmId").is(realm.getId())
.get();
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
if (roles == null) return Collections.EMPTY_SET;
Set<RoleModel> result = new HashSet<RoleModel>();
for (MongoRoleEntity role : roles) {
result.add(session.realms().getRoleById(role.getId(), realm));
}
return Collections.unmodifiableSet(result);
}
@Override
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
DBObject query = new QueryBuilder()
.and("clientId").is(client.getId())
.get();
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
Set<RoleModel> result = new HashSet<RoleModel>();
for (MongoRoleEntity role : roles) {
result.add(session.realms().getRoleById(role.getId(), realm));
}
return result;
}
@Override
public RoleModel getRealmRole(RealmModel realm, String name) {
DBObject query = new QueryBuilder()
.and("name").is(name)
.and("realmId").is(realm.getId())
.get();
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
if (role == null) {
return null;
} else {
return session.realms().getRoleById(role.getId(), realm);
}
}
@Override
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
DBObject query = new QueryBuilder()
.and("name").is(name)
.and("clientId").is(client.getId())
.get();
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
if (role == null) {
return null;
} else {
return session.realms().getRoleById(role.getId(), realm);
}
}
@Override
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
}
@Override
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
MongoRoleEntity roleEntity = new MongoRoleEntity();
roleEntity.setId(id);
roleEntity.setName(name);
roleEntity.setClientId(client.getId());
getMongoStore().insertEntity(roleEntity, invocationContext);
return new RoleAdapter(session, realm, roleEntity, client, invocationContext);
}
@Override
public boolean removeRole(RealmModel realm, RoleModel role) {
session.users().preRemove(realm, role);
RoleContainerModel container = role.getContainer();
if (container.getDefaultRoles().contains(role.getName())) {
container.removeDefaultRoles(role.getName());
}
return getMongoStore().removeEntity(MongoRoleEntity.class, role.getId(), invocationContext);
}
@Override
public boolean removeClient(String id, RealmModel realm) {
if (id == null) return false;
final ClientModel client = getClientById(id, realm);
if (client == null) return false;
session.users().preRemove(realm, client);
boolean removed = getMongoStore().removeEntity(MongoClientEntity.class, id, invocationContext);
if (removed) {
session.getKeycloakSessionFactory().publish(new RealmModel.ClientRemovedEvent() {
@Override
public ClientModel getClient() {
return client;
}
@Override
public KeycloakSession getKeycloakSession() {
return session;
}
});
}
return removed;
}
@Override
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
DBObject query = new QueryBuilder()
.and("realmId").is(realm.getId())
.and("clientId").is(clientId)
.get();
MongoClientEntity appEntity = getMongoStore().loadSingleEntity(MongoClientEntity.class, query, invocationContext);
if (appEntity == null) return null;
return session.realms().getClientById(appEntity.getId(), realm);
}
@Override
public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
MongoClientTemplateEntity appData = getMongoStore().loadEntity(MongoClientTemplateEntity.class, id, invocationContext);
// Check if application belongs to this realm
if (appData == null || !realm.getId().equals(appData.getRealmId())) {
return null;
}
return new ClientTemplateAdapter(session, realm, appData, invocationContext);
}
}