Added NoSQLQueryBuilder API. Support for persistence of all objects. All unit tests are passing and UI is working with MongoDB
This commit is contained in:
parent
815e466d43
commit
5b8908c822
27 changed files with 1823 additions and 699 deletions
|
@ -1,43 +0,0 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBImpl;
|
||||
|
||||
/**
|
||||
* NoSQL implementation based on MongoDB
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MongoDBSessionFactory implements KeycloakSessionFactory {
|
||||
|
||||
private final MongoClient mongoClient;
|
||||
private final NoSQL mongoDB;
|
||||
|
||||
public MongoDBSessionFactory(String host, int port, String dbName) {
|
||||
try {
|
||||
// TODO: authentication support
|
||||
mongoClient = new MongoClient(host, port);
|
||||
|
||||
DB db = mongoClient.getDB(dbName);
|
||||
mongoDB = new MongoDBImpl(db);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSession createSession() {
|
||||
return new NoSQLSession(mongoDB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mongoClient.close();
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.resteasy.spi.NotImplementedYetException;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.KeycloakTransaction;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.nosql.data.RealmData;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class NoSQLSession implements KeycloakSession {
|
||||
|
||||
private static final NoSQLTransaction PLACEHOLDER = new NoSQLTransaction();
|
||||
private final NoSQL noSQL;
|
||||
|
||||
public NoSQLSession(NoSQL noSQL) {
|
||||
this.noSQL = noSQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
return PLACEHOLDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmData newRealm = new RealmData();
|
||||
newRealm.setName(name);
|
||||
|
||||
noSQL.saveObject(newRealm);
|
||||
|
||||
RealmAdapter realm = new RealmAdapter(newRealm, noSQL);
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
// Ignore ID for now. It seems that it exists just for workaround picketlink
|
||||
return createRealm(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
RealmData realmData = noSQL.loadObject(RealmData.class, id);
|
||||
return new RealmAdapter(realmData, noSQL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms(UserModel admin) {
|
||||
throw new NotImplementedYetException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRealm(RealmModel realm) {
|
||||
noSQL.removeObject(RealmData.class, realm.getId());
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLId;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NoSQLCollection(collectionName = "users")
|
||||
public class NoSQLUser {
|
||||
|
||||
@NoSQLId
|
||||
private String oid;
|
||||
|
||||
private String username;
|
||||
|
||||
private String realmId;
|
||||
|
||||
@NoSQLId
|
||||
public String getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(String oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@NoSQLField(fieldName = "realm_id")
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
}
|
|
@ -1,492 +0,0 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bouncycastle.openssl.PEMWriter;
|
||||
import org.jboss.resteasy.security.PemUtils;
|
||||
import org.keycloak.services.models.ApplicationModel;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RequiredCredentialModel;
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.SocialLinkModel;
|
||||
import org.keycloak.services.models.UserCredentialModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.data.RealmData;
|
||||
import org.keycloak.services.models.nosql.data.RoleData;
|
||||
import org.keycloak.services.models.nosql.data.UserData;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RealmAdapter implements RealmModel {
|
||||
|
||||
private final RealmData realm;
|
||||
private final NoSQL noSQL;
|
||||
|
||||
protected volatile transient PublicKey publicKey;
|
||||
protected volatile transient PrivateKey privateKey;
|
||||
|
||||
public RealmAdapter(RealmData realmData, NoSQL noSQL) {
|
||||
this.realm = realmData;
|
||||
this.noSQL = noSQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return realm.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return realm.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
realm.setName(name);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return realm.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
realm.setEnabled(enabled);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSocial() {
|
||||
return realm.isSocial();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSocial(boolean social) {
|
||||
realm.setSocial(social);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutomaticRegistrationAfterSocialLogin() {
|
||||
return realm.isAutomaticRegistrationAfterSocialLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) {
|
||||
realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSslNotRequired() {
|
||||
return realm.isSslNotRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSslNotRequired(boolean sslNotRequired) {
|
||||
realm.setSslNotRequired(sslNotRequired);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCookieLoginAllowed() {
|
||||
return realm.isCookieLoginAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
|
||||
realm.setCookieLoginAllowed(cookieLoginAllowed);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistrationAllowed() {
|
||||
return realm.isRegistrationAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegistrationAllowed(boolean registrationAllowed) {
|
||||
realm.setRegistrationAllowed(registrationAllowed);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTokenLifespan() {
|
||||
return realm.getTokenLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTokenLifespan(int tokenLifespan) {
|
||||
realm.setTokenLifespan(tokenLifespan);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessCodeLifespan() {
|
||||
return realm.getAccessCodeLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessCodeLifespan(int accessCodeLifespan) {
|
||||
realm.setAccessCodeLifespan(accessCodeLifespan);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPublicKeyPem() {
|
||||
return realm.getPublicKeyPem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicKeyPem(String publicKeyPem) {
|
||||
realm.setPublicKeyPem(publicKeyPem);
|
||||
this.publicKey = null;
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrivateKeyPem() {
|
||||
return realm.getPrivateKeyPem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrivateKeyPem(String privateKeyPem) {
|
||||
realm.setPrivateKeyPem(privateKeyPem);
|
||||
this.privateKey = null;
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublicKey() {
|
||||
if (publicKey != null) return publicKey;
|
||||
String pem = getPublicKeyPem();
|
||||
if (pem != null) {
|
||||
try {
|
||||
publicKey = PemUtils.decodePublicKey(pem);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicKey(PublicKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
StringWriter writer = new StringWriter();
|
||||
PEMWriter pemWriter = new PEMWriter(writer);
|
||||
try {
|
||||
pemWriter.writeObject(publicKey);
|
||||
pemWriter.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String s = writer.toString();
|
||||
setPublicKeyPem(PemUtils.removeBeginEnd(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey() {
|
||||
if (privateKey != null) return privateKey;
|
||||
String pem = getPrivateKeyPem();
|
||||
if (pem != null) {
|
||||
try {
|
||||
privateKey = PemUtils.decodePrivateKey(pem);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrivateKey(PrivateKey privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
StringWriter writer = new StringWriter();
|
||||
PEMWriter pemWriter = new PEMWriter(writer);
|
||||
try {
|
||||
pemWriter.writeObject(privateKey);
|
||||
pemWriter.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String s = writer.toString();
|
||||
setPrivateKeyPem(PemUtils.removeBeginEnd(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredCredentials() {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredCredential(String cred) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePassword(UserModel user, String password) {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateTOTP(UserModel user, String password, String token) {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCredential(UserModel user, UserCredentialModel cred) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUser(String name) {
|
||||
NoSQLQuery query = NoSQLQuery.create().put("loginName", name).put("realmId", getId());
|
||||
UserData user = noSQL.loadSingleObject(UserData.class, query);
|
||||
|
||||
if (user == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new UserAdapter(user, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(String username) {
|
||||
if (getUser(username) != null) {
|
||||
throw new IllegalArgumentException("User " + username + " already exists");
|
||||
}
|
||||
|
||||
UserData userData = new UserData();
|
||||
userData.setLoginName(username);
|
||||
userData.setEnabled(true);
|
||||
userData.setRealmId(getId());
|
||||
|
||||
noSQL.saveObject(userData);
|
||||
return new UserAdapter(userData, noSQL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter getRole(String name) {
|
||||
NoSQLQuery query = NoSQLQuery.create().put("name", name).put("realmId", getId());
|
||||
RoleData role = noSQL.loadSingleObject(RoleData.class, query);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(role, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
if (getRole(name) != null) {
|
||||
throw new IllegalArgumentException("Role " + name + " already exists");
|
||||
}
|
||||
|
||||
RoleData roleData = new RoleData();
|
||||
roleData.setName(name);
|
||||
roleData.setRealmId(getId());
|
||||
|
||||
noSQL.saveObject(roleData);
|
||||
return new RoleAdapter(roleData, noSQL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getRoles() {
|
||||
NoSQLQuery query = NoSQLQuery.create().put("realmId", getId());
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
|
||||
List<RoleModel> result = new ArrayList<RoleModel>();
|
||||
for (RoleData role : roles) {
|
||||
result.add(new RoleAdapter(role, noSQL));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getDefaultRoles() {
|
||||
List<RoleModel> defaultRoleModels = new ArrayList<RoleModel>();
|
||||
if (realm.getDefaultRoles() != null) {
|
||||
for (String name : realm.getDefaultRoles()) {
|
||||
RoleAdapter role = getRole(name);
|
||||
if (role != null) {
|
||||
defaultRoleModels.add(role);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultRoleModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDefaultRole(String name) {
|
||||
if (getRole(name) == null) {
|
||||
addRole(name);
|
||||
}
|
||||
|
||||
String[] defaultRoles = realm.getDefaultRoles();
|
||||
if (defaultRoles == null) {
|
||||
defaultRoles = new String[1];
|
||||
} else {
|
||||
defaultRoles = Arrays.copyOf(defaultRoles, defaultRoles.length + 1);
|
||||
}
|
||||
defaultRoles[defaultRoles.length - 1] = name;
|
||||
|
||||
realm.setDefaultRoles(defaultRoles);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
for (String name : defaultRoles) {
|
||||
if (getRole(name) == null) {
|
||||
addRole(name);
|
||||
}
|
||||
}
|
||||
|
||||
realm.setDefaultRoles(defaultRoles);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ApplicationModel> getResourceNameMap() {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplicationModel> getApplications() {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel addApplication(String name) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(UserModel user, RoleModel role) {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRole(UserModel user, RoleModel role) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRoleMappings(UserModel user) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScope(UserModel agent, String roleName) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope(UserModel agent) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRealmAdmin(UserModel agent) {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRealmAdmin(UserModel agent) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id) {
|
||||
RoleData role = noSQL.loadObject(RoleData.class, id);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(role, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredApplicationCredentials() {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(UserModel user, String role) {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel getApplicationById(String id) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredOAuthClientCredential(String type) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredResourceCredential(String type) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRequiredCredentials(Set<String> creds) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRequiredOAuthClientCredentials(Set<String> creds) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRequiredApplicationCredentials(Set<String> creds) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocialLinkModel> getSocialLinks(UserModel user) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
protected void updateRealm() {
|
||||
noSQL.saveObject(realm);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
package org.keycloak.services.models.nosql.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.services.models.nosql.api;
|
||||
package org.keycloak.services.models.nosql.api.query;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -9,18 +9,11 @@ import java.util.Map;
|
|||
*/
|
||||
public class NoSQLQuery {
|
||||
|
||||
private Map<String, Object> queryAttributes = new HashMap<String, Object>();
|
||||
private final Map<String, Object> queryAttributes;
|
||||
|
||||
private NoSQLQuery() {};
|
||||
|
||||
public static NoSQLQuery create() {
|
||||
return new NoSQLQuery();
|
||||
}
|
||||
|
||||
public NoSQLQuery put(String name, Object value) {
|
||||
queryAttributes.put(name, value);
|
||||
return this;
|
||||
}
|
||||
NoSQLQuery(Map<String, Object> queryAttributes) {
|
||||
this.queryAttributes = queryAttributes;
|
||||
};
|
||||
|
||||
public Map<String, Object> getQueryAttributes() {
|
||||
return Collections.unmodifiableMap(queryAttributes);
|
|
@ -0,0 +1,38 @@
|
|||
package org.keycloak.services.models.nosql.api.query;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class NoSQLQueryBuilder {
|
||||
|
||||
private Map<String, Object> queryAttributes = new HashMap<String, Object>();
|
||||
|
||||
protected NoSQLQueryBuilder() {};
|
||||
|
||||
public static NoSQLQueryBuilder create(Class<? extends NoSQLQueryBuilder> builderClass) {
|
||||
try {
|
||||
return builderClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public NoSQLQuery build() {
|
||||
return new NoSQLQuery(queryAttributes);
|
||||
}
|
||||
|
||||
public NoSQLQueryBuilder andCondition(String name, Object value) {
|
||||
this.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract NoSQLQueryBuilder inCondition(String name, Object[] values);
|
||||
|
||||
protected void put(String name, Object value) {
|
||||
queryAttributes.put(name, value);
|
||||
}
|
||||
|
||||
}
|
|
@ -12,16 +12,13 @@ import com.mongodb.DBCollection;
|
|||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.jboss.resteasy.logging.Logger;
|
||||
import org.jboss.resteasy.spi.NotImplementedYetException;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.models.nosql.api.AttributedNoSQLObject;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLId;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLObject;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.types.Converter;
|
||||
import org.keycloak.services.models.nosql.api.types.TypeConverter;
|
||||
import org.keycloak.services.models.nosql.impl.types.BasicDBListToStringArrayConverter;
|
||||
|
@ -68,14 +65,14 @@ public class MongoDBImpl implements NoSQL {
|
|||
|
||||
|
||||
dbObject.append(propName, propValue);
|
||||
}
|
||||
|
||||
// Adding attributes
|
||||
if (object instanceof AttributedNoSQLObject) {
|
||||
AttributedNoSQLObject attributedObject = (AttributedNoSQLObject)object;
|
||||
Map<String, String> attributes = attributedObject.getAttributes();
|
||||
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
|
||||
dbObject.append(attribute.getKey(), attribute.getValue());
|
||||
}
|
||||
// Adding attributes
|
||||
if (object instanceof AttributedNoSQLObject) {
|
||||
AttributedNoSQLObject attributedObject = (AttributedNoSQLObject)object;
|
||||
Map<String, String> attributes = attributedObject.getAttributes();
|
||||
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
|
||||
dbObject.append(attribute.getKey(), attribute.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,8 +95,7 @@ public class MongoDBImpl implements NoSQL {
|
|||
|
||||
@Override
|
||||
public <T extends NoSQLObject> T loadObject(Class<T> type, String oid) {
|
||||
ObjectInfo<T> objectInfo = getObjectInfo(type);
|
||||
DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
|
||||
BasicDBObject idQuery = new BasicDBObject("_id", new ObjectId(oid));
|
||||
DBObject dbObject = dbCollection.findOne(idQuery);
|
||||
|
@ -122,15 +118,9 @@ public class MongoDBImpl implements NoSQL {
|
|||
|
||||
@Override
|
||||
public <T extends NoSQLObject> List<T> loadObjects(Class<T> type, NoSQLQuery query) {
|
||||
Map<String, Object> queryAttributes = query.getQueryAttributes();
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
BasicDBObject dbQuery = getDBQueryFromQuery(query);
|
||||
|
||||
ObjectInfo<T> objectInfo = getObjectInfo(type);
|
||||
DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
|
||||
|
||||
BasicDBObject dbQuery = new BasicDBObject();
|
||||
for (Map.Entry<String, Object> queryAttr : queryAttributes.entrySet()) {
|
||||
dbQuery.append(queryAttr.getKey(), queryAttr.getValue());
|
||||
}
|
||||
DBCursor cursor = dbCollection.find(dbQuery);
|
||||
|
||||
return convertCursor(type, cursor);
|
||||
|
@ -149,19 +139,21 @@ public class MongoDBImpl implements NoSQL {
|
|||
|
||||
@Override
|
||||
public void removeObject(Class<? extends NoSQLObject> type, String oid) {
|
||||
ObjectInfo<?> objectInfo = getObjectInfo(type);
|
||||
DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
|
||||
BasicDBObject query = new BasicDBObject("_id", new ObjectId(oid));
|
||||
dbCollection.remove(query);
|
||||
BasicDBObject dbQuery = new BasicDBObject("_id", new ObjectId(oid));
|
||||
dbCollection.remove(dbQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeObjects(Class<? extends NoSQLObject> type, NoSQLQuery query) {
|
||||
throw new NotImplementedYetException();
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
BasicDBObject dbQuery = getDBQueryFromQuery(query);
|
||||
|
||||
dbCollection.remove(dbQuery);
|
||||
}
|
||||
|
||||
// Possibility to add converters
|
||||
// Possibility to add user-defined converters
|
||||
public void addConverter(Converter<?, ?> converter) {
|
||||
typeConverter.addConverter(converter);
|
||||
}
|
||||
|
@ -171,6 +163,7 @@ public class MongoDBImpl implements NoSQL {
|
|||
if (objectInfo == null) {
|
||||
Property<String> idProperty = PropertyQueries.<String>createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(NoSQLId.class)).getFirstResult();
|
||||
if (idProperty == null) {
|
||||
// TODO: should be allowed to have NoSQLObject classes without declared NoSQLId annotation?
|
||||
throw new IllegalStateException("Class " + objectClass + " doesn't have property with declared annotation " + NoSQLId.class);
|
||||
}
|
||||
|
||||
|
@ -195,6 +188,10 @@ public class MongoDBImpl implements NoSQL {
|
|||
|
||||
|
||||
private <T extends NoSQLObject> T convertObject(Class<T> type, DBObject dbObject) {
|
||||
if (dbObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ObjectInfo<T> objectInfo = getObjectInfo(type);
|
||||
|
||||
T object;
|
||||
|
@ -255,4 +252,18 @@ public class MongoDBImpl implements NoSQL {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
private DBCollection getDBCollectionForType(Class<? extends NoSQLObject> type) {
|
||||
ObjectInfo<?> objectInfo = getObjectInfo(type);
|
||||
return database.getCollection(objectInfo.getDbCollectionName());
|
||||
}
|
||||
|
||||
private BasicDBObject getDBQueryFromQuery(NoSQLQuery query) {
|
||||
Map<String, Object> queryAttributes = query.getQueryAttributes();
|
||||
BasicDBObject dbQuery = new BasicDBObject();
|
||||
for (Map.Entry<String, Object> queryAttr : queryAttributes.entrySet()) {
|
||||
dbQuery.append(queryAttr.getKey(), queryAttr.getValue());
|
||||
}
|
||||
return dbQuery;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package org.keycloak.services.models.nosql.impl;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MongoDBQueryBuilder extends NoSQLQueryBuilder {
|
||||
|
||||
@Override
|
||||
public NoSQLQueryBuilder inCondition(String name, Object[] values) {
|
||||
if (values == null) {
|
||||
values = new Object[0];
|
||||
}
|
||||
|
||||
if ("_id".equals(name)) {
|
||||
// we need to convert Strings to ObjectID
|
||||
ObjectId[] objIds = new ObjectId[values.length];
|
||||
for (int i=0 ; i<values.length ; i++) {
|
||||
String id = values[i].toString();
|
||||
ObjectId objectId = new ObjectId(id);
|
||||
objIds[i] = objectId;
|
||||
}
|
||||
values = objIds;
|
||||
}
|
||||
|
||||
BasicDBObject inObject = new BasicDBObject("$in", values);
|
||||
put(name, inObject);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.keycloak.services.models.nosql.impl;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
private Utils() {};
|
||||
|
||||
/**
|
||||
* Add item to the end of array
|
||||
*
|
||||
* @param inputArray could be null. In this case, method will return array of length 1 with added item
|
||||
* @param item must be not-null
|
||||
* @return array with added item to the end
|
||||
*/
|
||||
public static <T> T[] addItemToArray(T[] inputArray, T item) {
|
||||
if (item == null) {
|
||||
throw new IllegalArgumentException("item must be non-null");
|
||||
}
|
||||
|
||||
T[] outputArray;
|
||||
if (inputArray == null) {
|
||||
outputArray = (T[])Array.newInstance(item.getClass(), 1);
|
||||
} else {
|
||||
outputArray = Arrays.copyOf(inputArray, inputArray.length + 1);
|
||||
}
|
||||
outputArray[outputArray.length - 1] = item;
|
||||
return outputArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if array contains specified item
|
||||
* @param array could be null (In this case method always return false)
|
||||
* @param item can't be null
|
||||
* @return
|
||||
*/
|
||||
public static boolean contains(Object[] array, Object item) {
|
||||
if (item == null) {
|
||||
throw new IllegalArgumentException("item must be non-null");
|
||||
}
|
||||
|
||||
if (array != null) {
|
||||
for (Object current : array) {
|
||||
if (item.equals(current)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.services.models.ApplicationModel;
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.impl.Utils;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.ApplicationData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RoleData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.UserData;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ApplicationAdapter implements ApplicationModel {
|
||||
|
||||
private final ApplicationData application;
|
||||
private final NoSQL noSQL;
|
||||
|
||||
private UserData resourceUser;
|
||||
|
||||
public ApplicationAdapter(ApplicationData applicationData, NoSQL noSQL) {
|
||||
this.application = applicationData;
|
||||
this.noSQL = noSQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateResource() {
|
||||
noSQL.saveObject(application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getResourceUser() {
|
||||
// This is not thread-safe. Assumption is that ApplicationAdapter instance is per-client object
|
||||
if (resourceUser == null) {
|
||||
resourceUser = noSQL.loadObject(UserData.class, application.getResourceUserId());
|
||||
}
|
||||
|
||||
return resourceUser != null ? new UserAdapter(resourceUser, noSQL) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return application.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return application.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
application.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return application.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
application.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return application.isSurrogateAuthRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
|
||||
application.setSurrogateAuthRequired(surrogateAuthRequired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManagementUrl() {
|
||||
return application.getManagementUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManagementUrl(String url) {
|
||||
application.setManagementUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter getRole(String name) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("name", name)
|
||||
.andCondition("applicationId", getId())
|
||||
.build();
|
||||
RoleData role = noSQL.loadSingleObject(RoleData.class, query);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(role, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter addRole(String name) {
|
||||
if (getRole(name) != null) {
|
||||
throw new IllegalArgumentException("Role " + name + " already exists");
|
||||
}
|
||||
|
||||
RoleData roleData = new RoleData();
|
||||
roleData.setName(name);
|
||||
roleData.setApplicationId(getId());
|
||||
|
||||
noSQL.saveObject(roleData);
|
||||
return new RoleAdapter(roleData, noSQL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getRoles() {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("applicationId", getId())
|
||||
.build();
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
|
||||
List<RoleModel> result = new ArrayList<RoleModel>();
|
||||
for (RoleData role : roles) {
|
||||
result.add(new RoleAdapter(role, noSQL));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRoleMappings(UserModel user) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
String[] roleIds = userData.getRoleIds();
|
||||
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.inCondition("_id", roleIds)
|
||||
.build();
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
// TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
|
||||
for (RoleData role : roles) {
|
||||
if (getId().equals(role.getApplicationId())) {
|
||||
result.add(role.getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScope(UserModel agent, String roleName) {
|
||||
RoleAdapter role = getRole(roleName);
|
||||
if (role == null) {
|
||||
throw new RuntimeException("Role not found");
|
||||
}
|
||||
|
||||
addScope(agent, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScope(UserModel agent, RoleModel role) {
|
||||
UserData userData = ((UserAdapter)agent).getUser();
|
||||
RoleData roleData = ((RoleAdapter)role).getRole();
|
||||
|
||||
String[] scopeIds = userData.getScopeIds();
|
||||
scopeIds = Utils.addItemToArray(scopeIds, roleData.getId());
|
||||
userData.setScopeIds(scopeIds);
|
||||
|
||||
noSQL.saveObject(userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope(UserModel agent) {
|
||||
UserData userData = ((UserAdapter)agent).getUser();
|
||||
String[] scopeIds = userData.getScopeIds();
|
||||
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.inCondition("_id", scopeIds)
|
||||
.build();
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
// TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
|
||||
for (RoleData role : roles) {
|
||||
if (getId().equals(role.getApplicationId())) {
|
||||
result.add(role.getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLObject;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.ApplicationData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RealmData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RequiredCredentialData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RoleData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.SocialLinkData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.UserData;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBImpl;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.credentials.PasswordData;
|
||||
|
||||
/**
|
||||
* NoSQL implementation based on MongoDB
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MongoDBSessionFactory implements KeycloakSessionFactory {
|
||||
|
||||
private static final Class<?>[] MANAGED_DATA_TYPES = {
|
||||
RealmData.class,
|
||||
UserData.class,
|
||||
RoleData.class,
|
||||
RequiredCredentialData.class,
|
||||
PasswordData.class,
|
||||
SocialLinkData.class,
|
||||
ApplicationData.class
|
||||
};
|
||||
|
||||
private final MongoClient mongoClient;
|
||||
private final NoSQL mongoDB;
|
||||
|
||||
public MongoDBSessionFactory(String host, int port, String dbName, boolean removeAllObjectsAtStartup) {
|
||||
try {
|
||||
// TODO: authentication support
|
||||
mongoClient = new MongoClient(host, port);
|
||||
|
||||
DB db = mongoClient.getDB(dbName);
|
||||
mongoDB = new MongoDBImpl(db);
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (removeAllObjectsAtStartup) {
|
||||
NoSQLQuery emptyQuery = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class).build();
|
||||
for (Class<?> type : MANAGED_DATA_TYPES) {
|
||||
mongoDB.removeObjects((Class<? extends NoSQLObject>)type, emptyQuery);
|
||||
}
|
||||
// TODO: logging
|
||||
System.out.println("All objects successfully removed from DB");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSession createSession() {
|
||||
return new NoSQLSession(mongoDB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mongoClient.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.resteasy.spi.NotImplementedYetException;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.KeycloakTransaction;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RealmData;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.picketlink.PicketlinkKeycloakSession;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class NoSQLSession implements KeycloakSession {
|
||||
|
||||
private static final NoSQLTransaction PLACEHOLDER = new NoSQLTransaction();
|
||||
private final NoSQL noSQL;
|
||||
|
||||
public NoSQLSession(NoSQL noSQL) {
|
||||
this.noSQL = noSQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
return PLACEHOLDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
return createRealm(PicketlinkKeycloakSession.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmData newRealm = new RealmData();
|
||||
newRealm.setId(id);
|
||||
newRealm.setName(name);
|
||||
|
||||
noSQL.saveObject(newRealm);
|
||||
|
||||
RealmAdapter realm = new RealmAdapter(newRealm, noSQL);
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("id", id)
|
||||
.build();
|
||||
RealmData realmData = noSQL.loadSingleObject(RealmData.class, query);
|
||||
return realmData != null ? new RealmAdapter(realmData, noSQL) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms(UserModel admin) {
|
||||
String userId = ((UserAdapter)admin).getUser().getId();
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("realmAdmins", userId)
|
||||
.build();
|
||||
List<RealmData> realms = noSQL.loadObjects(RealmData.class, query);
|
||||
|
||||
List<RealmModel> results = new ArrayList<RealmModel>();
|
||||
for (RealmData realmData : realms) {
|
||||
results.add(new RealmAdapter(realmData, noSQL));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRealm(RealmModel realm) {
|
||||
String oid = ((RealmAdapter)realm).getOid();
|
||||
noSQL.removeObject(RealmData.class, oid);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import org.keycloak.services.models.KeycloakTransaction;
|
||||
|
|
@ -0,0 +1,751 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bouncycastle.openssl.PEMWriter;
|
||||
import org.jboss.resteasy.security.PemUtils;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.models.ApplicationModel;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RequiredCredentialModel;
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.SocialLinkModel;
|
||||
import org.keycloak.services.models.UserCredentialModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.keycloak.credentials.PasswordCredentialHandler;
|
||||
import org.keycloak.services.models.nosql.keycloak.credentials.TOTPCredentialHandler;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.ApplicationData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RealmData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RequiredCredentialData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RoleData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.SocialLinkData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.UserData;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.impl.Utils;
|
||||
import org.keycloak.services.models.picketlink.relationships.ResourceRelationship;
|
||||
import org.picketlink.idm.credential.Credentials;
|
||||
import org.picketlink.idm.query.RelationshipQuery;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RealmAdapter implements RealmModel {
|
||||
|
||||
private final RealmData realm;
|
||||
private final NoSQL noSQL;
|
||||
|
||||
protected volatile transient PublicKey publicKey;
|
||||
protected volatile transient PrivateKey privateKey;
|
||||
|
||||
// TODO: likely shouldn't be static. And setup is not called ATM, which means that it's not possible to configure stuff like PasswordEncoder etc.
|
||||
private static PasswordCredentialHandler passwordCredentialHandler = new PasswordCredentialHandler();
|
||||
private static TOTPCredentialHandler totpCredentialHandler = new TOTPCredentialHandler();
|
||||
|
||||
public RealmAdapter(RealmData realmData, NoSQL noSQL) {
|
||||
this.realm = realmData;
|
||||
this.noSQL = noSQL;
|
||||
}
|
||||
|
||||
protected String getOid() {
|
||||
return realm.getOid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return realm.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return realm.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
realm.setName(name);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return realm.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
realm.setEnabled(enabled);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSocial() {
|
||||
return realm.isSocial();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSocial(boolean social) {
|
||||
realm.setSocial(social);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutomaticRegistrationAfterSocialLogin() {
|
||||
return realm.isAutomaticRegistrationAfterSocialLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) {
|
||||
realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSslNotRequired() {
|
||||
return realm.isSslNotRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSslNotRequired(boolean sslNotRequired) {
|
||||
realm.setSslNotRequired(sslNotRequired);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCookieLoginAllowed() {
|
||||
return realm.isCookieLoginAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
|
||||
realm.setCookieLoginAllowed(cookieLoginAllowed);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistrationAllowed() {
|
||||
return realm.isRegistrationAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegistrationAllowed(boolean registrationAllowed) {
|
||||
realm.setRegistrationAllowed(registrationAllowed);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTokenLifespan() {
|
||||
return realm.getTokenLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTokenLifespan(int tokenLifespan) {
|
||||
realm.setTokenLifespan(tokenLifespan);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessCodeLifespan() {
|
||||
return realm.getAccessCodeLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessCodeLifespan(int accessCodeLifespan) {
|
||||
realm.setAccessCodeLifespan(accessCodeLifespan);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPublicKeyPem() {
|
||||
return realm.getPublicKeyPem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicKeyPem(String publicKeyPem) {
|
||||
realm.setPublicKeyPem(publicKeyPem);
|
||||
this.publicKey = null;
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrivateKeyPem() {
|
||||
return realm.getPrivateKeyPem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrivateKeyPem(String privateKeyPem) {
|
||||
realm.setPrivateKeyPem(privateKeyPem);
|
||||
this.privateKey = null;
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublicKey() {
|
||||
if (publicKey != null) return publicKey;
|
||||
String pem = getPublicKeyPem();
|
||||
if (pem != null) {
|
||||
try {
|
||||
publicKey = PemUtils.decodePublicKey(pem);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicKey(PublicKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
StringWriter writer = new StringWriter();
|
||||
PEMWriter pemWriter = new PEMWriter(writer);
|
||||
try {
|
||||
pemWriter.writeObject(publicKey);
|
||||
pemWriter.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String s = writer.toString();
|
||||
setPublicKeyPem(PemUtils.removeBeginEnd(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey() {
|
||||
if (privateKey != null) return privateKey;
|
||||
String pem = getPrivateKeyPem();
|
||||
if (pem != null) {
|
||||
try {
|
||||
privateKey = PemUtils.decodePrivateKey(pem);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrivateKey(PrivateKey privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
StringWriter writer = new StringWriter();
|
||||
PEMWriter pemWriter = new PEMWriter(writer);
|
||||
try {
|
||||
pemWriter.writeObject(privateKey);
|
||||
pemWriter.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String s = writer.toString();
|
||||
setPrivateKeyPem(PemUtils.removeBeginEnd(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAdapter getUser(String name) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("loginName", name)
|
||||
.andCondition("realmId", getOid())
|
||||
.build();
|
||||
UserData user = noSQL.loadSingleObject(UserData.class, query);
|
||||
|
||||
if (user == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new UserAdapter(user, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAdapter addUser(String username) {
|
||||
if (getUser(username) != null) {
|
||||
throw new IllegalArgumentException("User " + username + " already exists");
|
||||
}
|
||||
|
||||
UserData userData = new UserData();
|
||||
userData.setLoginName(username);
|
||||
userData.setEnabled(true);
|
||||
userData.setRealmId(getOid());
|
||||
|
||||
noSQL.saveObject(userData);
|
||||
return new UserAdapter(userData, noSQL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter getRole(String name) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("name", name)
|
||||
.andCondition("realmId", getOid())
|
||||
.build();
|
||||
RoleData role = noSQL.loadSingleObject(RoleData.class, query);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(role, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
if (getRole(name) != null) {
|
||||
throw new IllegalArgumentException("Role " + name + " already exists");
|
||||
}
|
||||
|
||||
RoleData roleData = new RoleData();
|
||||
roleData.setName(name);
|
||||
roleData.setRealmId(getOid());
|
||||
|
||||
noSQL.saveObject(roleData);
|
||||
return new RoleAdapter(roleData, noSQL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getRoles() {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("realmId", getOid())
|
||||
.build();
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
|
||||
List<RoleModel> result = new ArrayList<RoleModel>();
|
||||
for (RoleData role : roles) {
|
||||
result.add(new RoleAdapter(role, noSQL));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getDefaultRoles() {
|
||||
String[] defaultRoles = realm.getDefaultRoles();
|
||||
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.inCondition("_id", defaultRoles)
|
||||
.build();
|
||||
List<RoleData> defaultRolesData = noSQL.loadObjects(RoleData.class, query);
|
||||
|
||||
List<RoleModel> defaultRoleModels = new ArrayList<RoleModel>();
|
||||
for (RoleData roleData : defaultRolesData) {
|
||||
defaultRoleModels.add(new RoleAdapter(roleData, noSQL));
|
||||
}
|
||||
return defaultRoleModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDefaultRole(String name) {
|
||||
RoleModel role = getRole(name);
|
||||
if (role == null) {
|
||||
role = addRole(name);
|
||||
}
|
||||
|
||||
String[] defaultRoles = realm.getDefaultRoles();
|
||||
String[] roleIds = Utils.addItemToArray(defaultRoles, role.getId());
|
||||
|
||||
realm.setDefaultRoles(roleIds);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
// defaultRoles is array with names of roles. So we need to convert to array of ids
|
||||
String[] roleIds = new String[defaultRoles.length];
|
||||
for (int i=0 ; i<defaultRoles.length ; i++) {
|
||||
String roleName = defaultRoles[i];
|
||||
RoleModel role = getRole(roleName);
|
||||
if (role == null) {
|
||||
role = addRole(roleName);
|
||||
}
|
||||
|
||||
roleIds[i] = role.getId();
|
||||
}
|
||||
|
||||
realm.setDefaultRoles(roleIds);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel getApplicationById(String id) {
|
||||
ApplicationData appData = noSQL.loadObject(ApplicationData.class, id);
|
||||
|
||||
// Check if application belongs to this realm
|
||||
if (appData == null || !getOid().equals(appData.getRealmId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ApplicationModel model = new ApplicationAdapter(appData, noSQL);
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ApplicationModel> getResourceNameMap() {
|
||||
Map<String, ApplicationModel> resourceMap = new HashMap<String, ApplicationModel>();
|
||||
for (ApplicationModel resource : getApplications()) {
|
||||
resourceMap.put(resource.getName(), resource);
|
||||
}
|
||||
return resourceMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplicationModel> getApplications() {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("realmId", getOid())
|
||||
.build();
|
||||
List<ApplicationData> appDatas = noSQL.loadObjects(ApplicationData.class, query);
|
||||
|
||||
List<ApplicationModel> result = new ArrayList<ApplicationModel>();
|
||||
for (ApplicationData appData : appDatas) {
|
||||
result.add(new ApplicationAdapter(appData, noSQL));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel addApplication(String name) {
|
||||
UserAdapter resourceUser = addUser(name);
|
||||
|
||||
ApplicationData appData = new ApplicationData();
|
||||
appData.setName(name);
|
||||
appData.setRealmId(getOid());
|
||||
appData.setResourceUserId(resourceUser.getUser().getId());
|
||||
noSQL.saveObject(appData);
|
||||
|
||||
ApplicationModel resource = new ApplicationAdapter(appData, noSQL);
|
||||
resource.addRole("*");
|
||||
resource.addScope(resourceUser, "*");
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(UserModel user, RoleModel role) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
|
||||
String[] roleIds = userData.getRoleIds();
|
||||
String roleId = role.getId();
|
||||
if (roleIds != null) {
|
||||
for (String currentId : roleIds) {
|
||||
if (roleId.equals(currentId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRole(UserModel user, RoleModel role) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
RoleData roleData = ((RoleAdapter)role).getRole();
|
||||
|
||||
String[] roleIds = userData.getRoleIds();
|
||||
roleIds = Utils.addItemToArray(roleIds, roleData.getId());
|
||||
userData.setRoleIds(roleIds);
|
||||
|
||||
noSQL.saveObject(userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRoleMappings(UserModel user) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
String[] roleIds = userData.getRoleIds();
|
||||
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.inCondition("_id", roleIds)
|
||||
.build();
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
// TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
|
||||
for (RoleData role : roles) {
|
||||
if (getOid().equals(role.getRealmId())) {
|
||||
result.add(role.getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScope(UserModel agent, String roleName) {
|
||||
UserData userData = ((UserAdapter)agent).getUser();
|
||||
RoleAdapter role = getRole(roleName);
|
||||
if (role == null) {
|
||||
throw new RuntimeException("Role not found");
|
||||
}
|
||||
RoleData roleData = role.getRole();
|
||||
|
||||
String[] scopeIds = userData.getScopeIds();
|
||||
scopeIds = Utils.addItemToArray(scopeIds, roleData.getId());
|
||||
userData.setScopeIds(scopeIds);
|
||||
|
||||
noSQL.saveObject(userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope(UserModel agent) {
|
||||
UserData userData = ((UserAdapter)agent).getUser();
|
||||
String[] scopeIds = userData.getScopeIds();
|
||||
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.inCondition("_id", scopeIds)
|
||||
.build();
|
||||
List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
|
||||
// TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
|
||||
for (RoleData role : roles) {
|
||||
if (getOid().equals(role.getRealmId())) {
|
||||
result.add(role.getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRealmAdmin(UserModel agent) {
|
||||
String[] realmAdmins = realm.getRealmAdmins();
|
||||
String userId = ((UserAdapter)agent).getUser().getId();
|
||||
return Utils.contains(realmAdmins, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRealmAdmin(UserModel agent) {
|
||||
UserData userData = ((UserAdapter)agent).getUser();
|
||||
|
||||
String[] currentAdmins = realm.getRealmAdmins();
|
||||
String[] newAdmins = Utils.addItemToArray(currentAdmins, userData.getId());
|
||||
|
||||
realm.setRealmAdmins(newAdmins);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id) {
|
||||
RoleData role = noSQL.loadObject(RoleData.class, id);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(role, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(UserModel user, String role) {
|
||||
RoleModel roleModel = getRole(role);
|
||||
return hasRole(user, roleModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredCredential(String cred) {
|
||||
RequiredCredentialModel credentialModel = initRequiredCredentialModel(cred);
|
||||
addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_USER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredResourceCredential(String type) {
|
||||
RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
|
||||
addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_RESOURCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredOAuthClientCredential(String type) {
|
||||
RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
|
||||
addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
|
||||
}
|
||||
|
||||
protected void addRequiredCredential(RequiredCredentialModel credentialModel, int clientType) {
|
||||
RequiredCredentialData credData = new RequiredCredentialData();
|
||||
credData.setType(credentialModel.getType());
|
||||
credData.setFormLabel(credentialModel.getFormLabel());
|
||||
credData.setInput(credentialModel.isInput());
|
||||
credData.setSecret(credentialModel.isSecret());
|
||||
|
||||
credData.setRealmId(getOid());
|
||||
credData.setClientType(clientType);
|
||||
|
||||
noSQL.saveObject(credData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRequiredCredentials(Set<String> creds) {
|
||||
List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_USER);
|
||||
updateRequiredCredentials(creds, credsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRequiredApplicationCredentials(Set<String> creds) {
|
||||
List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_RESOURCE);
|
||||
updateRequiredCredentials(creds, credsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRequiredOAuthClientCredentials(Set<String> creds) {
|
||||
List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
|
||||
updateRequiredCredentials(creds, credsData);
|
||||
}
|
||||
|
||||
protected void updateRequiredCredentials(Set<String> creds, List<RequiredCredentialData> credsData) {
|
||||
Set<String> already = new HashSet<String>();
|
||||
for (RequiredCredentialData data : credsData) {
|
||||
if (!creds.contains(data.getType())) {
|
||||
noSQL.removeObject(data);
|
||||
} else {
|
||||
already.add(data.getType());
|
||||
}
|
||||
}
|
||||
for (String cred : creds) {
|
||||
// TODO
|
||||
System.out.println("updating cred: " + cred);
|
||||
// logger.info("updating cred: " + cred);
|
||||
if (!already.contains(cred)) {
|
||||
addRequiredCredential(cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredCredentials() {
|
||||
return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_USER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredApplicationCredentials() {
|
||||
return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_RESOURCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
|
||||
return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
|
||||
}
|
||||
|
||||
protected List<RequiredCredentialModel> getRequiredCredentials(int credentialType) {
|
||||
List<RequiredCredentialData> credsData = getRequiredCredentialsData(credentialType);
|
||||
|
||||
List<RequiredCredentialModel> result = new ArrayList<RequiredCredentialModel>();
|
||||
for (RequiredCredentialData data : credsData) {
|
||||
RequiredCredentialModel model = new RequiredCredentialModel();
|
||||
model.setFormLabel(data.getFormLabel());
|
||||
model.setInput(data.isInput());
|
||||
model.setSecret(data.isSecret());
|
||||
model.setType(data.getType());
|
||||
|
||||
result.add(model);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<RequiredCredentialData> getRequiredCredentialsData(int credentialType) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("realmId", getOid())
|
||||
.andCondition("clientType", credentialType)
|
||||
.build();
|
||||
return noSQL.loadObjects(RequiredCredentialData.class, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePassword(UserModel user, String password) {
|
||||
Credentials.Status status = passwordCredentialHandler.validate(noSQL, ((UserAdapter)user).getUser(), password);
|
||||
return status == Credentials.Status.VALID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateTOTP(UserModel user, String password, String token) {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCredential(UserModel user, UserCredentialModel cred) {
|
||||
if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
|
||||
passwordCredentialHandler.update(noSQL, ((UserAdapter)user).getUser(), cred.getValue(), null, null);
|
||||
} else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
|
||||
// TODO
|
||||
// TOTPCredential totp = new TOTPCredential(cred.getValue());
|
||||
// totp.setDevice(cred.getDevice());
|
||||
// idm.updateCredential(((UserAdapter)user).getUser(), totp);
|
||||
} else if (cred.getType().equals(CredentialRepresentation.CLIENT_CERT)) {
|
||||
// TODO
|
||||
// X509Certificate cert = null;
|
||||
// try {
|
||||
// cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// X509CertificateCredentials creds = new X509CertificateCredentials(cert);
|
||||
// idm.updateCredential(((UserAdapter)user).getUser(), creds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("socialProvider", socialLink.getSocialProvider())
|
||||
.andCondition("socialUsername", socialLink.getSocialUsername())
|
||||
.build();
|
||||
SocialLinkData socialLinkData = noSQL.loadSingleObject(SocialLinkData.class, query);
|
||||
|
||||
if (socialLinkData == null) {
|
||||
return null;
|
||||
} else {
|
||||
UserData userData = noSQL.loadObject(UserData.class, socialLinkData.getUserId());
|
||||
// TODO: Add some checking if userData exists and programmatically remove binding if it doesn't? (There are more similar places where this should be handled)
|
||||
return new UserAdapter(userData, noSQL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocialLinkModel> getSocialLinks(UserModel user) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
String userId = userData.getId();
|
||||
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("userId", userId)
|
||||
.build();
|
||||
List<SocialLinkData> dbSocialLinks = noSQL.loadObjects(SocialLinkData.class, query);
|
||||
|
||||
Set<SocialLinkModel> result = new HashSet<SocialLinkModel>();
|
||||
for (SocialLinkData socialLinkData : dbSocialLinks) {
|
||||
SocialLinkModel model = new SocialLinkModel(socialLinkData.getSocialProvider(), socialLinkData.getSocialUsername());
|
||||
result.add(model);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
SocialLinkData socialLinkData = new SocialLinkData();
|
||||
socialLinkData.setSocialProvider(socialLink.getSocialProvider());
|
||||
socialLinkData.setSocialUsername(socialLink.getSocialUsername());
|
||||
socialLinkData.setUserId(userData.getId());
|
||||
|
||||
noSQL.saveObject(socialLinkData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
|
||||
UserData userData = ((UserAdapter)user).getUser();
|
||||
String userId = userData.getId();
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("socialProvider", socialLink.getSocialProvider())
|
||||
.andCondition("socialUsername", socialLink.getSocialUsername())
|
||||
.andCondition("userId", userId)
|
||||
.build();
|
||||
noSQL.removeObjects(SocialLinkData.class, query);
|
||||
}
|
||||
|
||||
protected void updateRealm() {
|
||||
noSQL.saveObject(realm);
|
||||
}
|
||||
|
||||
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
|
||||
RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
|
||||
if (model == null) {
|
||||
throw new RuntimeException("Unknown credential type " + type);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.data.RoleData;
|
||||
import org.keycloak.services.models.nosql.data.UserData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.RoleData;
|
||||
|
||||
/**
|
||||
* Wrapper around RoleData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl)
|
||||
|
@ -46,4 +45,8 @@ public class RoleAdapter implements RoleModel {
|
|||
role.setName(name);
|
||||
noSQL.saveObject(role);
|
||||
}
|
||||
|
||||
public RoleData getRole() {
|
||||
return role;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package org.keycloak.services.models.nosql.adapters;
|
||||
package org.keycloak.services.models.nosql.keycloak.adapters;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.data.UserData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.UserData;
|
||||
|
||||
/**
|
||||
* Wrapper around UserData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl)
|
||||
|
@ -90,4 +90,8 @@ public class UserAdapter implements UserModel {
|
|||
public Map<String, String> getAttributes() {
|
||||
return user.getAttributes();
|
||||
}
|
||||
|
||||
public UserData getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.credentials;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQL;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
|
||||
import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.UserData;
|
||||
import org.keycloak.services.models.nosql.keycloak.data.credentials.PasswordData;
|
||||
import org.picketlink.idm.credential.Credentials;
|
||||
import org.picketlink.idm.credential.encoder.PasswordEncoder;
|
||||
import org.picketlink.idm.credential.encoder.SHAPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Defacto forked from {@link org.picketlink.idm.credential.handler.PasswordCredentialHandler}
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PasswordCredentialHandler {
|
||||
|
||||
private static final String DEFAULT_SALT_ALGORITHM = "SHA1PRNG";
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Stores a <b>stateless</b> instance of {@link org.picketlink.idm.credential.encoder.PasswordEncoder} that should be used to encode passwords.
|
||||
* </p>
|
||||
*/
|
||||
public static final String PASSWORD_ENCODER = "PASSWORD_ENCODER";
|
||||
|
||||
private PasswordEncoder passwordEncoder = new SHAPasswordEncoder(512);;
|
||||
|
||||
public void setup(Map<String, Object> options) {
|
||||
if (options != null) {
|
||||
Object providedEncoder = options.get(PASSWORD_ENCODER);
|
||||
|
||||
if (providedEncoder != null) {
|
||||
if (PasswordEncoder.class.isInstance(providedEncoder)) {
|
||||
this.passwordEncoder = (PasswordEncoder) providedEncoder;
|
||||
} else {
|
||||
throw new IllegalArgumentException("The password encoder [" + providedEncoder
|
||||
+ "] must be an instance of " + PasswordEncoder.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Credentials.Status validate(NoSQL noSQL, UserData user, String passwordToValidate) {
|
||||
Credentials.Status status = Credentials.Status.INVALID;
|
||||
|
||||
user = noSQL.loadObject(UserData.class, user.getId());
|
||||
|
||||
// If the user for the provided username cannot be found we fail validation
|
||||
if (user != null) {
|
||||
if (user.isEnabled()) {
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("userId", user.getId())
|
||||
.build();
|
||||
PasswordData passwordData = noSQL.loadSingleObject(PasswordData.class, query);
|
||||
|
||||
// If the stored hash is null we automatically fail validation
|
||||
if (passwordData != null) {
|
||||
if (!isCredentialExpired(passwordData.getExpiryDate())) {
|
||||
|
||||
boolean matches = this.passwordEncoder.verify(saltPassword(passwordToValidate, passwordData.getSalt()), passwordData.getEncodedHash());
|
||||
|
||||
if (matches) {
|
||||
status = Credentials.Status.VALID;
|
||||
}
|
||||
} else {
|
||||
status = Credentials.Status.EXPIRED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status = Credentials.Status.ACCOUNT_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public void update(NoSQL noSQL, UserData user, String password,
|
||||
Date effectiveDate, Date expiryDate) {
|
||||
|
||||
// Try to look if user already has password
|
||||
NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class)
|
||||
.andCondition("userId", user.getId())
|
||||
.build();
|
||||
|
||||
PasswordData passwordData = noSQL.loadSingleObject(PasswordData.class, query);
|
||||
if (passwordData == null) {
|
||||
passwordData = new PasswordData();
|
||||
}
|
||||
|
||||
String passwordSalt = generateSalt();
|
||||
|
||||
passwordData.setSalt(passwordSalt);
|
||||
passwordData.setEncodedHash(this.passwordEncoder.encode(saltPassword(password, passwordSalt)));
|
||||
|
||||
if (effectiveDate != null) {
|
||||
passwordData.setEffectiveDate(effectiveDate);
|
||||
}
|
||||
|
||||
passwordData.setExpiryDate(expiryDate);
|
||||
|
||||
passwordData.setUserId(user.getId());
|
||||
|
||||
noSQL.saveObject(passwordData);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Salt the give <code>rawPassword</code> with the specified <code>salt</code> value.
|
||||
* </p>
|
||||
*
|
||||
* @param rawPassword
|
||||
* @param salt
|
||||
* @return
|
||||
*/
|
||||
private String saltPassword(String rawPassword, String salt) {
|
||||
return salt + rawPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Generates a random string to be used as a salt for passwords.
|
||||
* </p>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String generateSalt() {
|
||||
// TODO: always returns same salt (See https://issues.jboss.org/browse/PLINK-258)
|
||||
/*SecureRandom pseudoRandom = null;
|
||||
|
||||
try {
|
||||
pseudoRandom = SecureRandom.getInstance(DEFAULT_SALT_ALGORITHM);
|
||||
pseudoRandom.setSeed(1024);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Error getting SecureRandom instance: " + DEFAULT_SALT_ALGORITHM, e);
|
||||
}
|
||||
|
||||
return String.valueOf(pseudoRandom.nextLong());*/
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public static boolean isCredentialExpired(Date expiryDate) {
|
||||
return expiryDate != null && new Date().compareTo(expiryDate) > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.credentials;
|
||||
|
||||
/**
|
||||
* Defacto forked from {@link org.picketlink.idm.credential.handler.TOTPCredentialHandler}
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class TOTPCredentialHandler extends PasswordCredentialHandler {
|
||||
|
||||
// TODO: implement
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.data;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLId;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLObject;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NoSQLCollection(collectionName = "applications")
|
||||
public class ApplicationData implements NoSQLObject {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private boolean surrogateAuthRequired;
|
||||
private String managementUrl;
|
||||
|
||||
private String resourceUserId;
|
||||
private String realmId;
|
||||
|
||||
@NoSQLId
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
|
||||
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getManagementUrl() {
|
||||
return managementUrl;
|
||||
}
|
||||
|
||||
public void setManagementUrl(String managementUrl) {
|
||||
this.managementUrl = managementUrl;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getResourceUserId() {
|
||||
return resourceUserId;
|
||||
}
|
||||
|
||||
public void setResourceUserId(String resourceUserId) {
|
||||
this.resourceUserId = resourceUserId;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,8 @@
|
|||
package org.keycloak.services.models.nosql.data;
|
||||
package org.keycloak.services.models.nosql.keycloak.data;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
|
@ -11,6 +15,8 @@ import org.keycloak.services.models.nosql.api.NoSQLObject;
|
|||
@NoSQLCollection(collectionName = "realms")
|
||||
public class RealmData implements NoSQLObject {
|
||||
|
||||
private String oid;
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
|
@ -23,9 +29,21 @@ public class RealmData implements NoSQLObject {
|
|||
private int accessCodeLifespan;
|
||||
private String publicKeyPem;
|
||||
private String privateKeyPem;
|
||||
|
||||
private String[] defaultRoles;
|
||||
private String[] realmAdmins;
|
||||
|
||||
@NoSQLId
|
||||
public String getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(String oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
// TODO: Is ID really needed? It seems that it exists just to workaround picketlink...
|
||||
@NoSQLField
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -141,4 +159,14 @@ public class RealmData implements NoSQLObject {
|
|||
public void setDefaultRoles(String[] defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String[] getRealmAdmins() {
|
||||
return realmAdmins;
|
||||
}
|
||||
|
||||
public void setRealmAdmins(String[] realmAdmins) {
|
||||
this.realmAdmins = realmAdmins;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.data;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLId;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLObject;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NoSQLCollection(collectionName = "requiredCredentials")
|
||||
public class RequiredCredentialData implements NoSQLObject {
|
||||
|
||||
public static final int CLIENT_TYPE_USER = 1;
|
||||
public static final int CLIENT_TYPE_RESOURCE = 2;
|
||||
public static final int CLIENT_TYPE_OAUTH_RESOURCE = 3;
|
||||
|
||||
private String id;
|
||||
|
||||
private String type;
|
||||
private boolean input;
|
||||
private boolean secret;
|
||||
private String formLabel;
|
||||
|
||||
private String realmId;
|
||||
private int clientType;
|
||||
|
||||
@NoSQLId
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public boolean isInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void setInput(boolean input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public boolean isSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(boolean secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getFormLabel() {
|
||||
return formLabel;
|
||||
}
|
||||
|
||||
public void setFormLabel(String formLabel) {
|
||||
this.formLabel = formLabel;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public int getClientType() {
|
||||
return clientType;
|
||||
}
|
||||
|
||||
public void setClientType(int clientType) {
|
||||
this.clientType = clientType;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.services.models.nosql.data;
|
||||
package org.keycloak.services.models.nosql.keycloak.data;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
|
@ -0,0 +1,54 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.data;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLId;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLObject;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NoSQLCollection(collectionName = "socialLinks")
|
||||
public class SocialLinkData implements NoSQLObject {
|
||||
|
||||
private String id;
|
||||
private String socialUsername;
|
||||
private String socialProvider;
|
||||
private String userId;
|
||||
|
||||
@NoSQLId
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getSocialUsername() {
|
||||
return socialUsername;
|
||||
}
|
||||
|
||||
public void setSocialUsername(String socialUsername) {
|
||||
this.socialUsername = socialUsername;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getSocialProvider() {
|
||||
return socialProvider;
|
||||
}
|
||||
|
||||
public void setSocialProvider(String socialProvider) {
|
||||
this.socialProvider = socialProvider;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.services.models.nosql.data;
|
||||
package org.keycloak.services.models.nosql.keycloak.data;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.AbstractAttributedNoSQLObject;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
|
@ -20,6 +20,9 @@ public class UserData extends AbstractAttributedNoSQLObject {
|
|||
|
||||
private String realmId;
|
||||
|
||||
private String[] roleIds;
|
||||
private String[] scopeIds;
|
||||
|
||||
@NoSQLId
|
||||
public String getId() {
|
||||
return id;
|
||||
|
@ -82,4 +85,22 @@ public class UserData extends AbstractAttributedNoSQLObject {
|
|||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String[] getRoleIds() {
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
public void setRoleIds(String[] roleIds) {
|
||||
this.roleIds = roleIds;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String[] getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void setScopeIds(String[] scopeIds) {
|
||||
this.scopeIds = scopeIds;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.keycloak.services.models.nosql.keycloak.data.credentials;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.keycloak.services.models.nosql.api.NoSQLCollection;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLField;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLId;
|
||||
import org.keycloak.services.models.nosql.api.NoSQLObject;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@NoSQLCollection(collectionName = "passwordCredentials")
|
||||
public class PasswordData implements NoSQLObject {
|
||||
|
||||
private String id;
|
||||
private Date effectiveDate = new Date();
|
||||
private Date expiryDate;
|
||||
private String encodedHash;
|
||||
private String salt;
|
||||
|
||||
private String userId;
|
||||
|
||||
@NoSQLId
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public Date getEffectiveDate() {
|
||||
return effectiveDate;
|
||||
}
|
||||
|
||||
public void setEffectiveDate(Date effectiveDate) {
|
||||
this.effectiveDate = effectiveDate;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public Date getExpiryDate() {
|
||||
return expiryDate;
|
||||
}
|
||||
|
||||
public void setExpiryDate(Date expiryDate) {
|
||||
this.expiryDate = expiryDate;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getEncodedHash() {
|
||||
return encodedHash;
|
||||
}
|
||||
|
||||
public void setEncodedHash(String encodedHash) {
|
||||
this.encodedHash = encodedHash;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(String salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
@NoSQLField
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
}
|
|
@ -8,8 +8,9 @@ import org.keycloak.models.picketlink.PicketlinkKeycloakSessionFactory;
|
|||
import org.keycloak.models.picketlink.mappings.ApplicationEntity;
|
||||
import org.keycloak.models.picketlink.mappings.RealmEntity;
|
||||
import org.keycloak.services.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.models.nosql.adapters.MongoDBSessionFactory;
|
||||
import org.keycloak.services.models.nosql.keycloak.adapters.MongoDBSessionFactory;
|
||||
import org.keycloak.services.models.picketlink.PicketlinkKeycloakSession;
|
||||
import org.keycloak.services.models.picketlink.PicketlinkKeycloakSessionFactory;
|
||||
import org.keycloak.services.models.picketlink.mappings.ApplicationEntity;
|
||||
import org.keycloak.services.models.picketlink.mappings.RealmEntity;
|
||||
import org.keycloak.social.SocialRequestManager;
|
||||
|
@ -24,6 +25,8 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import javax.ws.rs.core.Application;
|
||||
import javax.ws.rs.core.Context;
|
||||
import java.util.HashSet;
|
||||
|
@ -61,7 +64,7 @@ public class KeycloakApplication extends Application {
|
|||
public static KeycloakSessionFactory buildSessionFactory() {
|
||||
// EntityManagerFactory emf = Persistence.createEntityManagerFactory("keycloak-identity-store");
|
||||
// return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager());
|
||||
return new MongoDBSessionFactory("localhost", 27017, "keycloak");
|
||||
return new MongoDBSessionFactory("localhost", 27017, "keycloak", true);
|
||||
}
|
||||
|
||||
public KeycloakSessionFactory getFactory() {
|
||||
|
|
Loading…
Reference in a new issue