mongo fed

This commit is contained in:
Bill Burke 2016-09-30 16:48:59 -04:00
parent 8967ca4066
commit 110f6ad549
14 changed files with 779 additions and 741 deletions

View file

@ -1,218 +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.storage.jpa;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaFederatedUserCredentialStore implements UserCredentialStore {
private final KeycloakSession session;
protected final EntityManager em;
public JpaFederatedUserCredentialStore(KeycloakSession session, EntityManager em) {
this.session = session;
this.em = em;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, cred.getId());
if (entity == null) return;
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (entity.getCredentialAttributes().isEmpty() && (cred.getConfig() == null || cred.getConfig().isEmpty())) {
} else {
MultivaluedHashMap<String, String> attrs = cred.getConfig();
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config == null) config = new MultivaluedHashMap<>();
Iterator<FederatedUserCredentialAttributeEntity> it = entity.getCredentialAttributes().iterator();
while (it.hasNext()) {
FederatedUserCredentialAttributeEntity attr = it.next();
List<String> values = config.getList(attr.getName());
if (values == null || !values.contains(attr.getValue())) {
em.remove(attr);
it.remove();
} else {
attrs.add(attr.getName(), attr.getValue());
}
}
for (String key : config.keySet()) {
List<String> values = config.getList(key);
List<String> attrValues = attrs.getList(key);
for (String val : values) {
if (attrValues == null || !attrValues.contains(val)) {
FederatedUserCredentialAttributeEntity attr = new FederatedUserCredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
}
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = new FederatedUserCredentialEntity();
String id = cred.getId() == null ? KeycloakModelUtils.generateId() : cred.getId();
entity.setId(id);
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
entity.setUserId(user.getId());
entity.setRealmId(realm.getId());
entity.setStorageProviderId(StorageId.resolveProviderId(user));
em.persist(entity);
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config != null || !config.isEmpty()) {
for (String key : config.keySet()) {
List<String> values = config.getList(key);
for (String val : values) {
FederatedUserCredentialAttributeEntity attr = new FederatedUserCredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
return toModel(entity);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, id);
if (entity == null) return false;
em.remove(entity);
return true;
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, id);
if (entity == null) return null;
CredentialModel model = toModel(entity);
return model;
}
protected CredentialModel toModel(FederatedUserCredentialEntity entity) {
CredentialModel model = new CredentialModel();
model.setId(entity.getId());
model.setType(entity.getType());
model.setValue(entity.getValue());
model.setAlgorithm(entity.getAlgorithm());
model.setSalt(entity.getSalt());
model.setPeriod(entity.getPeriod());
model.setCounter(entity.getCounter());
model.setCreatedDate(entity.getCreatedDate());
model.setDevice(entity.getDevice());
model.setDigits(entity.getDigits());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
model.setConfig(config);
for (FederatedUserCredentialAttributeEntity attr : entity.getCredentialAttributes()) {
config.add(attr.getName(), attr.getValue());
}
return model;
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUser", FederatedUserCredentialEntity.class)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (FederatedUserCredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUserAndType", FederatedUserCredentialEntity.class)
.setParameter("type", type)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (FederatedUserCredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByNameAndType", FederatedUserCredentialEntity.class)
.setParameter("type", type)
.setParameter("device", name)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
if (results.isEmpty()) return null;
return toModel(results.get(0));
}
@Override
public void close() {
}
}

View file

@ -30,18 +30,14 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FederatedCredentials;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.federated.UserAttributeFederatedStorage;
import org.keycloak.storage.federated.UserBrokerLinkFederatedStorage;
import org.keycloak.storage.federated.UserConsentFederatedStorage;
import org.keycloak.storage.federated.UserCredentialsFederatedStorage;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.storage.federated.UserGroupMembershipFederatedStorage;
import org.keycloak.storage.federated.UserRequiredActionsFederatedStorage;
@ -77,7 +73,6 @@ public class JpaUserFederatedStorageProvider implements
UserAttributeFederatedStorage,
UserBrokerLinkFederatedStorage,
UserConsentFederatedStorage,
UserCredentialsFederatedStorage,
UserGroupMembershipFederatedStorage,
UserRequiredActionsFederatedStorage,
UserRoleMappingsFederatedStorage,
@ -427,76 +422,6 @@ public class JpaUserFederatedStorageProvider implements
@Override
public List<UserCredentialValueModel> getCredentials(RealmModel realm, UserModel user) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUser", FederatedUserCredentialEntity.class)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<UserCredentialValueModel> list = new LinkedList<>();
for (FederatedUserCredentialEntity credEntity : results) {
UserCredentialValueModel credModel = new UserCredentialValueModel();
credModel.setId(credEntity.getId());
credModel.setType(credEntity.getType());
credModel.setDevice(credEntity.getDevice());
credModel.setValue(credEntity.getValue());
credModel.setCreatedDate(credEntity.getCreatedDate());
credModel.setSalt(credEntity.getSalt());
credModel.setHashIterations(credEntity.getHashIterations());
credModel.setCounter(credEntity.getCounter());
credModel.setAlgorithm(credEntity.getAlgorithm());
credModel.setDigits(credEntity.getDigits());
credModel.setPeriod(credEntity.getPeriod());
list.add(credModel);
}
return list;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel cred) {
createIndex(realm, user);
FederatedCredentials.updateCredential(session, this, realm, user, cred);
}
@Override
public void updateCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred) {
createIndex(realm, user);
FederatedUserCredentialEntity entity = null;
if (cred.getId() != null) entity = em.find(FederatedUserCredentialEntity.class, cred.getId());
boolean newEntity = false;
if (entity == null) {
entity = new FederatedUserCredentialEntity();
entity.setId(KeycloakModelUtils.generateId());
newEntity = true;
}
entity.setUserId(user.getId());
entity.setRealmId(realm.getId());
entity.setStorageProviderId(StorageId.resolveProviderId(user));
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
Long createdDate = cred.getCreatedDate();
if (createdDate == null) createdDate = System.currentTimeMillis();
entity.setCreatedDate(createdDate);
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (newEntity) {
em.persist(entity);
}
}
@Override
public void removeCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, cred.getId());
em.remove(entity);
}
@Override
public Set<GroupModel> getGroups(RealmModel realm, UserModel user) {
Set<GroupModel> set = new HashSet<>();
@ -638,36 +563,6 @@ public class JpaUserFederatedStorageProvider implements
em.flush();
}
@Override
public boolean removeCredential(RealmModel realm, String id) {
return false;
}
@Override
public CredentialModel getCredentialById(String id) {
return null;
}
@Override
public List<CredentialModel> getCredentials(RealmModel realm) {
return null;
}
@Override
public List<CredentialModel> getUserCredentials(RealmModel realm, UserModel user) {
return null;
}
@Override
public List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type) {
return null;
}
@Override
public CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
return null;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, cred.getId());
@ -686,7 +581,7 @@ public class JpaUserFederatedStorageProvider implements
if (entity.getCredentialAttributes().isEmpty() && (cred.getConfig() == null || cred.getConfig().isEmpty())) {
} else {
MultivaluedHashMap<String, String> attrs = cred.getConfig();
MultivaluedHashMap<String, String> attrs = new MultivaluedHashMap<>();
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config == null) config = new MultivaluedHashMap<>();

View file

@ -65,22 +65,23 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoOnlineUserSessionEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoOfflineUserSessionEntity",
"org.keycloak.models.entities.IdentityProviderEntity",
"org.keycloak.models.entities.ClientIdentityProviderMappingEntity",
"org.keycloak.models.entities.RequiredCredentialEntity",
"org.keycloak.models.entities.CredentialEntity",
"org.keycloak.models.entities.FederatedIdentityEntity",
"org.keycloak.models.entities.UserFederationProviderEntity",
"org.keycloak.models.entities.UserFederationMapperEntity",
"org.keycloak.models.entities.ProtocolMapperEntity",
"org.keycloak.models.entities.IdentityProviderMapperEntity",
"org.keycloak.models.entities.AuthenticationExecutionEntity",
"org.keycloak.models.entities.AuthenticationFlowEntity",
"org.keycloak.models.entities.AuthenticatorConfigEntity",
"org.keycloak.models.entities.RequiredActionProviderEntity",
"org.keycloak.models.entities.PersistentUserSessionEntity",
"org.keycloak.models.entities.PersistentClientSessionEntity",
"org.keycloak.models.entities.ComponentEntity",
"org.keycloak.models.mongo.keycloak.entities.IdentityProviderEntity",
"org.keycloak.models.mongo.keycloak.entities.ClientIdentityProviderMappingEntity",
"org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity",
"org.keycloak.models.mongo.keycloak.entities.CredentialEntity",
"org.keycloak.models.mongo.keycloak.entities.FederatedIdentityEntity",
"org.keycloak.models.mongo.keycloak.entities.UserFederationProviderEntity",
"org.keycloak.models.mongo.keycloak.entities.UserFederationMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.ProtocolMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.IdentityProviderMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.AuthenticationExecutionEntity",
"org.keycloak.models.mongo.keycloak.entities.AuthenticationFlowEntity",
"org.keycloak.models.mongo.keycloak.entities.AuthenticatorConfigEntity",
"org.keycloak.models.mongo.keycloak.entities.RequiredActionProviderEntity",
"org.keycloak.models.mongo.keycloak.entities.PersistentUserSessionEntity",
"org.keycloak.models.mongo.keycloak.entities.PersistentClientSessionEntity",
"org.keycloak.models.mongo.keycloak.entities.ComponentEntity",
"org.keycloak.storage.mongo.entity.FederatedUser",
"org.keycloak.authorization.mongo.entities.PolicyEntity",
"org.keycloak.authorization.mongo.entities.ResourceEntity",
"org.keycloak.authorization.mongo.entities.ResourceServerEntity",

View file

@ -0,0 +1,571 @@
/*
* 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.mongo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
import org.keycloak.models.mongo.keycloak.entities.FederatedIdentityEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.federated.UserAttributeFederatedStorage;
import org.keycloak.storage.federated.UserBrokerLinkFederatedStorage;
import org.keycloak.storage.federated.UserConsentFederatedStorage;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.storage.federated.UserGroupMembershipFederatedStorage;
import org.keycloak.storage.federated.UserRequiredActionsFederatedStorage;
import org.keycloak.storage.federated.UserRoleMappingsFederatedStorage;
import org.keycloak.storage.mongo.entity.FederatedUser;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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 MongoUserFederatedStorageProvider implements
UserFederatedStorageProvider,
UserAttributeFederatedStorage,
UserBrokerLinkFederatedStorage,
UserConsentFederatedStorage,
UserGroupMembershipFederatedStorage,
UserRequiredActionsFederatedStorage,
UserRoleMappingsFederatedStorage,
UserCredentialStore {
private final MongoStoreInvocationContext invocationContext;
private final KeycloakSession session;
public MongoUserFederatedStorageProvider(KeycloakSession session, MongoStoreInvocationContext invocationContext) {
this.session = session;
this.invocationContext = invocationContext;
}
protected MongoStore getMongoStore() {
return invocationContext.getMongoStore();
}
protected FederatedUser addUserEntity(RealmModel realm, String id) {
FederatedUser userEntity = new FederatedUser();
userEntity.setId(id);
userEntity.setStorageId(StorageId.providerId(id));
userEntity.setRealmId(realm.getId());
getMongoStore().insertEntity(userEntity, invocationContext);
return userEntity;
}
protected FederatedUser getUserById(String id) {
return getMongoStore().loadEntity(FederatedUser.class, id, invocationContext);
}
protected FederatedUser findOrCreate(RealmModel realm, String id) {
FederatedUser user = getUserById(id);
if (user != null) return user;
return addUserEntity(realm, id);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null) return false;
CredentialEntity ce = getCredentialEntity(id, userEntity);
if (ce != null) return getMongoStore().pullItemFromList(userEntity, "credentials", ce, invocationContext);
return false;
}
private CredentialEntity getCredentialEntity(String id, FederatedUser userEntity) {
CredentialEntity ce = null;
if (userEntity.getCredentials() != null) {
for (CredentialEntity credentialEntity : userEntity.getCredentials()) {
if (credentialEntity.getId().equals(id)) {
ce = credentialEntity;
break;
}
}
}
return ce;
}
protected CredentialModel toModel(CredentialEntity entity) {
CredentialModel model = new CredentialModel();
model.setId(entity.getId());
model.setHashIterations(entity.getHashIterations());
model.setType(entity.getType());
model.setValue(entity.getValue());
model.setAlgorithm(entity.getAlgorithm());
model.setSalt(entity.getSalt());
model.setPeriod(entity.getPeriod());
model.setCounter(entity.getCounter());
model.setCreatedDate(entity.getCreatedDate());
model.setDevice(entity.getDevice());
model.setDigits(entity.getDigits());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
model.setConfig(config);
if (entity.getConfig() != null) {
config.putAll(entity.getConfig());
}
return model;
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
FederatedUser userEntity = getUserById(id);
if (userEntity != null && userEntity.getCredentials() != null) {
for (CredentialEntity credentialEntity : userEntity.getCredentials()) {
if (credentialEntity.getId().equals(id)) {
return toModel(credentialEntity);
}
}
}
return null;
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity != null && userEntity.getCredentials() != null) {
List<CredentialModel> list = new LinkedList<>();
for (CredentialEntity credentialEntity : userEntity.getCredentials()) {
list.add(toModel(credentialEntity));
}
return list;
}
return Collections.EMPTY_LIST;
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity != null && userEntity.getCredentials() != null) {
List<CredentialModel> list = new LinkedList<>();
for (CredentialEntity credentialEntity : userEntity.getCredentials()) {
if (type.equals(credentialEntity.getType())) list.add(toModel(credentialEntity));
}
return list;
}
return Collections.EMPTY_LIST;
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity != null && userEntity.getCredentials() != null) {
for (CredentialEntity credentialEntity : userEntity.getCredentials()) {
if (credentialEntity.getDevice().equals(name) && type.equals(credentialEntity.getType())) {
return toModel(credentialEntity);
}
}
}
return null;
}
@Override
public List<String> getStoredUsers(RealmModel realm, int first, int max) {
QueryBuilder queryBuilder = new QueryBuilder()
.and("realmId").is(realm.getId());
DBObject query = queryBuilder.get();
List<FederatedUser> users = getMongoStore().loadEntities(FederatedUser.class, query, null, first, max, invocationContext);
List<String> ids = new LinkedList<>();
for (FederatedUser user : users) ids.add(user.getId());
return ids;
}
@Override
public void preRemove(RealmModel realm) {
}
@Override
public void preRemove(RealmModel realm, UserFederationProviderModel link) {
}
@Override
public void preRemove(RealmModel realm, GroupModel group) {
}
@Override
public void preRemove(RealmModel realm, RoleModel role) {
}
@Override
public void preRemove(RealmModel realm, ClientModel client) {
}
@Override
public void preRemove(ProtocolMapperModel protocolMapper) {
}
@Override
public void preRemove(RealmModel realm, UserModel user) {
}
@Override
public void preRemove(RealmModel realm, ComponentModel model) {
}
@Override
public void close() {
}
@Override
public void setSingleAttribute(RealmModel realm, UserModel user, String name, String value) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
if (userEntity.getAttributes() == null) {
userEntity.setAttributes(new HashMap<>());
}
List<String> attrValues = new LinkedList<>();
attrValues.add(value);
userEntity.getAttributes().put(name, attrValues);
getMongoStore().updateEntity(userEntity, invocationContext);
}
@Override
public void setAttribute(RealmModel realm, UserModel user, String name, List<String> values) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
if (userEntity.getAttributes() == null) {
userEntity.setAttributes(new HashMap<>());
}
userEntity.getAttributes().put(name, values);
getMongoStore().updateEntity(userEntity, invocationContext);
}
@Override
public void removeAttribute(RealmModel realm, UserModel user, String name) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getAttributes() == null) return;
userEntity.getAttributes().remove(name);
getMongoStore().updateEntity(userEntity, invocationContext);
}
@Override
public MultivaluedHashMap<String, String> getAttributes(RealmModel realm, UserModel user) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getAttributes() == null) return new MultivaluedHashMap<>();
MultivaluedHashMap<String, String> result = new MultivaluedHashMap<>();
result.putAll(userEntity.getAttributes());
return result;
}
@Override
public List<String> getUsersByUserAttribute(RealmModel realm, String name, String value) {
QueryBuilder queryBuilder = new QueryBuilder()
.and("realmId").is(realm.getId());
queryBuilder.and("attributes." + name).is(value);
List<FederatedUser> users = getMongoStore().loadEntities(FederatedUser.class, queryBuilder.get(), invocationContext);
List<String> ids = new LinkedList<>();
for (FederatedUser user : users) ids.add(user.getId());
return ids;
}
@Override
public String getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
DBObject query = new QueryBuilder()
.and("federatedIdentities.identityProvider").is(socialLink.getIdentityProvider())
.and("federatedIdentities.userId").is(socialLink.getUserId())
.and("realmId").is(realm.getId())
.get();
FederatedUser userEntity = getMongoStore().loadSingleEntity(FederatedUser.class, query, invocationContext);
return userEntity != null ? userEntity.getId() : null;
}
@Override
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity();
federatedIdentityEntity.setIdentityProvider(socialLink.getIdentityProvider());
federatedIdentityEntity.setUserId(socialLink.getUserId());
federatedIdentityEntity.setUserName(socialLink.getUserName().toLowerCase());
federatedIdentityEntity.setToken(socialLink.getToken());
getMongoStore().pushItemToList(userEntity, "federatedIdentities", federatedIdentityEntity, true, invocationContext);
}
@Override
public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null) return false;
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, socialProvider);
if (federatedIdentityEntity == null) {
return false;
}
return getMongoStore().pullItemFromList(userEntity, "federatedIdentities", federatedIdentityEntity, invocationContext); }
private FederatedIdentityEntity findFederatedIdentityLink(FederatedUser userEntity, String identityProvider) {
List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
if (linkEntities == null) {
return null;
}
for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
if (federatedIdentityEntity.getIdentityProvider().equals(identityProvider)) {
return federatedIdentityEntity;
}
}
return null;
}
@Override
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
FederatedUser userEntity = getUserById(federatedUser.getId());
if (userEntity == null) return;
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, federatedIdentityModel.getIdentityProvider());
if (federatedIdentityEntity == null) return;
//pushItemToList updates the whole federatedIdentities array in Mongo so we just need to remove this object from the Java
//List and pushItemToList will handle the DB update.
userEntity.getFederatedIdentities().remove(federatedIdentityEntity);
federatedIdentityEntity.setToken(federatedIdentityModel.getToken());
getMongoStore().pushItemToList(userEntity, "federatedIdentities", federatedIdentityEntity, true, invocationContext);
}
@Override
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null) return Collections.EMPTY_SET;
List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
if (linkEntities == null) {
return Collections.EMPTY_SET;
}
Set<FederatedIdentityModel> result = new HashSet<FederatedIdentityModel>();
for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
FederatedIdentityModel model = new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(),
federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName(), federatedIdentityEntity.getToken());
result.add(model);
}
return result;
}
@Override
public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null) return null;
FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, socialProvider);
return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(),
federatedIdentityEntity.getUserName(), federatedIdentityEntity.getToken()) : null;
}
@Override
public void addConsent(RealmModel realm, UserModel user, UserConsentModel consent) {
session.userLocalStorage().addConsent(realm, user, consent);
}
@Override
public UserConsentModel getConsentByClient(RealmModel realm, UserModel user, String clientInternalId) {
return session.userLocalStorage().getConsentByClient(realm, user, clientInternalId);
}
@Override
public List<UserConsentModel> getConsents(RealmModel realm, UserModel user) {
return session.userLocalStorage().getConsents(realm, user);
}
@Override
public void updateConsent(RealmModel realm, UserModel user, UserConsentModel consent) {
session.userLocalStorage().updateConsent(realm, user, consent);
}
@Override
public boolean revokeConsentForClient(RealmModel realm, UserModel user, String clientInternalId) {
return session.userLocalStorage().revokeConsentForClient(realm, user, clientInternalId);
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null) return;
CredentialEntity entity = getCredentialEntity(cred.getId(), userEntity);
if (entity == null) return;
toEntity(cred, entity);
userEntity.getCredentials().remove(entity);
getMongoStore().pushItemToList(userEntity, "credentials", entity, true, invocationContext);
}
private void toEntity(CredentialModel cred, CredentialEntity entity) {
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (cred.getConfig() == null) entity.setConfig(null);
else {
MultivaluedHashMap<String, String> newConfig = new MultivaluedHashMap<>();
newConfig.putAll(cred.getConfig());
entity.setConfig(newConfig);
}
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
CredentialEntity entity = new CredentialEntity();
entity.setId(KeycloakModelUtils.generateId());
toEntity(cred, entity);
getMongoStore().pushItemToList(userEntity, "credentials", entity, true, invocationContext);
cred.setId(entity.getId());
return cred;
}
@Override
public Set<GroupModel> getGroups(RealmModel realm, UserModel user) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getGroupIds() == null || userEntity.getGroupIds().isEmpty()) return Collections.EMPTY_SET;
Set<GroupModel> groups = new HashSet<>();
for (String groupId : userEntity.getGroupIds()) {
GroupModel group = session.realms().getGroupById(groupId, realm);
if (group != null) groups.add(group);
}
return groups;
}
@Override
public void joinGroup(RealmModel realm, UserModel user, GroupModel group) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
getMongoStore().pushItemToList(userEntity, "groupIds", group.getId(), true, invocationContext);
}
@Override
public void leaveGroup(RealmModel realm, UserModel user, GroupModel group) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || group == null) return;
getMongoStore().pullItemFromList(userEntity, "groupIds", group.getId(), invocationContext);
}
@Override
public List<String> getMembership(RealmModel realm, GroupModel group, int firstResult, int max) {
QueryBuilder queryBuilder = new QueryBuilder()
.and("realmId").is(realm.getId());
queryBuilder.and("groupIds").is(group.getId());
List<FederatedUser> users = getMongoStore().loadEntities(FederatedUser.class, queryBuilder.get(), null, firstResult, max, invocationContext);
List<String> ids = new LinkedList<>();
for (FederatedUser user : users) ids.add(user.getId());
return ids;
}
@Override
public Set<String> getRequiredActions(RealmModel realm, UserModel user) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getRequiredActions() == null || userEntity.getRequiredActions().isEmpty()) return Collections.EMPTY_SET;
Set<String> set = new HashSet<>();
set.addAll(userEntity.getRequiredActions());
return set;
}
@Override
public void addRequiredAction(RealmModel realm, UserModel user, String action) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
getMongoStore().pushItemToList(userEntity, "requiredActions", action, true, invocationContext);
}
@Override
public void removeRequiredAction(RealmModel realm, UserModel user, String action) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getRequiredActions() == null || userEntity.getRequiredActions().isEmpty()) return;
getMongoStore().pullItemFromList(userEntity, "requiredActions", action, invocationContext);
}
@Override
public void grantRole(RealmModel realm, UserModel user, RoleModel role) {
FederatedUser userEntity = findOrCreate(realm, user.getId());
getMongoStore().pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext);
}
@Override
public Set<RoleModel> getRoleMappings(RealmModel realm, UserModel user) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getRoleIds() == null || userEntity.getRoleIds().isEmpty()) return Collections.EMPTY_SET;
Set<RoleModel> roles = new HashSet<>();
for (String roleId : userEntity.getRoleIds()) {
RoleModel role = realm.getRoleById(roleId);
if (role != null) roles.add(role);
}
return roles;
}
@Override
public void deleteRoleMapping(RealmModel realm, UserModel user, RoleModel role) {
FederatedUser userEntity = getUserById(user.getId());
if (userEntity == null || userEntity.getRoleIds() == null || userEntity.getRoleIds().isEmpty()) return;
getMongoStore().pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext);
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.mongo;
import org.keycloak.Config;
import org.keycloak.connections.mongo.MongoConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.storage.federated.UserFederatedStorageProviderFactory;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class MongoUserFederatedStorageProviderFactory implements UserFederatedStorageProviderFactory {
@Override
public UserFederatedStorageProvider create(KeycloakSession session) {
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
return new MongoUserFederatedStorageProvider(session, connection.getInvocationContext());
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return "mongo";
}
}

View file

@ -0,0 +1,124 @@
/*
* 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.mongo.entity;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.keycloak.connections.mongo.api.MongoCollection;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.AbstractIdentifiableEntity;
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
import org.keycloak.models.mongo.keycloak.entities.FederatedIdentityEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity;
import org.keycloak.models.mongo.keycloak.entities.UserConsentEntity;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@MongoCollection(collectionName = "federatedusers")
public class FederatedUser extends AbstractIdentifiableEntity implements MongoIdentifiableEntity {
protected String realmId;
protected String storageId;
private Map<String, List<String>> attributes;
private List<String> roleIds;
private List<String> groupIds;
private List<String> requiredActions;
private List<CredentialEntity> credentials;
private List<FederatedIdentityEntity> federatedIdentities;
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.realmId = realmId;
}
public String getStorageId() {
return storageId;
}
public void setStorageId(String storageId) {
this.storageId = storageId;
}
public Map<String, List<String>> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, List<String>> attributes) {
this.attributes = attributes;
}
public List<String> getRoleIds() {
return roleIds;
}
public void setRoleIds(List<String> roleIds) {
this.roleIds = roleIds;
}
public List<String> getGroupIds() {
return groupIds;
}
public void setGroupIds(List<String> groupIds) {
this.groupIds = groupIds;
}
public List<String> getRequiredActions() {
return requiredActions;
}
public void setRequiredActions(List<String> requiredActions) {
this.requiredActions = requiredActions;
}
public List<CredentialEntity> getCredentials() {
return credentials;
}
public void setCredentials(List<CredentialEntity> credentials) {
this.credentials = credentials;
}
public List<FederatedIdentityEntity> getFederatedIdentities() {
return federatedIdentities;
}
public void setFederatedIdentities(List<FederatedIdentityEntity> federatedIdentities) {
this.federatedIdentities = federatedIdentities;
}
@Override
public void afterRemove(MongoStoreInvocationContext context) {
// Remove all consents of this user
DBObject query = new QueryBuilder()
.and("userId").is(getId())
.get();
context.getMongoStore().removeEntities(MongoUserConsentEntity.class, query, true, context);
}
}

View file

@ -0,0 +1 @@
org.keycloak.storage.mongo.MongoUserFederatedStorageProviderFactory

View file

@ -1,166 +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.utils;
import org.keycloak.common.util.Time;
import org.keycloak.hash.PasswordHashManager;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.PasswordToken;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FederatedCredentialValidation {
private static int hashIterations(RealmModel realm) {
PasswordPolicy policy = realm.getPasswordPolicy();
if (policy != null) {
return policy.getHashIterations();
}
return -1;
}
/**
* Will update password if hash iteration policy has changed
*
* @param realm
* @param user
* @param password
* @return
*/
public static boolean validPassword(KeycloakSession session, RealmModel realm, UserModel user, String password, UserCredentialValueModel fedCred) {
return validateHashedCredential(session, realm, user, password, fedCred);
}
public static boolean validateHashedCredential(KeycloakSession session, RealmModel realm, UserModel user, String unhashedCredValue, UserCredentialValueModel credential) {
if (unhashedCredValue == null || unhashedCredValue.isEmpty()) {
return false;
}
boolean validated = PasswordHashManager.verify(session, realm, unhashedCredValue, credential);
if (validated) {
int iterations = hashIterations(realm);
if (iterations > -1 && iterations != credential.getHashIterations()) {
UserCredentialValueModel newCred = PasswordHashManager.encode(session, realm, unhashedCredValue);
session.userFederatedStorage().updateCredential(realm, user, newCred);
}
}
return validated;
}
public static boolean validPasswordToken(RealmModel realm, UserModel user, String encodedPasswordToken) {
try {
JWSInput jws = new JWSInput(encodedPasswordToken);
if (!RSAProvider.verify(jws, realm.getPublicKey())) {
return false;
}
PasswordToken passwordToken = jws.readJsonContent(PasswordToken.class);
if (!passwordToken.getRealm().equals(realm.getName())) {
return false;
}
if (!passwordToken.getUser().equals(user.getId())) {
return false;
}
if (Time.currentTime() - passwordToken.getTimestamp() > realm.getAccessCodeLifespanUserAction()) {
return false;
}
return true;
} catch (JWSInputException e) {
return false;
}
}
public static boolean validHOTP(KeycloakSession session, RealmModel realm, UserModel user, String otp, List<UserCredentialValueModel> fedCreds) {
UserCredentialValueModel passwordCred = null;
OTPPolicy policy = realm.getOTPPolicy();
HmacOTP validator = new HmacOTP(policy.getDigits(), policy.getAlgorithm(), policy.getLookAheadWindow());
for (UserCredentialValueModel cred : fedCreds) {
if (cred.getType().equals(UserCredentialModel.HOTP)) {
int counter = validator.validateHOTP(otp, cred.getValue(), cred.getCounter());
if (counter < 0) return false;
cred.setCounter(counter);
session.userFederatedStorage().updateCredential(realm, user, cred);
return true;
}
}
return false;
}
public static boolean validTOTP(RealmModel realm, UserModel user, String otp, List<UserCredentialValueModel> fedCreds) {
UserCredentialValueModel passwordCred = null;
OTPPolicy policy = realm.getOTPPolicy();
TimeBasedOTP validator = new TimeBasedOTP(policy.getAlgorithm(), policy.getDigits(), policy.getPeriod(), policy.getLookAheadWindow());
for (UserCredentialValueModel cred : fedCreds) {
if (validator.validateTOTP(otp, cred.getValue().getBytes())) {
return true;
}
}
return false;
}
public static boolean validSecret(RealmModel realm, UserModel user, String secret, UserCredentialValueModel cred) {
return cred.getValue().equals(secret);
}
public static boolean validCredential(KeycloakSession session, RealmModel realm, UserModel user, UserCredentialModel credential, List<UserCredentialValueModel> fedCreds) {
if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
if (!validPassword(session, realm, user, credential.getValue(), fedCreds.get(0))) {
return false;
}
} else if (credential.getType().equals(UserCredentialModel.PASSWORD_TOKEN)) {
if (!validPasswordToken(realm, user, credential.getValue())) {
return false;
}
} else if (credential.getType().equals(UserCredentialModel.TOTP)) {
if (!validTOTP(realm, user, credential.getValue(), fedCreds)) {
return false;
}
} else if (credential.getType().equals(UserCredentialModel.HOTP)) {
if (!validHOTP(session, realm, user, credential.getValue(), fedCreds)) {
return false;
}
} else if (credential.getType().equals(UserCredentialModel.SECRET)) {
if (!validSecret(realm, user, credential.getValue(), fedCreds.get(0))) {
return false;
}
} else {
return false;
}
return true;
}
}

View file

@ -1,186 +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.utils;
import org.keycloak.common.util.Time;
import org.keycloak.hash.PasswordHashManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FederatedCredentials {
public static void updateCredential(KeycloakSession session, UserFederatedStorageProvider provider, RealmModel realm, UserModel user, UserCredentialModel cred) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
updatePasswordCredential(session, provider,realm, user, cred);
} else if (UserCredentialModel.isOtp(cred.getType())) {
updateOtpCredential(session, provider, realm, user, cred);
} else {
UserCredentialValueModel fedCred = getCredentialByType(provider, realm, user, cred.getType());
if (fedCred == null) {
fedCred.setCreatedDate(Time.toMillis(Time.currentTime()));
fedCred.setType(cred.getType());
fedCred.setDevice(cred.getDevice());
fedCred.setValue(cred.getValue());
} else {
fedCred.setValue(cred.getValue());
}
provider.updateCredential(realm, user, fedCred);
}
}
public static UserCredentialValueModel getCredentialByType(UserFederatedStorageProvider provider, RealmModel realm, UserModel user, String type) {
List<UserCredentialValueModel> creds = provider.getCredentials(realm, user);
for (UserCredentialValueModel cred : creds) {
if (cred.getType().equals(type)) return cred;
}
return null;
}
public static LinkedList<UserCredentialValueModel> getCredentialsByType(UserFederatedStorageProvider provider, RealmModel realm, UserModel user, String type) {
List<UserCredentialValueModel> creds = provider.getCredentials(realm, user);
LinkedList<UserCredentialValueModel> newCreds = new LinkedList<>();
for (UserCredentialValueModel cred : creds) {
if (cred.getType().equals(type)) newCreds.add(cred);
}
return newCreds;
}
public static void updatePasswordCredential(KeycloakSession session, UserFederatedStorageProvider provider, RealmModel realm, UserModel user, UserCredentialModel cred) {
UserCredentialValueModel fedCred = getCredentialByType(provider, realm, user, cred.getType());
if (fedCred == null) {
UserCredentialValueModel newCred = PasswordHashManager.encode(session, realm, cred.getValue());
newCred.setCreatedDate(Time.toMillis(Time.currentTime()));
newCred.setType(cred.getType());
newCred.setDevice(cred.getDevice());
provider.updateCredential(realm, user, newCred);
} else {
int expiredPasswordsPolicyValue = -1;
PasswordPolicy policy = realm.getPasswordPolicy();
if(policy != null) {
expiredPasswordsPolicyValue = policy.getExpiredPasswords();
}
if (expiredPasswordsPolicyValue != -1) {
fedCred.setType(UserCredentialModel.PASSWORD_HISTORY);
LinkedList<UserCredentialValueModel> credentialEntities = getCredentialsByType(provider, realm, user, UserCredentialModel.PASSWORD_HISTORY);
if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
Collections.sort(credentialEntities, new Comparator<UserCredentialValueModel>() {
@Override
public int compare(UserCredentialValueModel o1, UserCredentialValueModel o2) {
if (o1.getCreatedDate().equals(o2.getCreatedDate())) return 0;
return o1.getCreatedDate() < o2.getCreatedDate() ? -1 : 1;
}
});
while (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
UserCredentialValueModel model = credentialEntities.removeFirst();
provider.removeCredential(realm, user, model);
}
}
provider.updateCredential(realm, user, fedCred);
fedCred = PasswordHashManager.encode(session, realm, cred.getValue());
fedCred.setCreatedDate(Time.toMillis(Time.currentTime()));
fedCred.setType(cred.getType());
fedCred.setDevice(cred.getDevice());
provider.updateCredential(realm, user, fedCred);
} else {
// clear password history as it is not required anymore
for (UserCredentialValueModel model : getCredentialsByType(provider, realm, user, UserCredentialModel.PASSWORD_HISTORY)) {
provider.removeCredential(realm, user, model);
}
UserCredentialValueModel newCred = PasswordHashManager.encode(session, realm, cred.getValue());
newCred.setCreatedDate(Time.toMillis(Time.currentTime()));
newCred.setType(cred.getType());
newCred.setDevice(cred.getDevice());
newCred.setId(fedCred.getId());
provider.updateCredential(realm, user, newCred);
}
}
}
public static void updateOtpCredential(KeycloakSession session, UserFederatedStorageProvider provider, RealmModel realm, UserModel user, UserCredentialModel cred) {
LinkedList<UserCredentialValueModel> credentialEntities = getCredentialsByType(provider, realm, user, UserCredentialModel.PASSWORD_HISTORY);
if (credentialEntities.isEmpty()) {
UserCredentialValueModel fedCred = new UserCredentialValueModel();
fedCred.setCreatedDate(Time.toMillis(Time.currentTime()));
fedCred.setType(cred.getType());
fedCred.setDevice(cred.getDevice());
fedCred.setValue(cred.getValue());
OTPPolicy otpPolicy = realm.getOTPPolicy();
fedCred.setAlgorithm(otpPolicy.getAlgorithm());
fedCred.setDigits(otpPolicy.getDigits());
fedCred.setCounter(otpPolicy.getInitialCounter());
fedCred.setPeriod(otpPolicy.getPeriod());
provider.updateCredential(realm, user, fedCred);
} else {
OTPPolicy policy = realm.getOTPPolicy();
if (cred.getDevice() == null) {
for (UserCredentialValueModel model : credentialEntities) provider.removeCredential(realm, user, model);
UserCredentialValueModel fedCred = new UserCredentialValueModel();
fedCred.setCreatedDate(Time.toMillis(Time.currentTime()));
fedCred.setType(cred.getType());
fedCred.setDevice(cred.getDevice());
fedCred.setDigits(policy.getDigits());
fedCred.setCounter(policy.getInitialCounter());
fedCred.setAlgorithm(policy.getAlgorithm());
fedCred.setValue(cred.getValue());
fedCred.setPeriod(policy.getPeriod());
provider.updateCredential(realm, user, fedCred);
} else {
UserCredentialValueModel fedCred = new UserCredentialValueModel();
for (UserCredentialValueModel model : credentialEntities) {
if (cred.getDevice().equals(model.getDevice())) {
fedCred = model;
break;
}
}
fedCred.setCreatedDate(Time.toMillis(Time.currentTime()));
fedCred.setType(cred.getType());
fedCred.setDevice(cred.getDevice());
fedCred.setDigits(policy.getDigits());
fedCred.setCounter(policy.getInitialCounter());
fedCred.setAlgorithm(policy.getAlgorithm());
fedCred.setValue(cred.getValue());
fedCred.setPeriod(policy.getPeriod());
provider.updateCredential(realm, user, fedCred);
}
}
}
}

View file

@ -63,6 +63,9 @@ public class StorageId implements Serializable {
public static String externalId(String keycloakId) {
return new StorageId(keycloakId).getExternalId();
}
public static String providerId(String keycloakId) {
return new StorageId(keycloakId).getProviderId();
}

View file

@ -1,48 +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.storage.federated;
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface UserCredentialsFederatedStorage {
// deprecated
void updateCredential(RealmModel realm, UserModel user, UserCredentialModel cred);
void updateCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred);
void removeCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred);
List<UserCredentialValueModel> getCredentials(RealmModel realm, UserModel user);
// new
void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
boolean removeCredential(RealmModel realm, String id);
CredentialModel getCredentialById(String id);
List<CredentialModel> getCredentials(RealmModel realm);
List<CredentialModel> getUserCredentials(RealmModel realm, UserModel user);
List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type);
CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
}

View file

@ -37,7 +37,6 @@ public interface UserFederatedStorageProvider extends Provider,
UserAttributeFederatedStorage,
UserBrokerLinkFederatedStorage,
UserConsentFederatedStorage,
UserCredentialsFederatedStorage,
UserGroupMembershipFederatedStorage,
UserRequiredActionsFederatedStorage,
UserRoleMappingsFederatedStorage {

View file

@ -459,6 +459,7 @@
<systemPropertyVariables>
<keycloak.realm.provider>mongo</keycloak.realm.provider>
<keycloak.user.provider>mongo</keycloak.user.provider>
<keycloak.userFederatedStorage.provider>mongo</keycloak.userFederatedStorage.provider>
<keycloak.userSessionPersister.provider>mongo</keycloak.userSessionPersister.provider>
<keycloak.eventsStore.provider>mongo</keycloak.eventsStore.provider>
<keycloak.authorization.provider>mongo</keycloak.authorization.provider>

View file

@ -22,6 +22,10 @@
"provider": "${keycloak.user.provider:jpa}"
},
"userFederatedStorage": {
"provider": "${keycloak.userFederatedStorage.provider:jpa}"
},
"userSessionPersister": {
"provider": "${keycloak.userSessionPersister.provider:jpa}"
},