Merge pull request #505 from stianst/master

Added hybrid model provider with jpa stores
This commit is contained in:
Stian Thorgersen 2014-07-08 12:04:36 +01:00
commit 6775af302a
102 changed files with 9216 additions and 127 deletions

30
model/hybrid/pom.xml Executable file
View file

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-hybrid</artifactId>
<name>Keycloak Model Hybrid</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,140 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.realms.Application;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ApplicationAdapter extends ClientAdapter implements ApplicationModel {
private Application application;
ApplicationAdapter(HybridModelProvider provider, Application application) {
super(provider, application);
this.application = application;
}
Application getApplication() {
return application;
}
@Override
public void updateApplication() {
application.updateApplication();
}
@Override
public String getName() {
return application.getName();
}
@Override
public void setName(String name) {
application.setName(name);
}
@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 String getBaseUrl() {
return application.getBaseUrl();
}
@Override
public void setBaseUrl(String url) {
application.setBaseUrl(url);
}
@Override
public List<String> getDefaultRoles() {
return application.getDefaultRoles();
}
@Override
public void addDefaultRole(String name) {
if (getRole(name) == null) {
addRole(name);
}
application.addDefaultRole(name);
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
application.updateDefaultRoles(defaultRoles);
}
@Override
public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
return provider.mappings().wrap(application.getApplicationScopeMappings(provider.mappings().unwrap(client)));
}
@Override
public boolean isBearerOnly() {
return application.isBearerOnly();
}
@Override
public void setBearerOnly(boolean only) {
application.setBearerOnly(only);
}
@Override
public RoleModel getRole(String name) {
return provider.mappings().wrap(application.getRole(name));
}
@Override
public RoleModel addRole(String name) {
return addRole(KeycloakModelUtils.generateId(), name);
}
@Override
public RoleModel addRole(String id, String name) {
return provider.mappings().wrap(application.addRole(id, name));
}
@Override
public boolean removeRole(RoleModel role) {
if (application.removeRole(provider.mappings().unwrap(role))) {
provider.users().onRoleRemoved(role.getId());
return true;
} else {
return false;
}
}
@Override
public Set<RoleModel> getRoles() {
return provider.mappings().wrap(application.getRoles());
}
}

View file

@ -0,0 +1,202 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.realms.Client;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public abstract class ClientAdapter implements ClientModel {
protected HybridModelProvider provider;
protected Client client;
ClientAdapter(HybridModelProvider provider, Client client) {
this.provider = provider;
this.client = client;
}
@Override
public String getId() {
return client.getId();
}
@Override
public String getClientId() {
return client.getClientId();
}
@Override
public long getAllowedClaimsMask() {
return client.getAllowedClaimsMask();
}
@Override
public void setAllowedClaimsMask(long mask) {
client.setAllowedClaimsMask(mask);
}
@Override
public Set<String> getWebOrigins() {
return client.getWebOrigins();
}
@Override
public void setWebOrigins(Set<String> webOrigins) {
client.setWebOrigins(webOrigins);
}
@Override
public void addWebOrigin(String webOrigin) {
client.addWebOrigin(webOrigin);
}
@Override
public void removeWebOrigin(String webOrigin) {
client.removeWebOrigin(webOrigin);
}
@Override
public Set<String> getRedirectUris() {
return client.getRedirectUris();
}
@Override
public void setRedirectUris(Set<String> redirectUris) {
client.setRedirectUris(redirectUris);
}
@Override
public void addRedirectUri(String redirectUri) {
client.addRedirectUri(redirectUri);
}
@Override
public void removeRedirectUri(String redirectUri) {
client.removeRedirectUri(redirectUri);
}
@Override
public boolean isEnabled() {
return client.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
client.setEnabled(enabled);
}
@Override
public boolean validateSecret(String secret) {
return client.validateSecret(secret);
}
@Override
public String getSecret() {
return client.getSecret();
}
@Override
public void setSecret(String secret) {
client.setSecret(secret);
}
@Override
public boolean isPublicClient() {
return client.isPublicClient();
}
@Override
public void setPublicClient(boolean flag) {
client.setPublicClient(flag);
}
@Override
public boolean isDirectGrantsOnly() {
return client.isDirectGrantsOnly();
}
@Override
public void setDirectGrantsOnly(boolean flag) {
client.setDirectGrantsOnly(flag);
}
@Override
public Set<RoleModel> getScopeMappings() {
return provider.mappings().wrap(client.getScopeMappings());
}
@Override
public void addScopeMapping(RoleModel role) {
if (!hasScope(role)) {
client.addScopeMapping(provider.mappings().unwrap(role));
}
}
@Override
public void deleteScopeMapping(RoleModel role) {
client.deleteScopeMapping(provider.mappings().unwrap(role));
}
@Override
public Set<RoleModel> getRealmScopeMappings() {
return provider.mappings().wrap(client.getRealmScopeMappings());
}
@Override
public boolean hasScope(RoleModel role) {
Set<RoleModel> roles = getScopeMappings();
if (roles.contains(role)) {
return true;
}
for (RoleModel mapping : roles) {
if (mapping.hasRole(role)) {
return true;
}
}
return false;
}
@Override
public RealmModel getRealm() {
return provider.mappings().wrap(client.getRealm());
}
@Override
public int getNotBefore() {
return client.getNotBefore();
}
@Override
public void setNotBefore(int notBefore) {
client.setNotBefore(notBefore);
}
@Override
public Set<UserSessionModel> getUserSessions() {
return provider.mappings().wrapSessions(getRealm(), provider.sessions().getUserSessionsByClient(client.getRealm().getId(), client.getId()));
}
@Override
public int getActiveUserSessions() {
return provider.sessions().getActiveUserSessions(client.getRealm().getId(), client.getId());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!this.getClass().equals(o.getClass())) return false;
ClientAdapter that = (ClientAdapter) o;
return that.getId().equals(getId());
}
}

View file

@ -0,0 +1,65 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.KeycloakTransaction;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class HybridKeycloakTransaction implements KeycloakTransaction {
private KeycloakTransaction[] txs;
public HybridKeycloakTransaction(KeycloakTransaction... txs) {
this.txs = txs;
}
@Override
public void begin() {
for (KeycloakTransaction tx : txs) {
tx.begin();
}
}
@Override
public void commit() {
// TODO What do we do if one tx fails?
for (KeycloakTransaction tx : txs) {
tx.commit();
}
}
@Override
public void rollback() {
for (KeycloakTransaction tx : txs) {
tx.rollback();
}
}
@Override
public void setRollbackOnly() {
for (KeycloakTransaction tx : txs) {
tx.setRollbackOnly();
}
}
@Override
public boolean getRollbackOnly() {
for (KeycloakTransaction tx : txs) {
if (tx.getRollbackOnly()) {
return true;
}
}
return false;
}
@Override
public boolean isActive() {
for (KeycloakTransaction tx : txs) {
if (tx.isActive()) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,256 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.ModelProvider;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.sessions.SessionProvider;
import org.keycloak.models.users.UserProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.util.Time;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class HybridModelProvider implements ModelProvider {
private final Mappings mappings;
private KeycloakSession session;
private HybridKeycloakTransaction tx;
public HybridModelProvider(KeycloakSession session) {
this.session = session;
this.mappings = new Mappings(this);
}
@Override
public KeycloakTransaction getTransaction() {
if (tx == null) {
tx = new HybridKeycloakTransaction(realms().getTransaction(), users().getTransaction(), sessions().getTransaction());
}
return tx;
}
Mappings mappings() {
return mappings;
}
SessionProvider sessions() {
return session.getProvider(SessionProvider.class);
}
UserProvider users() {
return session.getProvider(UserProvider.class);
}
RealmProvider realms() {
return session.getProvider(RealmProvider.class);
}
@Override
public RealmModel createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
}
@Override
public RealmModel createRealm(String id, String name) {
return mappings.wrap(realms().createRealm(id, name));
}
@Override
public RealmModel getRealm(String id) {
return mappings.wrap(realms().getRealm(id));
}
@Override
public RealmModel getRealmByName(String name) {
return mappings.wrap(realms().getRealmByName(name));
}
@Override
public UserModel getUserById(String id, RealmModel realm) {
return mappings.wrap(realm, users().getUserById(id, realm.getId()));
}
@Override
public UserModel getUserByUsername(String username, RealmModel realm) {
return mappings.wrap(realm, users().getUserByUsername(username, realm.getId()));
}
@Override
public UserModel getUserByEmail(String email, RealmModel realm) {
return mappings.wrap(realm, users().getUserByEmail(email, realm.getId()));
}
@Override
public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
return mappings.wrap(realm, users().getUserByAttribute("keycloak.socialLink." + socialLink.getSocialProvider() + ".userId", socialLink.getSocialUserId(), realm.getId()));
}
@Override
public List<UserModel> getUsers(RealmModel realm) {
return mappings.wrapUsers(realm, users().getUsers(realm.getId()));
}
@Override
public List<UserModel> searchForUser(String search, RealmModel realm) {
return mappings.wrapUsers(realm, users().searchForUser(search, realm.getId()));
}
@Override
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
return mappings.wrapUsers(realm, users().searchForUserByAttributes(attributes, realm.getId()));
}
@Override
public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
Set<SocialLinkModel> links = new HashSet<SocialLinkModel>();
for (Map.Entry<String, String> e : user.getAttributes().entrySet()) {
if (e.getKey().matches("keycloak\\.socialLink\\..*\\.userId")) {
String provider = e.getKey().split("\\.")[2];
SocialLinkModel link = new SocialLinkModel();
link.setSocialProvider(provider);
link.setSocialUserId(e.getValue());
link.setSocialUsername(user.getAttribute("keycloak.socialLink." + provider + ".username"));
links.add(link);
}
}
return links;
}
@Override
public SocialLinkModel getSocialLink(UserModel user, String provider, RealmModel realm) {
if (user.getAttribute("keycloak.socialLink." + provider + ".userId") != null) {
SocialLinkModel link = new SocialLinkModel();
link.setSocialProvider(provider);
link.setSocialUserId(user.getAttribute("keycloak.socialLink." + provider + ".userId"));
link.setSocialUsername(user.getAttribute("keycloak.socialLink." + provider + ".username"));
return link;
} else {
return null;
}
}
@Override
public RoleModel getRoleById(String id, RealmModel realm) {
return mappings.wrap(realms().getRoleById(id, realm.getId()));
}
@Override
public ApplicationModel getApplicationById(String id, RealmModel realm) {
return mappings.wrap(realms().getApplicationById(id, realm.getId()));
}
@Override
public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
return mappings.wrap(realms().getOAuthClientById(id, realm.getId()));
}
@Override
public List<RealmModel> getRealms() {
return mappings.wrap(realms().getRealms());
}
@Override
public boolean removeRealm(String id) {
if (realms().removeRealm(id)) {
users().onRealmRemoved(id);
return true;
} else {
return false;
}
}
@Override
public UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm) {
return mappings.wrap(sessions().getUserLoginFailure(username, realm.getId()));
}
@Override
public UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm) {
return mappings.wrap(sessions().addUserLoginFailure(username, realm.getId()));
}
@Override
public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
return mappings.wrapLoginFailures(sessions().getAllUserLoginFailures(realm.getId()));
}
@Override
public UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress) {
return mappings.wrap(realm, sessions().createUserSession(realm.getId(), KeycloakModelUtils.generateId(), user.getId(), ipAddress));
}
@Override
public UserSessionModel getUserSession(String id, RealmModel realm) {
return mappings.wrap(realm, sessions().getUserSession(id, realm.getId()));
}
@Override
public List<UserSessionModel> getUserSessions(UserModel user, RealmModel realm) {
return mappings.wrapSessions(realm, sessions().getUserSessionsByUser(user.getId(), realm.getId()));
}
@Override
public Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
return mappings.wrapSessions(realm, sessions().getUserSessionsByClient(realm.getId(), client.getClientId()));
}
@Override
public int getActiveUserSessions(RealmModel realm, ClientModel client) {
return sessions().getActiveUserSessions(realm.getId(), client.getClientId());
}
@Override
public void removeUserSession(UserSessionModel session) {
sessions().removeUserSession(mappings.unwrap(session));
}
@Override
public void removeUserSessions(RealmModel realm, UserModel user) {
sessions().removeUserSessions(realm.getId(), user.getId());
}
@Override
public void removeExpiredUserSessions(RealmModel realm) {
long refreshTimeout = Time.currentTime() - realm.getSsoSessionIdleTimeout();
long sessionTimeout = Time.currentTime() - realm.getSsoSessionMaxLifespan();
sessions().removeExpiredUserSessions(realm.getId(), refreshTimeout, sessionTimeout);
}
@Override
public void removeUserSessions(RealmModel realm) {
sessions().removeUserSessions(realm.getId());
}
@Override
public void removeAllData() {
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,34 @@
package org.keycloak.models.hybrid;
import org.keycloak.Config;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelProvider;
import org.keycloak.models.ModelProviderFactory;
import org.keycloak.models.sessions.SessionProvider;
import org.keycloak.models.users.UserProvider;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class HybridModelProviderFactory implements ModelProviderFactory {
@Override
public String getId() {
return "hybrid";
}
@Override
public ModelProvider create(KeycloakSession session) {
return new HybridModelProvider(session);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,220 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.realms.Application;
import org.keycloak.models.realms.Client;
import org.keycloak.models.realms.OAuthClient;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.Role;
import org.keycloak.models.sessions.LoginFailure;
import org.keycloak.models.sessions.Session;
import org.keycloak.models.users.User;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Mappings {
private final HybridModelProvider provider;
private Map<Object, Object> mappings = new HashMap<Object, Object>();
public Mappings(HybridModelProvider provider) {
this.provider = provider;
}
public RealmModel wrap(Realm realm) {
if (realm == null) return null;
RealmAdapter adapter = (RealmAdapter) mappings.get(realm);
if (adapter == null) {
adapter = new RealmAdapter(provider, realm);
mappings.put(realm, adapter);
}
return adapter;
}
public List<RealmModel> wrap(List<Realm> realms) {
List<RealmModel> adapters = new LinkedList<RealmModel>();
for (Realm realm : realms) {
adapters.add(wrap(realm));
}
return adapters;
}
public RoleModel wrap(Role role) {
if (role == null) return null;
RoleAdapter adapter = (RoleAdapter) mappings.get(role);
if (adapter == null) {
adapter = new RoleAdapter(provider, role);
mappings.put(role, adapter);
}
return adapter;
}
public Set<RoleModel> wrap(Set<Role> roles) {
Set<RoleModel> adapters = new HashSet<RoleModel>();
for (Role role : roles) {
adapters.add(wrap(role));
}
return adapters;
}
public Role unwrap(RoleModel role) {
if (role instanceof RoleAdapter) {
return ((RoleAdapter) role).getRole();
} else {
return provider.realms().getRoleById(role.getId(), getRealm(role.getContainer()));
}
}
public ApplicationModel wrap(Application application) {
return application != null ? new ApplicationAdapter(provider, application) : null;
}
public List<ApplicationModel> wrapApps(List<Application> applications) {
List<ApplicationModel> adapters = new LinkedList<ApplicationModel>();
for (Application application : applications) {
adapters.add(wrap(application));
}
return adapters;
}
public Map<String, ApplicationModel> wrap(Map<String, Application> applications) {
Map<String, ApplicationModel> adapters = new HashMap<String, ApplicationModel>();
for (Map.Entry<String, Application> e : applications.entrySet()) {
adapters.put(e.getKey(), wrap(e.getValue()));
}
return adapters;
}
public OAuthClientModel wrap(OAuthClient client) {
return client != null ? new OAuthClientAdapter(provider, client) : null;
}
public List<OAuthClientModel> wrapClients(List<OAuthClient> clients) {
List<OAuthClientModel> adapters = new LinkedList<OAuthClientModel>();
for (OAuthClient client : clients) {
adapters.add(wrap(client));
}
return adapters;
}
public Client unwrap(ClientModel client) {
if (client == null) {
return null;
}
if (client instanceof ApplicationAdapter) {
return ((ApplicationAdapter) client).getApplication();
} else if (client instanceof OAuthClientAdapter) {
return ((OAuthClientAdapter) client).getOauthClient();
} else {
throw new IllegalArgumentException("Not a hybrid model");
}
}
public Application unwrap(ApplicationModel application) {
if (application == null) {
return null;
}
if (!(application instanceof ApplicationAdapter)) {
throw new IllegalArgumentException("Not a hybrid model");
}
return ((ApplicationAdapter) application).getApplication();
}
public UserModel wrap(RealmModel realm, User user) {
return user != null ? new UserAdapter(provider, realm, user) : null;
}
public List<UserModel> wrapUsers(RealmModel realm, List<User> users) {
List<UserModel> adapters = new LinkedList<UserModel>();
for (User user : users) {
adapters.add(wrap(realm, user));
}
return adapters;
}
public static User unwrap(UserModel user) {
if (user == null) {
return null;
}
if (!(user instanceof UserAdapter)) {
throw new IllegalArgumentException("Not a hybrid model");
}
return ((UserAdapter) user).getUser();
}
public UserSessionModel wrap(RealmModel realm, Session session) {
return session != null ? new UserSessionAdapter(provider, realm, session) : null;
}
public List<UserSessionModel> wrapSessions(RealmModel realm, List<Session> sessions) {
List<UserSessionModel> adapters = new LinkedList<UserSessionModel>();
for (Session session : sessions) {
adapters.add(wrap(realm, session));
}
return adapters;
}
public Set<UserSessionModel> wrapSessions(RealmModel realm, Set<Session> sessions) {
Set<UserSessionModel> adapters = new HashSet<UserSessionModel>();
for (Session session : sessions) {
adapters.add(wrap(realm, session));
}
return adapters;
}
public Session unwrap(UserSessionModel session) {
if (session == null) {
return null;
}
if (!(session instanceof UserSessionAdapter)) {
throw new IllegalArgumentException("Not a hybrid model");
}
return ((UserSessionAdapter) session).getSession();
}
public UsernameLoginFailureModel wrap(LoginFailure loginFailure) {
return loginFailure != null ? new UsernameLoginFailureAdapter(provider, loginFailure) : null;
}
public List<UsernameLoginFailureModel> wrapLoginFailures(List<LoginFailure> loginFailures) {
List<UsernameLoginFailureModel> adapters = new LinkedList<UsernameLoginFailureModel>();
for (LoginFailure loginFailure : loginFailures) {
adapters.add(wrap(loginFailure));
}
return adapters;
}
private String getRealm(RoleContainerModel container) {
if (container instanceof RealmModel) {
return ((RealmModel) container).getId();
} else {
return ((ApplicationModel) container).getRealm().getId();
}
}
}

View file

@ -0,0 +1,27 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.realms.OAuthClient;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class OAuthClientAdapter extends ClientAdapter implements OAuthClientModel {
private OAuthClient oauthClient;
OAuthClientAdapter(HybridModelProvider provider, OAuthClient oauthClient) {
super(provider, oauthClient);
this.oauthClient = oauthClient;
}
OAuthClient getOauthClient() {
return oauthClient;
}
@Override
public void setClientId(String id) {
oauthClient.setClientId(id);
}
}

View file

@ -0,0 +1,830 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.realms.Application;
import org.keycloak.models.realms.Client;
import org.keycloak.models.realms.OAuthClient;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.users.Credentials;
import org.keycloak.models.users.Feature;
import org.keycloak.models.users.User;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.models.utils.TimeBasedOTP;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RealmAdapter implements RealmModel {
private HybridModelProvider provider;
private Realm realm;
RealmAdapter(HybridModelProvider provider, Realm realm) {
this.provider = provider;
this.realm = realm;
}
Realm getRealm() {
return realm;
}
@Override
public String getId() {
return realm.getId();
}
@Override
public String getName() {
return realm.getName();
}
@Override
public void setName(String name) {
realm.setName(name);
}
@Override
public boolean isEnabled() {
return realm.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
realm.setEnabled(enabled);
}
@Override
public boolean isSslNotRequired() {
return realm.isSslNotRequired();
}
@Override
public void setSslNotRequired(boolean sslNotRequired) {
realm.setSslNotRequired(sslNotRequired);
}
@Override
public boolean isRegistrationAllowed() {
return realm.isRegistrationAllowed();
}
@Override
public void setRegistrationAllowed(boolean registrationAllowed) {
realm.setRegistrationAllowed(registrationAllowed);
}
@Override
public boolean isPasswordCredentialGrantAllowed() {
return realm.isPasswordCredentialGrantAllowed();
}
@Override
public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
}
@Override
public boolean isRememberMe() {
return realm.isRememberMe();
}
@Override
public void setRememberMe(boolean rememberMe) {
realm.setRememberMe(rememberMe);
}
@Override
public boolean isBruteForceProtected() {
return realm.isBruteForceProtected();
}
@Override
public void setBruteForceProtected(boolean value) {
realm.setBruteForceProtected(value);
}
@Override
public int getMaxFailureWaitSeconds() {
return realm.getMaxFailureWaitSeconds();
}
@Override
public void setMaxFailureWaitSeconds(int val) {
realm.setMaxFailureWaitSeconds(val);
}
@Override
public int getWaitIncrementSeconds() {
return realm.getWaitIncrementSeconds();
}
@Override
public void setWaitIncrementSeconds(int val) {
realm.setWaitIncrementSeconds(val);
}
@Override
public int getMinimumQuickLoginWaitSeconds() {
return realm.getMinimumQuickLoginWaitSeconds();
}
@Override
public void setMinimumQuickLoginWaitSeconds(int val) {
realm.setMinimumQuickLoginWaitSeconds(val);
}
@Override
public long getQuickLoginCheckMilliSeconds() {
return realm.getQuickLoginCheckMilliSeconds();
}
@Override
public void setQuickLoginCheckMilliSeconds(long val) {
realm.setQuickLoginCheckMilliSeconds(val);
}
@Override
public int getMaxDeltaTimeSeconds() {
return realm.getMaxDeltaTimeSeconds();
}
@Override
public void setMaxDeltaTimeSeconds(int val) {
realm.setMaxDeltaTimeSeconds(val);
}
@Override
public int getFailureFactor() {
return realm.getFailureFactor();
}
@Override
public void setFailureFactor(int failureFactor) {
realm.setFailureFactor(failureFactor);
}
@Override
public boolean isVerifyEmail() {
return realm.isVerifyEmail();
}
@Override
public void setVerifyEmail(boolean verifyEmail) {
realm.setVerifyEmail(verifyEmail);
}
@Override
public boolean isResetPasswordAllowed() {
return realm.isResetPasswordAllowed();
}
@Override
public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
realm.setResetPasswordAllowed(resetPasswordAllowed);
}
@Override
public int getSsoSessionIdleTimeout() {
return realm.getSsoSessionIdleTimeout();
}
@Override
public void setSsoSessionIdleTimeout(int seconds) {
realm.setSsoSessionIdleTimeout(seconds);
}
@Override
public int getSsoSessionMaxLifespan() {
return realm.getSsoSessionMaxLifespan();
}
@Override
public void setSsoSessionMaxLifespan(int seconds) {
realm.setSsoSessionMaxLifespan(seconds);
}
@Override
public int getAccessTokenLifespan() {
return realm.getAccessTokenLifespan();
}
@Override
public void setAccessTokenLifespan(int seconds) {
realm.setAccessTokenLifespan(seconds);
}
@Override
public int getAccessCodeLifespan() {
return realm.getAccessCodeLifespan();
}
@Override
public void setAccessCodeLifespan(int seconds) {
realm.setAccessCodeLifespan(seconds);
}
@Override
public int getAccessCodeLifespanUserAction() {
return realm.getAccessCodeLifespanUserAction();
}
@Override
public void setAccessCodeLifespanUserAction(int seconds) {
realm.setAccessCodeLifespanUserAction(seconds);
}
@Override
public String getPublicKeyPem() {
return realm.getPublicKeyPem();
}
@Override
public void setPublicKeyPem(String publicKeyPem) {
realm.setPublicKeyPem(publicKeyPem);
}
@Override
public String getPrivateKeyPem() {
return realm.getPrivateKeyPem();
}
@Override
public void setPrivateKeyPem(String privateKeyPem) {
realm.setPrivateKeyPem(privateKeyPem);
}
@Override
public PublicKey getPublicKey() {
return realm.getPublicKey();
}
@Override
public void setPublicKey(PublicKey publicKey) {
realm.setPublicKey(publicKey);
}
@Override
public PrivateKey getPrivateKey() {
return realm.getPrivateKey();
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
realm.setPrivateKey(privateKey);
}
@Override
public List<RequiredCredentialModel> getRequiredCredentials() {
return realm.getRequiredCredentials();
}
@Override
public void addRequiredCredential(String cred) {
realm.addRequiredCredential(cred);
}
@Override
public PasswordPolicy getPasswordPolicy() {
return realm.getPasswordPolicy();
}
@Override
public void setPasswordPolicy(PasswordPolicy policy) {
realm.setPasswordPolicy(policy);
}
@Override
public boolean validatePassword(UserModel userModel, String password) {
if (provider.users().supports(Feature.VERIFY_CREDENTIALS)) {
User user = provider.mappings().unwrap(userModel);
return provider.users().verifyCredentials(user, new Credentials(UserCredentialModel.PASSWORD, password));
} else {
for (UserCredentialValueModel cred : userModel.getCredentialsDirectly()) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
}
}
return false;
}
}
@Override
public boolean validateTOTP(UserModel userModel, String password, String token) {
if (provider.users().supports(Feature.VERIFY_CREDENTIALS)) {
User user = provider.mappings().unwrap(userModel);
return provider.users().verifyCredentials(user, new Credentials(UserCredentialModel.PASSWORD, password),
new Credentials(UserCredentialModel.TOTP, token));
} else {
if (!validatePassword(userModel, password)) return false;
for (UserCredentialValueModel cred : userModel.getCredentialsDirectly()) {
if (cred.getType().equals(UserCredentialModel.TOTP)) {
return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
}
}
return false;
}
}
@Override
public UserModel getUser(String name) {
return provider.getUserByUsername(name, this);
}
@Override
public UserModel getUserByEmail(String email) {
return provider.getUserByEmail(email, this);
}
@Override
public UserModel getUserById(String name) {
return provider.getUserById(name, this);
}
@Override
public UserModel addUser(String id, String username, boolean addDefaultRoles) {
if (id == null) {
id = KeycloakModelUtils.generateId();
}
Set<String> initialRoles = new HashSet<String>();
if (addDefaultRoles) {
for (String r : realm.getDefaultRoles()) {
initialRoles.add(realm.getRole(r).getId());
}
for (Application app : realm.getApplications()) {
for (String r : app.getDefaultRoles()) {
initialRoles.add(app.getRole(r).getId());
}
}
}
return provider.mappings().wrap(this, provider.users().addUser(id, username, initialRoles, realm.getId()));
}
@Override
public UserModel addUser(String username) {
return addUser(null, username, true);
}
@Override
public boolean removeUser(String name) {
return provider.users().removeUser(name, realm.getId());
}
@Override
public RoleModel getRoleById(String id) {
return provider.mappings().wrap(provider.realms().getRoleById(id, realm.getId()));
}
@Override
public List<String> getDefaultRoles() {
return realm.getDefaultRoles();
}
@Override
public void addDefaultRole(String name) {
if (getRole(name) == null) {
addRole(name);
}
realm.addDefaultRole(name);
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
for (String name : defaultRoles) {
if (getRole(name) == null) {
addRole(name);
}
}
realm.updateDefaultRoles(defaultRoles);
}
@Override
public ClientModel findClient(String clientId) {
Client client = realm.findClient(clientId);
if (client instanceof Application) {
return provider.mappings().wrap((Application) client);
} else if (client instanceof OAuthClient) {
return provider.mappings().wrap((OAuthClient) client);
} else {
throw new IllegalArgumentException("Unsupported client type");
}
}
@Override
public Map<String, ApplicationModel> getApplicationNameMap() {
return provider.mappings().wrap(realm.getApplicationNameMap());
}
@Override
public List<ApplicationModel> getApplications() {
return provider.mappings().wrapApps(realm.getApplications());
}
@Override
public ApplicationModel addApplication(String name) {
return addApplication(KeycloakModelUtils.generateId(), name);
}
@Override
public ApplicationModel addApplication(String id, String name) {
return provider.mappings().wrap(realm.addApplication(id, name));
}
@Override
public boolean removeApplication(String id) {
Application application = provider.realms().getApplicationById(id, realm.getId());
if (application != null) {
return realm.removeApplication(application);
} else {
return false;
}
}
@Override
public ApplicationModel getApplicationById(String id) {
return provider.getApplicationById(id, this);
}
@Override
public ApplicationModel getApplicationByName(String name) {
return provider.mappings().wrap(realm.getApplicationByName(name));
}
@Override
public void updateRequiredCredentials(Set<String> creds) {
realm.updateRequiredCredentials(creds);
}
@Override
public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
return provider.getUserBySocialLink(socialLink, this);
}
@Override
public Set<SocialLinkModel> getSocialLinks(UserModel user) {
return provider.getSocialLinks(user, this);
}
@Override
public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
return provider.getSocialLink(user, socialProvider, this);
}
@Override
public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
user.setAttribute("keycloak.socialLink." + socialLink.getSocialProvider() + ".userId", socialLink.getSocialUserId());
user.setAttribute("keycloak.socialLink." + socialLink.getSocialProvider() + ".username", socialLink.getSocialUsername());
}
@Override
public boolean removeSocialLink(UserModel user, String socialProvider) {
if (user.getAttribute("keycloak.socialLink." + socialProvider + ".userId") != null) {
user.removeAttribute("keycloak.socialLink." + socialProvider + ".userId");
user.removeAttribute("keycloak.socialLink." + socialProvider + ".username");
return true;
} else {
return false;
}
}
@Override
public boolean isSocial() {
return realm.isSocial();
}
@Override
public void setSocial(boolean social) {
realm.setSocial(social);
}
@Override
public boolean isUpdateProfileOnInitialSocialLogin() {
return realm.isUpdateProfileOnInitialSocialLogin();
}
@Override
public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
}
@Override
public UsernameLoginFailureModel getUserLoginFailure(String username) {
return provider.getUserLoginFailure(username, this);
}
@Override
public UsernameLoginFailureModel addUserLoginFailure(String username) {
return provider.addUserLoginFailure(username, this);
}
@Override
public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
return provider.getAllUserLoginFailures(this);
}
@Override
public List<UserModel> getUsers() {
return provider.getUsers(this);
}
@Override
public List<UserModel> searchForUser(String search) {
return provider.searchForUser(search, this);
}
@Override
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
return provider.searchForUserByAttributes(attributes, this);
}
@Override
public OAuthClientModel addOAuthClient(String name) {
return addOAuthClient(KeycloakModelUtils.generateId(), name);
}
@Override
public OAuthClientModel addOAuthClient(String id, String name) {
return provider.mappings().wrap(realm.addOAuthClient(id, name));
}
@Override
public OAuthClientModel getOAuthClient(String name) {
return provider.mappings().wrap(realm.getOAuthClient(name));
}
@Override
public OAuthClientModel getOAuthClientById(String id) {
return provider.getOAuthClientById(id, this);
}
@Override
public boolean removeOAuthClient(String id) {
OAuthClient client = provider.realms().getOAuthClientById(id, realm.getId());
if (client != null) {
return realm.removeOAuthClient(client);
} else {
return false;
}
}
@Override
public List<OAuthClientModel> getOAuthClients() {
return provider.mappings().wrapClients(realm.getOAuthClients());
}
@Override
public Map<String, String> getSmtpConfig() {
return realm.getSmtpConfig();
}
@Override
public void setSmtpConfig(Map<String, String> smtpConfig) {
realm.setSmtpConfig(smtpConfig);
}
@Override
public Map<String, String> getSocialConfig() {
return realm.getSocialConfig();
}
@Override
public void setSocialConfig(Map<String, String> socialConfig) {
realm.setSocialConfig(socialConfig);
}
@Override
public Map<String, String> getLdapServerConfig() {
return realm.getLdapServerConfig();
}
@Override
public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
realm.setLdapServerConfig(ldapServerConfig);
}
@Override
public List<AuthenticationProviderModel> getAuthenticationProviders() {
return realm.getAuthenticationProviders();
}
@Override
public void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders) {
realm.setAuthenticationProviders(authenticationProviders);
}
@Override
public String getLoginTheme() {
return realm.getLoginTheme();
}
@Override
public void setLoginTheme(String name) {
realm.setLoginTheme(name);
}
@Override
public String getAccountTheme() {
return realm.getAccountTheme();
}
@Override
public void setAccountTheme(String name) {
realm.setAccountTheme(name);
}
@Override
public String getAdminTheme() {
return realm.getAdminTheme();
}
@Override
public void setAdminTheme(String name) {
realm.setAdminTheme(name);
}
@Override
public String getEmailTheme() {
return realm.getEmailTheme();
}
@Override
public void setEmailTheme(String name) {
realm.setEmailTheme(name);
}
@Override
public int getNotBefore() {
return realm.getNotBefore();
}
@Override
public void setNotBefore(int notBefore) {
realm.setNotBefore(notBefore);
}
@Override
public boolean isAuditEnabled() {
return realm.isAuditEnabled();
}
@Override
public void setAuditEnabled(boolean enabled) {
realm.setAuditEnabled(enabled);
}
@Override
public long getAuditExpiration() {
return realm.getAuditExpiration();
}
@Override
public void setAuditExpiration(long expiration) {
realm.setAuditExpiration(expiration);
}
@Override
public Set<String> getAuditListeners() {
return realm.getAuditListeners();
}
@Override
public void setAuditListeners(Set<String> listeners) {
realm.setAuditListeners(listeners);
}
@Override
public ApplicationModel getMasterAdminApp() {
return provider.mappings().wrap(realm.getMasterAdminApp());
}
@Override
public void setMasterAdminApp(ApplicationModel app) {
realm.setMasterAdminApp(provider.mappings().unwrap(app));
}
@Override
public UserSessionModel createUserSession(UserModel user, String ipAddress) {
return provider.createUserSession(this, user, ipAddress);
}
@Override
public UserSessionModel getUserSession(String id) {
return provider.getUserSession(id, this);
}
@Override
public List<UserSessionModel> getUserSessions(UserModel user) {
return provider.getUserSessions(user, this);
}
@Override
public void removeUserSession(UserSessionModel session) {
provider.removeUserSession(session);
}
@Override
public void removeUserSessions(UserModel user) {
provider.removeUserSessions(this, user);
}
@Override
public void removeExpiredUserSessions() {
provider.removeExpiredUserSessions(this);
}
@Override
public ClientModel findClientById(String id) {
Application application = provider.realms().getApplicationById(id, realm.getId());
if (application != null) {
return provider.mappings().wrap(application);
}
OAuthClient client = provider.realms().getOAuthClientById(id, realm.getId());
if (client != null) {
return provider.mappings().wrap(client);
}
return null;
}
@Override
public void removeUserSessions() {
provider.removeUserSessions(this);
}
@Override
public RoleModel getRole(String name) {
return provider.mappings().wrap(realm.getRole(name));
}
@Override
public RoleModel addRole(String name) {
return addRole(KeycloakModelUtils.generateId(), name);
}
@Override
public RoleModel addRole(String id, String name) {
return provider.mappings().wrap(realm.addRole(id, name));
}
@Override
public boolean removeRoleById(String id) {
RoleModel role = getRoleById(id);
if (role != null) {
if (role.getContainer().removeRole(role)) {
provider.users().onRoleRemoved(role.getId());
return true;
} else {
return false;
}
}
return false;
}
@Override
public boolean removeRole(RoleModel role) {
return removeRoleById(role.getId());
}
@Override
public Set<RoleModel> getRoles() {
return provider.mappings().wrap(realm.getRoles());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof RealmModel)) return false;
RealmModel that = (RealmModel) o;
return that.getId().equals(getId());
}
}

View file

@ -0,0 +1,114 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.realms.Application;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.Role;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RoleAdapter implements RoleModel {
private HybridModelProvider provider;
private Role role;
RoleAdapter(HybridModelProvider provider, Role role) {
this.provider = provider;
this.role = role;
}
Role getRole() {
return role;
}
@Override
public String getName() {
return role.getName();
}
@Override
public String getDescription() {
return role.getDescription();
}
@Override
public void setDescription(String description) {
role.setDescription(description);
}
@Override
public String getId() {
return role.getId();
}
@Override
public void setName(String name) {
role.setName(name);
}
@Override
public boolean isComposite() {
return role.isComposite();
}
@Override
public void addCompositeRole(RoleModel role) {
this.role.addCompositeRole(provider.mappings().unwrap(role));
}
@Override
public void removeCompositeRole(RoleModel role) {
this.role.removeCompositeRole(provider.mappings().unwrap(role));
}
@Override
public Set<RoleModel> getComposites() {
return provider.mappings().wrap(role.getComposites());
}
@Override
public RoleContainerModel getContainer() {
if (role.getContainer() instanceof Application) {
return provider.mappings().wrap((Application) role.getContainer());
} else if (role.getContainer() instanceof Realm) {
return provider.mappings().wrap((Realm) role.getContainer());
} else {
throw new IllegalArgumentException("Unsupported role container");
}
}
@Override
public boolean hasRole(RoleModel role) {
if (this.equals(role)) {
return true;
}
if (!isComposite()) {
return false;
}
Set<RoleModel> visited = new HashSet<RoleModel>();
return KeycloakModelUtils.searchFor(role, this, visited);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof RoleModel)) return false;
RoleModel that = (RoleModel) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
}

View file

@ -0,0 +1,325 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationLinkModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.users.Attributes;
import org.keycloak.models.users.Credentials;
import org.keycloak.models.users.Feature;
import org.keycloak.models.users.User;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserAdapter implements UserModel {
private HybridModelProvider provider;
private RealmModel realm;
private User user;
UserAdapter(HybridModelProvider provider, RealmModel realm, User user) {
this.provider = provider;
this.realm = realm;
this.user = user;
}
User getUser() {
return user;
}
@Override
public String getId() {
return user.getId();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public void setUsername(String username) {
user.setUsername(username);
}
@Override
public boolean isEnabled() {
return user.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
user.setEnabled(enabled);
}
@Override
public void setAttribute(String name, String value) {
user.setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
user.removeAttribute(name);
}
@Override
public String getAttribute(String name) {
return user.getAttribute(name);
}
@Override
public Map<String, String> getAttributes() {
return user.getAttributes();
}
@Override
public Set<RequiredAction> getRequiredActions() {
String value = user.getAttribute(Attributes.REQUIRED_ACTIONS);
if (value == null) {
return Collections.emptySet();
}
Set<RequiredAction> actions = new HashSet<RequiredAction>();
for (String a : value.substring(1, value.length() - 1).split(",")) {
actions.add(RequiredAction.valueOf(a.trim()));
}
return actions;
}
@Override
public void addRequiredAction(RequiredAction action) {
Set<RequiredAction> actions;
if (user.getAttribute(Attributes.REQUIRED_ACTIONS) == null) {
actions = new HashSet<RequiredAction>();
} else {
actions = getRequiredActions();
}
if (!actions.contains(action)) {
actions.add(action);
user.setAttribute(Attributes.REQUIRED_ACTIONS, actions.toString());
}
}
@Override
public void removeRequiredAction(RequiredAction action) {
Set<RequiredAction> actions = getRequiredActions();
if (actions.contains(action)) {
actions.remove(action);
if (actions.isEmpty()) {
user.removeAttribute(Attributes.REQUIRED_ACTIONS);
} else {
user.setAttribute(Attributes.REQUIRED_ACTIONS, actions.toString());
}
}
}
@Override
public String getFirstName() {
return user.getFirstName();
}
@Override
public void setFirstName(String firstName) {
user.setFirstName(firstName);
}
@Override
public String getLastName() {
return user.getLastName();
}
@Override
public void setLastName(String lastName) {
user.setLastName(lastName);
}
@Override
public String getEmail() {
return user.getEmail();
}
@Override
public void setEmail(String email) {
user.setEmail(email);
}
@Override
public boolean isEmailVerified() {
return isBooleanAttribute(Attributes.EMAIL_VERIFIED);
}
@Override
public void setEmailVerified(boolean verified) {
setBooleanAttribute(Attributes.EMAIL_VERIFIED, verified);
}
@Override
public boolean isTotp() {
return isBooleanAttribute(Attributes.TOTP_ENABLED);
}
@Override
public void setTotp(boolean totp) {
setBooleanAttribute(Attributes.TOTP_ENABLED, totp);
}
@Override
public void updateCredential(UserCredentialModel model) {
if (provider.users().supports(Feature.UPDATE_CREDENTIALS)) {
Credentials credentials;
if (model.getType().equals(UserCredentialModel.PASSWORD)) {
byte[] salt = getSalt();
int hashIterations = 1;
PasswordPolicy policy = realm.getPasswordPolicy();
if (policy != null) {
hashIterations = policy.getHashIterations();
if (hashIterations == -1) hashIterations = 1;
}
String value = new Pbkdf2PasswordEncoder(salt).encode(model.getValue(), hashIterations);
credentials = new Credentials(model.getType(), salt, value, hashIterations, model.getDevice());
} else {
credentials = new Credentials(model.getType(), model.getValue(), model.getDevice());
}
user.updateCredential(credentials);
} else {
throw new RuntimeException("Users store doesn't support updating credentials");
}
}
@Override
public List<UserCredentialValueModel> getCredentialsDirectly() {
if (provider.users().supports(Feature.READ_CREDENTIALS)) {
List<UserCredentialValueModel> models = new LinkedList<UserCredentialValueModel>();
for (Credentials cred : user.getCredentials()) {
UserCredentialValueModel model = new UserCredentialValueModel();
model.setType(cred.getType());
model.setValue(cred.getValue());
model.setDevice(cred.getDevice());
model.setSalt(cred.getSalt());
model.setHashIterations(cred.getHashIterations());
models.add(model);
}
return models;
} else {
throw new IllegalStateException("Users provider doesn't support reading credentials");
}
}
@Override
public void updateCredentialDirectly(UserCredentialValueModel model) {
if (provider.users().supports(Feature.UPDATE_CREDENTIALS)) {
Credentials credentials = new Credentials(model.getType(), model.getSalt(), model.getValue(), model.getHashIterations(), model.getDevice());
user.updateCredential(credentials);
} else {
throw new IllegalStateException("Users provider doesn't support updating credentials");
}
}
@Override
public AuthenticationLinkModel getAuthenticationLink() {
for (Map.Entry<String, String> e : user.getAttributes().entrySet()) {
if (e.getKey().matches("keycloak\\.authenticationLink\\..*\\.userId")) {
String provider = e.getKey().split("\\.")[2];
return new AuthenticationLinkModel(provider, e.getValue());
}
}
return null;
}
@Override
public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
Iterator<Map.Entry<String, String>> itr = user.getAttributes().entrySet().iterator();
while (itr.hasNext()) {
if (itr.next().getKey().matches("keycloak\\.authenticationLink\\..*\\.userId")) {
itr.remove();
}
}
user.setAttribute("keycloak.authenticationLink." + authenticationLink.getAuthProvider() + ".userId", authenticationLink.getAuthUserId());
}
@Override
public Set<RoleModel> getRealmRoleMappings() {
Set<RoleModel> roles = new HashSet<RoleModel>();
for (RoleModel r : getRoleMappings()) {
if (r.getContainer() instanceof RealmModel) {
roles.add(r);
}
}
return roles;
}
@Override
public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
Set<RoleModel> roles = new HashSet<RoleModel>();
for (RoleModel r : getRoleMappings()) {
if (r.getContainer() instanceof ApplicationModel && ((ApplicationModel) r.getContainer()).getId().equals(app.getId())) {
roles.add(r);
}
}
return roles;
}
@Override
public boolean hasRole(RoleModel role) {
for (RoleModel r : getRoleMappings()) {
if (r.hasRole(role)) {
return true;
}
}
return false;
}
@Override
public void grantRole(RoleModel role) {
user.grantRole(role.getId());
}
@Override
public Set<RoleModel> getRoleMappings() {
Set<RoleModel> roles = new HashSet<RoleModel>();
for (String r : user.getRoleMappings()) {
roles.add(realm.getRoleById(r));
}
return roles;
}
@Override
public void deleteRoleMapping(RoleModel role) {
user.deleteRoleMapping(role.getId());
}
private boolean isBooleanAttribute(String name) {
String v = user.getAttribute(name);
return v != null ? v.equals("true") : false;
}
private void setBooleanAttribute(String name, boolean enable) {
if (enable) {
user.setAttribute(name, "true");
} else {
user.removeAttribute(name);
}
}
}

View file

@ -0,0 +1,100 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.sessions.Session;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserSessionAdapter implements UserSessionModel {
private HybridModelProvider provider;
private RealmModel realm;
private Session session;
UserSessionAdapter(HybridModelProvider provider, RealmModel realm, Session session) {
this.provider = provider;
this.realm = realm;
this.session = session;
}
Session getSession() {
return session;
}
@Override
public String getId() {
return session.getId();
}
@Override
public void setId(String id) {
session.setId(id);
}
@Override
public UserModel getUser() {
return provider.getUserById(session.getUser(), realm);
}
@Override
public void setUser(UserModel user) {
session.setUser(user.getId());
}
@Override
public String getIpAddress() {
return session.getIpAddress();
}
@Override
public void setIpAddress(String ipAddress) {
session.setIpAddress(ipAddress);
}
@Override
public int getStarted() {
return session.getStarted();
}
@Override
public void setStarted(int started) {
session.setStarted(started);
}
@Override
public int getLastSessionRefresh() {
return session.getLastSessionRefresh();
}
@Override
public void setLastSessionRefresh(int seconds) {
session.setLastSessionRefresh(seconds);
}
@Override
public void associateClient(ClientModel client) {
session.associateClient(client.getId());
}
@Override
public List<ClientModel> getClientAssociations() {
List<ClientModel> clients = new LinkedList<ClientModel>();
for (String id : session.getClientAssociations()) {
clients.add(realm.findClientById(id));
}
return clients;
}
@Override
public void removeAssociatedClient(ClientModel client) {
session.removeAssociatedClient(client.getId());
}
}

View file

@ -0,0 +1,73 @@
package org.keycloak.models.hybrid;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.models.sessions.LoginFailure;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel {
private HybridModelProvider provider;
private LoginFailure loginFailure;
UsernameLoginFailureAdapter(HybridModelProvider provider, LoginFailure loginFailure) {
this.provider = provider;
this.loginFailure = loginFailure;
}
@Override
public String getUsername() {
return loginFailure.getUsername();
}
@Override
public int getFailedLoginNotBefore() {
return loginFailure.getFailedLoginNotBefore();
}
@Override
public void setFailedLoginNotBefore(int notBefore) {
loginFailure.setFailedLoginNotBefore(notBefore);
}
@Override
public int getNumFailures() {
return loginFailure.getNumFailures();
}
@Override
public void incrementFailures() {
loginFailure.incrementFailures();
}
@Override
public void clearFailures() {
loginFailure.clearFailures();
}
@Override
public long getLastFailure() {
return loginFailure.getLastFailure();
}
@Override
public void setLastFailure(long lastFailure) {
loginFailure.setLastFailure(lastFailure);
}
@Override
public String getLastIPFailure() {
return loginFailure.getLastIPFailure();
}
@Override
public void setLastIPFailure(String ip) {
loginFailure.setLastIPFailure(ip);
}
}

View file

@ -0,0 +1,40 @@
package org.keycloak.models.realms;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Application extends RoleContainer, Client {
void updateApplication();
String getName();
void setName(String name);
boolean isSurrogateAuthRequired();
void setSurrogateAuthRequired(boolean surrogateAuthRequired);
String getManagementUrl();
void setManagementUrl(String url);
String getBaseUrl();
void setBaseUrl(String url);
List<String> getDefaultRoles();
void addDefaultRole(String name);
void updateDefaultRoles(String[] defaultRoles);
Set<Role> getApplicationScopeMappings(Client client);
boolean isBearerOnly();
void setBearerOnly(boolean only);
}

View file

@ -0,0 +1,76 @@
package org.keycloak.models.realms;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Client {
/**
* Internal database key
*
* @return
*/
String getId();
/**
* String exposed to outside world
*
* @return
*/
String getClientId();
long getAllowedClaimsMask();
void setAllowedClaimsMask(long mask);
Set<String> getWebOrigins();
void setWebOrigins(Set<String> webOrigins);
void addWebOrigin(String webOrigin);
void removeWebOrigin(String webOrigin);
Set<String> getRedirectUris();
void setRedirectUris(Set<String> redirectUris);
void addRedirectUri(String redirectUri);
void removeRedirectUri(String redirectUri);
boolean isEnabled();
void setEnabled(boolean enabled);
boolean validateSecret(String secret);
String getSecret();
public void setSecret(String secret);
boolean isPublicClient();
void setPublicClient(boolean flag);
boolean isDirectGrantsOnly();
void setDirectGrantsOnly(boolean flag);
Set<Role> getScopeMappings();
void addScopeMapping(Role role);
void deleteScopeMapping(Role role);
Set<Role> getRealmScopeMappings();
Realm getRealm();
/**
* Time in seconds since epoc
*
* @return
*/
int getNotBefore();
void setNotBefore(int notBefore);
}

View file

@ -0,0 +1,11 @@
package org.keycloak.models.realms;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface OAuthClient extends Client {
void setClientId(String id);
}

View file

@ -0,0 +1,208 @@
package org.keycloak.models.realms;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RequiredCredentialModel;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Realm extends RoleContainer {
String getId();
String getName();
void setName(String name);
boolean isEnabled();
void setEnabled(boolean enabled);
boolean isSslNotRequired();
void setSslNotRequired(boolean sslNotRequired);
boolean isRegistrationAllowed();
void setRegistrationAllowed(boolean registrationAllowed);
boolean isPasswordCredentialGrantAllowed();
void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed);
boolean isRememberMe();
void setRememberMe(boolean rememberMe);
//--- brute force settings
boolean isBruteForceProtected();
void setBruteForceProtected(boolean value);
int getMaxFailureWaitSeconds();
void setMaxFailureWaitSeconds(int val);
int getWaitIncrementSeconds();
void setWaitIncrementSeconds(int val);
int getMinimumQuickLoginWaitSeconds();
void setMinimumQuickLoginWaitSeconds(int val);
long getQuickLoginCheckMilliSeconds();
void setQuickLoginCheckMilliSeconds(long val);
int getMaxDeltaTimeSeconds();
void setMaxDeltaTimeSeconds(int val);
int getFailureFactor();
void setFailureFactor(int failureFactor);
//--- end brute force settings
boolean isVerifyEmail();
void setVerifyEmail(boolean verifyEmail);
boolean isResetPasswordAllowed();
void setResetPasswordAllowed(boolean resetPasswordAllowed);
int getSsoSessionIdleTimeout();
void setSsoSessionIdleTimeout(int seconds);
int getSsoSessionMaxLifespan();
void setSsoSessionMaxLifespan(int seconds);
int getAccessTokenLifespan();
void setAccessTokenLifespan(int seconds);
int getAccessCodeLifespan();
void setAccessCodeLifespan(int seconds);
int getAccessCodeLifespanUserAction();
void setAccessCodeLifespanUserAction(int seconds);
String getPublicKeyPem();
void setPublicKeyPem(String publicKeyPem);
String getPrivateKeyPem();
void setPrivateKeyPem(String privateKeyPem);
PublicKey getPublicKey();
void setPublicKey(PublicKey publicKey);
PrivateKey getPrivateKey();
void setPrivateKey(PrivateKey privateKey);
List<RequiredCredentialModel> getRequiredCredentials();
void addRequiredCredential(String cred);
PasswordPolicy getPasswordPolicy();
void setPasswordPolicy(PasswordPolicy policy);
List<String> getDefaultRoles();
void addDefaultRole(String name);
void updateDefaultRoles(String[] defaultRoles);
Client findClient(String clientId);
Map<String, Application> getApplicationNameMap();
List<Application> getApplications();
Application addApplication(String id, String name);
boolean removeApplication(Application application);
Application getApplicationByName(String name);
void updateRequiredCredentials(Set<String> creds);
boolean isSocial();
void setSocial(boolean social);
boolean isUpdateProfileOnInitialSocialLogin();
void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin);
OAuthClient addOAuthClient(String id, String name);
OAuthClient getOAuthClient(String name);
boolean removeOAuthClient(OAuthClient client);
List<OAuthClient> getOAuthClients();
Map<String, String> getSmtpConfig();
void setSmtpConfig(Map<String, String> smtpConfig);
Map<String, String> getSocialConfig();
void setSocialConfig(Map<String, String> socialConfig);
Map<String, String> getLdapServerConfig();
void setLdapServerConfig(Map<String, String> ldapServerConfig);
List<AuthenticationProviderModel> getAuthenticationProviders();
void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders);
String getLoginTheme();
void setLoginTheme(String name);
String getAccountTheme();
void setAccountTheme(String name);
String getAdminTheme();
void setAdminTheme(String name);
String getEmailTheme();
void setEmailTheme(String name);
/**
* Time in seconds since epoc
*
* @return
*/
int getNotBefore();
void setNotBefore(int notBefore);
boolean removeRole(Role role);
boolean isAuditEnabled();
void setAuditEnabled(boolean enabled);
long getAuditExpiration();
void setAuditExpiration(long expiration);
Set<String> getAuditListeners();
void setAuditListeners(Set<String> listeners);
Application getMasterAdminApp();
void setMasterAdminApp(Application app);
}

View file

@ -0,0 +1,29 @@
package org.keycloak.models.realms;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.Provider;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface RealmProvider extends Provider {
Realm createRealm(String name);
Realm createRealm(String id, String name);
Realm getRealm(String id);
Realm getRealmByName(String name);
List<Realm> getRealms();
boolean removeRealm(String id);
Role getRoleById(String id, String realm);
Application getApplicationById(String id, String realm);
OAuthClient getOAuthClientById(String id, String realm);
KeycloakTransaction getTransaction();
void close();
}

View file

@ -0,0 +1,9 @@
package org.keycloak.models.realms;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface RealmProviderFactory extends ProviderFactory<RealmProvider> {
}

View file

@ -0,0 +1,27 @@
package org.keycloak.models.realms;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RealmSpi implements Spi {
@Override
public String getName() {
return "modelRealms";
}
@Override
public Class<? extends Provider> getProviderClass() {
return RealmProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return RealmProviderFactory.class;
}
}

View file

@ -0,0 +1,31 @@
package org.keycloak.models.realms;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Role {
String getId();
String getName();
void setName(String name);
String getDescription();
void setDescription(String description);
boolean isComposite();
void addCompositeRole(Role role);
void removeCompositeRole(Role role);
Set<Role> getComposites();
RoleContainer getContainer();
}

View file

@ -0,0 +1,19 @@
package org.keycloak.models.realms;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RoleContainer {
Role getRole(String name);
Set<Role> getRoles();
Role addRole(String id, String name);
boolean removeRole(Role role);
}

View file

@ -0,0 +1,29 @@
package org.keycloak.models.sessions;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface LoginFailure {
String getUsername();
int getFailedLoginNotBefore();
void setFailedLoginNotBefore(int notBefore);
int getNumFailures();
void incrementFailures();
void clearFailures();
long getLastFailure();
void setLastFailure(long lastFailure);
String getLastIPFailure();
void setLastIPFailure(String ip);
}

View file

@ -0,0 +1,36 @@
package org.keycloak.models.sessions;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface Session {
String getId();
void setId(String id);
String getUser();
void setUser(String user);
String getIpAddress();
void setIpAddress(String ipAddress);
int getStarted();
void setStarted(int started);
int getLastSessionRefresh();
void setLastSessionRefresh(int seconds);
void associateClient(String client);
List<String> getClientAssociations();
void removeAssociatedClient(String client);
}

View file

@ -0,0 +1,42 @@
package org.keycloak.models.sessions;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.Provider;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface SessionProvider extends Provider {
LoginFailure getUserLoginFailure(String username, String realm);
LoginFailure addUserLoginFailure(String username, String realm);
List<LoginFailure> getAllUserLoginFailures(String realm);
Session createUserSession(String realm, String id, String user, String ipAddress);
Session getUserSession(String id, String realm);
List<Session> getUserSessionsByUser(String user, String realm);
Set<Session> getUserSessionsByClient(String realm, String client);
int getActiveUserSessions(String realm, String client);
void removeUserSession(Session session);
void removeUserSessions(String realm, String user);
void removeExpiredUserSessions(String realm, long refreshTimeout, long sessionTimeout);
void removeUserSessions(String realm);
KeycloakTransaction getTransaction();
void close();
}

View file

@ -0,0 +1,9 @@
package org.keycloak.models.sessions;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface SessionProviderFactory extends ProviderFactory<SessionProvider> {
}

View file

@ -0,0 +1,27 @@
package org.keycloak.models.sessions;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class SessionSpi implements Spi {
@Override
public String getName() {
return "modelSessions";
}
@Override
public Class<? extends Provider> getProviderClass() {
return SessionProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return SessionProviderFactory.class;
}
}

View file

@ -0,0 +1,12 @@
package org.keycloak.models.users;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface Attributes {
String EMAIL_VERIFIED = "keycloak.emailVerified";
String TOTP_ENABLED = "keycloak.totpEnabled";
String REQUIRED_ACTIONS = "keycloak.requiredActions";
}

View file

@ -0,0 +1,72 @@
package org.keycloak.models.users;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Credentials {
private byte[] salt;
private String type;
private String value;
private String device;
private int hashIterations;
public Credentials(String type, String value) {
this.type = type;
this.value = value;
}
public Credentials(String type, String value, String device) {
this.type = type;
this.value = value;
this.device = device;
}
public Credentials(String type, byte[] salt, String value, int hashIterations, String device) {
this.salt = salt;
this.type = type;
this.value = value;
this.hashIterations = hashIterations;
this.device = device;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public byte[] getSalt() {
return salt;
}
public void setSalt(byte[] salt) {
this.salt = salt;
}
public int getHashIterations() {
return hashIterations;
}
public void setHashIterations(int hashIterations) {
this.hashIterations = hashIterations;
}
}

View file

@ -0,0 +1,12 @@
package org.keycloak.models.users;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public enum Feature {
READ_CREDENTIALS,
UPDATE_CREDENTIALS,
VERIFY_CREDENTIALS;
}

View file

@ -0,0 +1,58 @@
package org.keycloak.models.users;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface User {
public static final String USERNAME = "username";
public static final String LAST_NAME = "lastName";
public static final String FIRST_NAME = "firstName";
public static final String EMAIL = "email";
String getId();
boolean isEnabled();
void setEnabled(boolean enabled);
String getUsername();
void setUsername(String username);
String getFirstName();
void setFirstName(String firstName);
String getLastName();
void setLastName(String lastName);
String getEmail();
void setEmail(String email);
String getAttribute(String name);
Map<String, String> getAttributes();
void setAttribute(String name, String value);
void removeAttribute(String name);
List<Credentials> getCredentials();
void updateCredential(Credentials cred);
Set<String> getRoleMappings();
void grantRole(String role);
void deleteRoleMapping(String role);
}

View file

@ -0,0 +1,46 @@
package org.keycloak.models.users;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.Provider;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface UserProvider extends Provider {
KeycloakTransaction getTransaction();
User addUser(String id, String username, Set<String> roles, String realm);
boolean removeUser(String name, String realm);
User getUserById(String id, String realm);
User getUserByUsername(String username, String realm);
User getUserByEmail(String email, String realm);
User getUserByAttribute(String name, String value, String realm);
List<User> getUsers(String realm);
List<User> searchForUser(String search, String realm);
List<User> searchForUserByAttributes(Map<String, String> attributes, String realm);
/**
* Returns features supported by the provider. A provider is required to at least support one of verifying credentials
* or reading credentials.
*
* @param feature
* @return
*/
boolean supports(Feature feature);
boolean verifyCredentials(User user, Credentials... credentials);
void onRealmRemoved(String realm);
void onRoleRemoved(String role);
void close();
}

View file

@ -0,0 +1,9 @@
package org.keycloak.models.users;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface UserProviderFactory extends ProviderFactory<UserProvider> {
}

View file

@ -0,0 +1,27 @@
package org.keycloak.models.users;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserSpi implements Spi {
@Override
public String getName() {
return "modelUsers";
}
@Override
public Class<? extends Provider> getProviderClass() {
return UserProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return UserProviderFactory.class;
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.hybrid.HybridModelProviderFactory

View file

@ -0,0 +1,3 @@
org.keycloak.models.realms.RealmSpi
org.keycloak.models.sessions.SessionSpi
org.keycloak.models.users.UserSpi

View file

@ -31,5 +31,12 @@
<module>jpa</module>
<module>mongo</module>
<module>tests</module>
<module>hybrid</module>
<module>realms-jpa</module>
<module>users-jpa</module>
<module>sessions-mem</module>
<module>sessions-jpa</module>
<module>tests-hybrid</module>
</modules>
</project>

45
model/realms-jpa/pom.xml Executable file
View file

@ -0,0 +1,45 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-realms-jpa</artifactId>
<name>Keycloak Model Realms JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,258 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.realms.Application;
import org.keycloak.models.realms.Client;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.Role;
import org.keycloak.models.realms.RoleContainer;
import org.keycloak.models.realms.jpa.entities.ScopeMappingEntity;
import org.keycloak.models.realms.jpa.entities.ApplicationEntity;
import org.keycloak.models.realms.jpa.entities.RoleEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ApplicationAdapter extends ClientAdapter implements Application {
protected EntityManager em;
protected ApplicationEntity applicationEntity;
public ApplicationAdapter(RealmProvider provider, EntityManager em, ApplicationEntity applicationEntity) {
super(provider, applicationEntity, em);
this.em = em;
this.applicationEntity = applicationEntity;
}
@Override
public void updateApplication() {
em.flush();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public boolean isSurrogateAuthRequired() {
return applicationEntity.isSurrogateAuthRequired();
}
@Override
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
applicationEntity.setSurrogateAuthRequired(surrogateAuthRequired);
}
@Override
public String getManagementUrl() {
return applicationEntity.getManagementUrl();
}
@Override
public void setManagementUrl(String url) {
applicationEntity.setManagementUrl(url);
}
@Override
public String getBaseUrl() {
return applicationEntity.getBaseUrl();
}
@Override
public void setBaseUrl(String url) {
applicationEntity.setBaseUrl(url);
}
@Override
public boolean isBearerOnly() {
return applicationEntity.isBearerOnly();
}
@Override
public void setBearerOnly(boolean only) {
applicationEntity.setBearerOnly(only);
}
@Override
public boolean isDirectGrantsOnly() {
return false; // applications can't be grant only
}
@Override
public void setDirectGrantsOnly(boolean flag) {
// applications can't be grant only
}
@Override
public Role getRole(String name) {
TypedQuery<RoleEntity> query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
query.setParameter("name", name);
query.setParameter("application", entity);
List<RoleEntity> roles = query.getResultList();
if (roles.size() == 0) return null;
return new RoleAdapter(provider, em, roles.get(0));
}
@Override
public Role addRole(String id, String name) {
RoleEntity roleEntity = new RoleEntity();
roleEntity.setId(id);
roleEntity.setName(name);
roleEntity.setApplication(applicationEntity);
roleEntity.setApplicationRole(true);
roleEntity.setRealmId(entity.getRealm().getId());
em.persist(roleEntity);
applicationEntity.getRoles().add(roleEntity);
em.flush();
return new RoleAdapter(provider, em, roleEntity);
}
@Override
public boolean removeRole(Role Role) {
RoleAdapter roleAdapter = (RoleAdapter) Role;
if (Role == null) {
return false;
}
if (!roleAdapter.getContainer().equals(this)) return false;
if (!roleAdapter.getRole().isApplicationRole()) return false;
RoleEntity role = roleAdapter.getRole();
applicationEntity.getRoles().remove(role);
applicationEntity.getDefaultRoles().remove(role);
em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", role).executeUpdate();
em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
role.setApplication(null);
em.flush();
em.remove(role);
em.flush();
return true;
}
@Override
public Set<Role> getRoles() {
Set<Role> list = new HashSet<Role>();
Collection<RoleEntity> roles = applicationEntity.getRoles();
if (roles == null) return list;
for (RoleEntity entity : roles) {
list.add(new RoleAdapter(provider, em, entity));
}
return list;
}
@Override
public Set<Role> getApplicationScopeMappings(Client client) {
Set<Role> roleMappings = client.getScopeMappings();
Set<Role> appRoles = new HashSet<Role>();
for (Role role : roleMappings) {
RoleContainer container = role.getContainer();
if (container instanceof Realm) {
} else {
Application app = (Application)container;
if (app.getId().equals(getId())) {
appRoles.add(role);
}
}
}
return appRoles;
}
@Override
public List<String> getDefaultRoles() {
Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
List<String> roles = new ArrayList<String>();
if (entities == null) return roles;
for (RoleEntity entity : entities) {
roles.add(entity.getName());
}
return roles;
}
@Override
public void addDefaultRole(String name) {
Role role = getRole(name);
Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
for (RoleEntity entity : entities) {
if (entity.getId().equals(role.getId())) {
return;
}
}
entities.add(((RoleAdapter) role).getRole());
em.flush();
}
public static boolean contains(String str, String[] array) {
for (String s : array) {
if (str.equals(s)) return true;
}
return false;
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
Set<String> already = new HashSet<String>();
List<RoleEntity> remove = new ArrayList<RoleEntity>();
for (RoleEntity rel : entities) {
if (!contains(rel.getName(), defaultRoles)) {
remove.add(rel);
} else {
already.add(rel.getName());
}
}
for (RoleEntity entity : remove) {
entities.remove(entity);
}
em.flush();
for (String roleName : defaultRoles) {
if (!already.contains(roleName)) {
addDefaultRole(roleName);
}
}
em.flush();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof Application)) return false;
Application that = (Application) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
public String toString() {
return getName();
}
ApplicationEntity getJpaEntity() {
return applicationEntity;
}
}

View file

@ -0,0 +1,225 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.realms.Client;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.Role;
import org.keycloak.models.realms.RoleContainer;
import org.keycloak.models.realms.jpa.entities.ScopeMappingEntity;
import org.keycloak.models.realms.jpa.entities.ClientEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class ClientAdapter implements Client {
protected RealmProvider provider;
protected ClientEntity entity;
protected EntityManager em;
public ClientAdapter(RealmProvider provider, ClientEntity entity, EntityManager em) {
this.provider = provider;
this.entity = entity;
this.em = em;
}
public ClientEntity getEntity() {
return entity;
}
@Override
public String getId() {
return entity.getId();
}
@Override
public Realm getRealm() {
return new RealmAdapter(provider, em, entity.getRealm());
}
@Override
public String getClientId() {
return entity.getName();
}
@Override
public boolean isEnabled() {
return entity.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
entity.setEnabled(enabled);
}
@Override
public long getAllowedClaimsMask() {
return entity.getAllowedClaimsMask();
}
@Override
public void setAllowedClaimsMask(long mask) {
entity.setAllowedClaimsMask(mask);
}
@Override
public boolean isPublicClient() {
return entity.isPublicClient();
}
@Override
public void setPublicClient(boolean flag) {
entity.setPublicClient(flag);
}
@Override
public Set<String> getWebOrigins() {
Set<String> result = new HashSet<String>();
result.addAll(entity.getWebOrigins());
return result;
}
@Override
public void setWebOrigins(Set<String> webOrigins) {
entity.setWebOrigins(webOrigins);
}
@Override
public void addWebOrigin(String webOrigin) {
entity.getWebOrigins().add(webOrigin);
}
@Override
public void removeWebOrigin(String webOrigin) {
entity.getWebOrigins().remove(webOrigin);
}
@Override
public Set<String> getRedirectUris() {
Set<String> result = new HashSet<String>();
result.addAll(entity.getRedirectUris());
return result;
}
@Override
public void setRedirectUris(Set<String> redirectUris) {
entity.setRedirectUris(redirectUris);
}
@Override
public void addRedirectUri(String redirectUri) {
entity.getRedirectUris().add(redirectUri);
}
@Override
public void removeRedirectUri(String redirectUri) {
entity.getRedirectUris().remove(redirectUri);
}
@Override
public String getSecret() {
return entity.getSecret();
}
@Override
public void setSecret(String secret) {
entity.setSecret(secret);
}
@Override
public boolean validateSecret(String secret) {
return secret.equals(entity.getSecret());
}
@Override
public int getNotBefore() {
return entity.getNotBefore();
}
@Override
public void setNotBefore(int notBefore) {
entity.setNotBefore(notBefore);
}
@Override
public Set<Role> getRealmScopeMappings() {
Set<Role> roleMappings = getScopeMappings();
Set<Role> appRoles = new HashSet<Role>();
for (Role role : roleMappings) {
RoleContainer container = role.getContainer();
if (container instanceof Realm) {
if (((Realm) container).getId().equals(entity.getRealm().getId())) {
appRoles.add(role);
}
}
}
return appRoles;
}
@Override
public Set<Role> getScopeMappings() {
TypedQuery<String> query = em.createNamedQuery("clientScopeMappingIds", String.class);
query.setParameter("client", getEntity());
List<String> ids = query.getResultList();
Set<Role> roles = new HashSet<Role>();
for (String roleId : ids) {
Role role = provider.getRoleById(roleId, entity.getRealm().getId());
if (role == null) continue;
roles.add(role);
}
return roles;
}
@Override
public void addScopeMapping(Role role) {
ScopeMappingEntity entity = new ScopeMappingEntity();
entity.setClient(getEntity());
entity.setRole(((RoleAdapter) role).getRole());
em.persist(entity);
em.flush();
em.detach(entity);
}
@Override
public void deleteScopeMapping(Role role) {
TypedQuery<ScopeMappingEntity> query = getRealmScopeMappingQuery((RoleAdapter) role);
List<ScopeMappingEntity> results = query.getResultList();
if (results.size() == 0) return;
for (ScopeMappingEntity entity : results) {
em.remove(entity);
}
}
protected TypedQuery<ScopeMappingEntity> getRealmScopeMappingQuery(RoleAdapter role) {
TypedQuery<ScopeMappingEntity> query = em.createNamedQuery("hasScope", ScopeMappingEntity.class);
query.setParameter("client", getEntity());
query.setParameter("role", role.getRole());
return query;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!this.getClass().equals(o.getClass())) return false;
ClientAdapter that = (ClientAdapter) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return entity.getId().hashCode();
}
}

View file

@ -0,0 +1,53 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.KeycloakTransaction;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaKeycloakTransaction implements KeycloakTransaction {
protected EntityManager em;
public JpaKeycloakTransaction(EntityManager em) {
this.em = em;
}
@Override
public void begin() {
em.getTransaction().begin();
}
@Override
public void commit() {
try {
em.getTransaction().commit();
} catch (PersistenceException e) {
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
}
}
@Override
public void rollback() {
em.getTransaction().rollback();
}
@Override
public void setRollbackOnly() {
em.getTransaction().setRollbackOnly();
}
@Override
public boolean getRollbackOnly() {
return em.getTransaction().getRollbackOnly();
}
@Override
public boolean isActive() {
return em.getTransaction().isActive();
}
}

View file

@ -0,0 +1,135 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.realms.Application;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.realms.OAuthClient;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.Role;
import org.keycloak.models.realms.jpa.entities.ApplicationEntity;
import org.keycloak.models.realms.jpa.entities.OAuthClientEntity;
import org.keycloak.models.realms.jpa.entities.RealmEntity;
import org.keycloak.models.realms.jpa.entities.RoleEntity;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaRealmProvider implements RealmProvider {
protected final EntityManager em;
public JpaRealmProvider(EntityManager em) {
this.em = PersistenceExceptionConverter.create(em);
}
@Override
public KeycloakTransaction getTransaction() {
return new JpaKeycloakTransaction(em);
}
@Override
public Realm createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
}
@Override
public Realm createRealm(String id, String name) {
RealmEntity realm = new RealmEntity();
realm.setName(name);
realm.setId(id);
em.persist(realm);
em.flush();
return new RealmAdapter(this, em, realm);
}
@Override
public Realm getRealm(String id) {
RealmEntity realm = em.find(RealmEntity.class, id);
if (realm == null) return null;
return new RealmAdapter(this, em, realm);
}
@Override
public List<Realm> getRealms() {
TypedQuery<RealmEntity> query = em.createNamedQuery("getAllRealms", RealmEntity.class);
List<RealmEntity> entities = query.getResultList();
List<Realm> realms = new ArrayList<Realm>();
for (RealmEntity entity : entities) {
realms.add(new RealmAdapter(this, em, entity));
}
return realms;
}
@Override
public Realm getRealmByName(String name) {
TypedQuery<RealmEntity> query = em.createNamedQuery("getRealmByName", RealmEntity.class);
query.setParameter("name", name);
List<RealmEntity> entities = query.getResultList();
if (entities.size() == 0) return null;
if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name");
RealmEntity realm = query.getResultList().get(0);
if (realm == null) return null;
return new RealmAdapter(this, em, realm);
}
@Override
public boolean removeRealm(String id) {
RealmEntity realm = em.find(RealmEntity.class, id);
if (realm == null) {
return false;
}
RealmAdapter adapter = new RealmAdapter(this, em, realm);
for (Application a : adapter.getApplications()) {
adapter.removeApplication(a);
}
for (OAuthClient oauth : adapter.getOAuthClients()) {
adapter.removeOAuthClient(oauth);
}
em.remove(realm);
return true;
}
@Override
public void close() {
if (em.getTransaction().isActive()) em.getTransaction().rollback();
if (em.isOpen()) em.close();
}
@Override
public Role getRoleById(String id, String realm) {
RoleEntity entity = em.find(RoleEntity.class, id);
if (entity == null) return null;
if (!realm.equals(entity.getRealmId())) return null;
return new RoleAdapter(this, em, entity);
}
@Override
public Application getApplicationById(String id, String realm) {
ApplicationEntity app = em.find(ApplicationEntity.class, id);
// Check if application belongs to this realm
if (app == null || !realm.equals(app.getRealm().getId())) return null;
return new ApplicationAdapter(this, em, app);
}
@Override
public OAuthClient getOAuthClientById(String id, String realm) {
OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
// Check if client belongs to this realm
if (client == null || !realm.equals(client.getRealm().getId())) return null;
return new OAuthClientAdapter(this, client, em);
}
}

View file

@ -0,0 +1,41 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.Config;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.realms.RealmProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.util.JpaUtils;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaRealmProviderFactory implements RealmProviderFactory {
protected EntityManagerFactory emf;
@Override
public void init(Config.Scope config) {
String persistenceUnit = config.get("persistenceUnit", "jpa-keycloak-identity-store");
emf = Persistence.createEntityManagerFactory(persistenceUnit, JpaUtils.getHibernateProperties());
}
@Override
public String getId() {
return "jpa";
}
@Override
public RealmProvider create(KeycloakSession session) {
return new JpaRealmProvider(emf.createEntityManager());
}
@Override
public void close() {
emf.close();
}
}

View file

@ -0,0 +1,53 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.realms.OAuthClient;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.jpa.entities.OAuthClientEntity;
import javax.persistence.EntityManager;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthClientAdapter extends ClientAdapter implements OAuthClient {
protected final OAuthClientEntity oAuthClientEntity;
public OAuthClientAdapter(RealmProvider provider, OAuthClientEntity entity, EntityManager em) {
super(provider, entity, em);
oAuthClientEntity = entity;
}
@Override
public void setClientId(String id) {
entity.setName(id);
}
@Override
public boolean isDirectGrantsOnly() {
return oAuthClientEntity.isDirectGrantsOnly();
}
@Override
public void setDirectGrantsOnly(boolean flag) {
oAuthClientEntity.setDirectGrantsOnly(flag);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof OAuthClient)) return false;
OAuthClient that = (OAuthClient) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
}

View file

@ -0,0 +1,48 @@
package org.keycloak.models.realms.jpa;
import org.hibernate.exception.ConstraintViolationException;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PersistenceExceptionConverter implements InvocationHandler {
private EntityManager em;
public static EntityManager create(EntityManager em) {
return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
}
private PersistenceExceptionConverter(EntityManager em) {
this.em = em;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(em, args);
} catch (InvocationTargetException e) {
throw convert(e.getCause());
}
}
public static ModelException convert(Throwable t) {
if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
throw new ModelDuplicateException(t);
} if (t instanceof EntityExistsException) {
throw new ModelDuplicateException(t);
} else {
throw new ModelException(t);
}
}
}

View file

@ -0,0 +1,853 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.realms.Application;
import org.keycloak.models.realms.Client;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.realms.OAuthClient;
import org.keycloak.models.realms.Realm;
import org.keycloak.models.realms.Role;
import org.keycloak.models.realms.jpa.entities.ApplicationEntity;
import org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity;
import org.keycloak.models.realms.jpa.entities.RealmEntity;
import org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity;
import org.keycloak.models.realms.jpa.entities.RoleEntity;
import org.keycloak.models.realms.jpa.entities.ScopeMappingEntity;
import org.keycloak.models.realms.jpa.entities.OAuthClientEntity;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmAdapter implements Realm {
protected RealmEntity realm;
protected RealmProvider provider;
protected EntityManager em;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
protected PasswordPolicy passwordPolicy;
public RealmAdapter(RealmProvider provider, EntityManager em, RealmEntity realm) {
this.provider = provider;
this.em = em;
this.realm = realm;
}
public RealmEntity getEntity() {
return realm;
}
@Override
public String getId() {
return realm.getId();
}
@Override
public String getName() {
return realm.getName();
}
@Override
public void setName(String name) {
realm.setName(name);
em.flush();
}
@Override
public boolean isEnabled() {
return realm.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
realm.setEnabled(enabled);
em.flush();
}
@Override
public boolean isSslNotRequired() {
return realm.isSslNotRequired();
}
@Override
public void setSslNotRequired(boolean sslNotRequired) {
realm.setSslNotRequired(sslNotRequired);
em.flush();
}
@Override
public boolean isPasswordCredentialGrantAllowed() {
return realm.isPasswordCredentialGrantAllowed();
}
@Override
public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
em.flush();
}
@Override
public boolean isRegistrationAllowed() {
return realm.isRegistrationAllowed();
}
@Override
public void setRegistrationAllowed(boolean registrationAllowed) {
realm.setRegistrationAllowed(registrationAllowed);
em.flush();
}
@Override
public boolean isRememberMe() {
return realm.isRememberMe();
}
@Override
public void setRememberMe(boolean rememberMe) {
realm.setRememberMe(rememberMe);
em.flush();
}
@Override
public boolean isBruteForceProtected() {
return realm.isBruteForceProtected();
}
@Override
public void setBruteForceProtected(boolean value) {
realm.setBruteForceProtected(value);
}
@Override
public int getMaxFailureWaitSeconds() {
return realm.getMaxFailureWaitSeconds();
}
@Override
public void setMaxFailureWaitSeconds(int val) {
realm.setMaxFailureWaitSeconds(val);
}
@Override
public int getWaitIncrementSeconds() {
return realm.getWaitIncrementSeconds();
}
@Override
public void setWaitIncrementSeconds(int val) {
realm.setWaitIncrementSeconds(val);
}
@Override
public long getQuickLoginCheckMilliSeconds() {
return realm.getQuickLoginCheckMilliSeconds();
}
@Override
public void setQuickLoginCheckMilliSeconds(long val) {
realm.setQuickLoginCheckMilliSeconds(val);
}
@Override
public int getMinimumQuickLoginWaitSeconds() {
return realm.getMinimumQuickLoginWaitSeconds();
}
@Override
public void setMinimumQuickLoginWaitSeconds(int val) {
realm.setMinimumQuickLoginWaitSeconds(val);
}
@Override
public int getMaxDeltaTimeSeconds() {
return realm.getMaxDeltaTimeSeconds();
}
@Override
public void setMaxDeltaTimeSeconds(int val) {
realm.setMaxDeltaTimeSeconds(val);
}
@Override
public int getFailureFactor() {
return realm.getFailureFactor();
}
@Override
public void setFailureFactor(int failureFactor) {
realm.setFailureFactor(failureFactor);
}
@Override
public boolean isVerifyEmail() {
return realm.isVerifyEmail();
}
@Override
public void setVerifyEmail(boolean verifyEmail) {
realm.setVerifyEmail(verifyEmail);
em.flush();
}
@Override
public boolean isResetPasswordAllowed() {
return realm.isResetPasswordAllowed();
}
@Override
public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
realm.setResetPasswordAllowed(resetPasswordAllowed);
em.flush();
}
@Override
public int getNotBefore() {
return realm.getNotBefore();
}
@Override
public void setNotBefore(int notBefore) {
realm.setNotBefore(notBefore);
}
@Override
public int getAccessTokenLifespan() {
return realm.getAccessTokenLifespan();
}
@Override
public void setAccessTokenLifespan(int tokenLifespan) {
realm.setAccessTokenLifespan(tokenLifespan);
em.flush();
}
@Override
public int getSsoSessionIdleTimeout() {
return realm.getSsoSessionIdleTimeout();
}
@Override
public void setSsoSessionIdleTimeout(int seconds) {
realm.setSsoSessionIdleTimeout(seconds);
}
@Override
public int getSsoSessionMaxLifespan() {
return realm.getSsoSessionMaxLifespan();
}
@Override
public void setSsoSessionMaxLifespan(int seconds) {
realm.setSsoSessionMaxLifespan(seconds);
}
@Override
public int getAccessCodeLifespan() {
return realm.getAccessCodeLifespan();
}
@Override
public void setAccessCodeLifespan(int accessCodeLifespan) {
realm.setAccessCodeLifespan(accessCodeLifespan);
em.flush();
}
@Override
public int getAccessCodeLifespanUserAction() {
return realm.getAccessCodeLifespanUserAction();
}
@Override
public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
realm.setAccessCodeLifespanUserAction(accessCodeLifespanUserAction);
em.flush();
}
@Override
public String getPublicKeyPem() {
return realm.getPublicKeyPem();
}
@Override
public void setPublicKeyPem(String publicKeyPem) {
realm.setPublicKeyPem(publicKeyPem);
em.flush();
}
@Override
public String getPrivateKeyPem() {
return realm.getPrivateKeyPem();
}
@Override
public void setPrivateKeyPem(String privateKeyPem) {
realm.setPrivateKeyPem(privateKeyPem);
em.flush();
}
@Override
public PublicKey getPublicKey() {
if (publicKey != null) return publicKey;
publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
return publicKey;
}
@Override
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
setPublicKeyPem(publicKeyPem);
}
@Override
public PrivateKey getPrivateKey() {
if (privateKey != null) return privateKey;
privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
return privateKey;
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
setPrivateKeyPem(privateKeyPem);
}
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
if (model == null) {
throw new RuntimeException("Unknown credential type " + type);
}
return model;
}
@Override
public void addRequiredCredential(String type) {
RequiredCredentialModel model = initRequiredCredentialModel(type);
addRequiredCredential(model);
em.flush();
}
public void addRequiredCredential(RequiredCredentialModel model) {
RequiredCredentialEntity entity = new RequiredCredentialEntity();
entity.setInput(model.isInput());
entity.setSecret(model.isSecret());
entity.setType(model.getType());
entity.setFormLabel(model.getFormLabel());
em.persist(entity);
realm.getRequiredCredentials().add(entity);
em.flush();
}
@Override
public void updateRequiredCredentials(Set<String> creds) {
Collection<RequiredCredentialEntity> relationships = realm.getRequiredCredentials();
if (relationships == null) relationships = new ArrayList<RequiredCredentialEntity>();
Set<String> already = new HashSet<String>();
List<RequiredCredentialEntity> remove = new ArrayList<RequiredCredentialEntity>();
for (RequiredCredentialEntity rel : relationships) {
if (!creds.contains(rel.getType())) {
remove.add(rel);
} else {
already.add(rel.getType());
}
}
for (RequiredCredentialEntity entity : remove) {
relationships.remove(entity);
em.remove(entity);
}
for (String cred : creds) {
if (!already.contains(cred)) {
addRequiredCredential(cred);
}
}
em.flush();
}
@Override
public List<RequiredCredentialModel> getRequiredCredentials() {
List<RequiredCredentialModel> requiredCredentialModels = new ArrayList<RequiredCredentialModel>();
Collection<RequiredCredentialEntity> entities = realm.getRequiredCredentials();
if (entities == null) return requiredCredentialModels;
for (RequiredCredentialEntity entity : entities) {
RequiredCredentialModel model = new RequiredCredentialModel();
model.setFormLabel(entity.getFormLabel());
model.setType(entity.getType());
model.setSecret(entity.isSecret());
model.setInput(entity.isInput());
requiredCredentialModels.add(model);
}
return requiredCredentialModels; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public List<String> getDefaultRoles() {
Collection<RoleEntity> entities = realm.getDefaultRoles();
List<String> roles = new ArrayList<String>();
if (entities == null) return roles;
for (RoleEntity entity : entities) {
roles.add(entity.getName());
}
return roles;
}
@Override
public void addDefaultRole(String name) {
Role role = getRole(name);
Collection<RoleEntity> entities = realm.getDefaultRoles();
for (RoleEntity entity : entities) {
if (entity.getId().equals(role.getId())) {
return;
}
}
realm.getDefaultRoles().add(((RoleAdapter) role).getRole());
em.flush();
}
public static boolean contains(String str, String[] array) {
for (String s : array) {
if (str.equals(s)) return true;
}
return false;
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
Collection<RoleEntity> entities = realm.getDefaultRoles();
Set<String> already = new HashSet<String>();
List<RoleEntity> remove = new ArrayList<RoleEntity>();
for (RoleEntity rel : entities) {
if (!contains(rel.getName(), defaultRoles)) {
remove.add(rel);
} else {
already.add(rel.getName());
}
}
for (RoleEntity entity : remove) {
entities.remove(entity);
}
em.flush();
for (String roleName : defaultRoles) {
if (!already.contains(roleName)) {
addDefaultRole(roleName);
}
}
em.flush();
}
@Override
public Client findClient(String clientId) {
Client model = getApplicationByName(clientId);
if (model != null) return model;
return getOAuthClient(clientId);
}
@Override
public Map<String, Application> getApplicationNameMap() {
Map<String, Application> map = new HashMap<String, Application>();
for (Application app : getApplications()) {
map.put(app.getName(), app);
}
return map; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public List<Application> getApplications() {
List<Application> list = new ArrayList<Application>();
if (realm.getApplications() == null) return list;
for (ApplicationEntity entity : realm.getApplications()) {
list.add(new ApplicationAdapter(provider, em, entity));
}
return list;
}
@Override
public Application addApplication(String id, String name) {
ApplicationEntity applicationData = new ApplicationEntity();
applicationData.setId(id);
applicationData.setName(name);
applicationData.setEnabled(true);
applicationData.setRealm(realm);
realm.getApplications().add(applicationData);
em.persist(applicationData);
em.flush();
Application resource = new ApplicationAdapter(provider, em, applicationData);
em.flush();
return resource;
}
@Override
public boolean removeApplication(Application application) {
for (Role role : application.getRoles()) {
application.removeRole(role);
}
ApplicationEntity applicationEntity = null;
Iterator<ApplicationEntity> it = realm.getApplications().iterator();
while (it.hasNext()) {
ApplicationEntity ae = it.next();
if (ae.getId().equals(application.getId())) {
applicationEntity = ae;
it.remove();
break;
}
}
for (ApplicationEntity a : realm.getApplications()) {
if (a.getId().equals(application.getId())) {
applicationEntity = a;
}
}
if (application == null) {
return false;
}
em.remove(applicationEntity);
em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", applicationEntity).executeUpdate();
return true;
}
@Override
public Application getApplicationByName(String name) {
return getApplicationNameMap().get(name);
}
@Override
public boolean isSocial() {
return realm.isSocial();
}
@Override
public void setSocial(boolean social) {
realm.setSocial(social);
em.flush();
}
@Override
public boolean isUpdateProfileOnInitialSocialLogin() {
return realm.isUpdateProfileOnInitialSocialLogin();
}
@Override
public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
em.flush();
}
@Override
public OAuthClient addOAuthClient(String id, String name) {
OAuthClientEntity data = new OAuthClientEntity();
data.setId(id);
data.setEnabled(true);
data.setName(name);
data.setRealm(realm);
em.persist(data);
em.flush();
return new OAuthClientAdapter(provider, data, em);
}
@Override
public boolean removeOAuthClient(OAuthClient client) {
em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", client).executeUpdate();
em.remove(client);
return true;
}
@Override
public OAuthClient getOAuthClient(String name) {
TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByName", OAuthClientEntity.class);
query.setParameter("name", name);
query.setParameter("realm", realm);
List<OAuthClientEntity> entities = query.getResultList();
if (entities.size() == 0) return null;
return new OAuthClientAdapter(provider, entities.get(0), em);
}
@Override
public List<OAuthClient> getOAuthClients() {
TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByRealm", OAuthClientEntity.class);
query.setParameter("realm", realm);
List<OAuthClientEntity> entities = query.getResultList();
List<OAuthClient> list = new ArrayList<OAuthClient>();
for (OAuthClientEntity entity : entities) list.add(new OAuthClientAdapter(provider, entity, em));
return list;
}
@Override
public Map<String, String> getSmtpConfig() {
return realm.getSmtpConfig();
}
@Override
public void setSmtpConfig(Map<String, String> smtpConfig) {
realm.setSmtpConfig(smtpConfig);
em.flush();
}
@Override
public Map<String, String> getSocialConfig() {
return realm.getSocialConfig();
}
@Override
public void setSocialConfig(Map<String, String> socialConfig) {
realm.setSocialConfig(socialConfig);
em.flush();
}
@Override
public Map<String, String> getLdapServerConfig() {
return realm.getLdapServerConfig();
}
@Override
public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
realm.setLdapServerConfig(ldapServerConfig);
em.flush();
}
@Override
public List<AuthenticationProviderModel> getAuthenticationProviders() {
List<AuthenticationProviderEntity> entities = realm.getAuthenticationProviders();
List<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>();
for (AuthenticationProviderEntity entity : entities) {
copy.add(entity);
}
Collections.sort(copy, new Comparator<AuthenticationProviderEntity>() {
@Override
public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
return o1.getPriority() - o2.getPriority();
}
});
List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
for (AuthenticationProviderEntity entity : copy) {
result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
}
return result;
}
@Override
public void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders) {
List<AuthenticationProviderEntity> newEntities = new ArrayList<AuthenticationProviderEntity>();
int counter = 1;
for (AuthenticationProviderModel model : authenticationProviders) {
AuthenticationProviderEntity entity = new AuthenticationProviderEntity();
entity.setProviderName(model.getProviderName());
entity.setPasswordUpdateSupported(model.isPasswordUpdateSupported());
entity.setConfig(model.getConfig());
entity.setPriority(counter++);
newEntities.add(entity);
}
// Remove all existing first
Collection<AuthenticationProviderEntity> existing = realm.getAuthenticationProviders();
Collection<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>(existing);
for (AuthenticationProviderEntity apToRemove : copy) {
existing.remove(apToRemove);
em.remove(apToRemove);
}
// Now create all new providers
for (AuthenticationProviderEntity apToAdd : newEntities) {
existing.add(apToAdd);
em.persist(apToAdd);
}
em.flush();
}
@Override
public Role getRole(String name) {
TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
query.setParameter("name", name);
query.setParameter("realm", realm);
List<RoleEntity> roles = query.getResultList();
if (roles.size() == 0) return null;
return new RoleAdapter(provider, em, roles.get(0));
}
@Override
public Role addRole(String id, String name) {
RoleEntity entity = new RoleEntity();
entity.setId(id);
entity.setName(name);
entity.setRealm(realm);
entity.setRealmId(realm.getId());
em.persist(entity);
realm.getRoles().add(entity);
em.flush();
return new RoleAdapter(provider, em, entity);
}
@Override
public boolean removeRole(Role role) {
if (role == null) {
return false;
}
if (!role.getContainer().equals(this)) return false;
RoleEntity roleEntity = ((RoleAdapter) role).getRole();
realm.getRoles().remove(role);
realm.getDefaultRoles().remove(role);
em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", roleEntity).executeUpdate();
em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
em.remove(roleEntity);
return true;
}
@Override
public Set<Role> getRoles() {
Set<Role> list = new HashSet<Role>();
Collection<RoleEntity> roles = realm.getRoles();
if (roles == null) return list;
for (RoleEntity entity : roles) {
list.add(new RoleAdapter(provider, em, entity));
}
return list;
}
@Override
public PasswordPolicy getPasswordPolicy() {
if (passwordPolicy == null) {
passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
}
return passwordPolicy;
}
@Override
public void setPasswordPolicy(PasswordPolicy policy) {
this.passwordPolicy = policy;
realm.setPasswordPolicy(policy.toString());
em.flush();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof Realm)) return false;
Realm that = (Realm) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
@Override
public String getLoginTheme() {
return realm.getLoginTheme();
}
@Override
public void setLoginTheme(String name) {
realm.setLoginTheme(name);
em.flush();
}
@Override
public String getAccountTheme() {
return realm.getAccountTheme();
}
@Override
public void setAccountTheme(String name) {
realm.setAccountTheme(name);
em.flush();
}
@Override
public String getAdminTheme() {
return realm.getAdminTheme();
}
@Override
public void setAdminTheme(String name) {
realm.setAdminTheme(name);
em.flush();
}
@Override
public String getEmailTheme() {
return realm.getEmailTheme();
}
@Override
public void setEmailTheme(String name) {
realm.setEmailTheme(name);
em.flush();
}
@Override
public boolean isAuditEnabled() {
return realm.isAuditEnabled();
}
@Override
public void setAuditEnabled(boolean enabled) {
realm.setAuditEnabled(enabled);
em.flush();
}
@Override
public long getAuditExpiration() {
return realm.getAuditExpiration();
}
@Override
public void setAuditExpiration(long expiration) {
realm.setAuditExpiration(expiration);
em.flush();
}
@Override
public Set<String> getAuditListeners() {
return realm.getAuditListeners();
}
@Override
public void setAuditListeners(Set<String> listeners) {
realm.setAuditListeners(listeners);
em.flush();
}
@Override
public Application getMasterAdminApp() {
return new ApplicationAdapter(provider, em, realm.getMasterAdminApp());
}
@Override
public void setMasterAdminApp(Application app) {
realm.setMasterAdminApp(((ApplicationAdapter) app).getJpaEntity());
em.flush();
}
}

View file

@ -0,0 +1,117 @@
package org.keycloak.models.realms.jpa;
import org.keycloak.models.realms.RealmProvider;
import org.keycloak.models.realms.Role;
import org.keycloak.models.realms.RoleContainer;
import org.keycloak.models.realms.jpa.entities.RoleEntity;
import javax.persistence.EntityManager;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleAdapter implements Role {
protected RoleEntity role;
protected RealmProvider provider;
protected EntityManager em;
public RoleAdapter(RealmProvider provider, EntityManager em, RoleEntity role) {
this.provider = provider;
this.em = em;
this.role = role;
}
public RoleEntity getRole() {
return role;
}
public void setRole(RoleEntity role) {
this.role = role;
}
@Override
public String getName() {
return role.getName();
}
@Override
public String getDescription() {
return role.getDescription();
}
@Override
public void setDescription(String description) {
role.setDescription(description);
}
@Override
public String getId() {
return role.getId();
}
@Override
public void setName(String name) {
role.setName(name);
}
@Override
public boolean isComposite() {
return getComposites().size() > 0;
}
@Override
public void addCompositeRole(Role role) {
RoleEntity entity = ((RoleAdapter)role).getRole();
for (RoleEntity composite : getRole().getCompositeRoles()) {
if (composite.equals(entity)) return;
}
getRole().getCompositeRoles().add(entity);
em.flush();
}
@Override
public void removeCompositeRole(Role role) {
RoleEntity entity = ((RoleAdapter)role).getRole();
Iterator<RoleEntity> it = getRole().getCompositeRoles().iterator();
while (it.hasNext()) {
if (it.next().equals(entity)) it.remove();
}
}
@Override
public Set<Role> getComposites() {
Set<Role> set = new HashSet<Role>();
for (RoleEntity composite : getRole().getCompositeRoles()) {
set.add(new RoleAdapter(provider, em, composite));
}
return set;
}
@Override
public RoleContainer getContainer() {
if (role.isApplicationRole()) {
return provider.getApplicationById(role.getApplication().getId(), role.getApplication().getRealm().getId());
} else {
return provider.getRealm(role.getRealm().getId());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof Role)) return false;
Role that = (Role) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
}

View file

@ -0,0 +1,77 @@
package org.keycloak.models.realms.jpa.entities;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
public class ApplicationEntity extends ClientEntity {
private boolean surrogateAuthRequired;
private String baseUrl;
private String managementUrl;
private boolean bearerOnly;
@OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="ApplicationDefaultRoles")
Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
public boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String getManagementUrl() {
return managementUrl;
}
public void setManagementUrl(String managementUrl) {
this.managementUrl = managementUrl;
}
public Collection<RoleEntity> getRoles() {
return roles;
}
public void setRoles(Collection<RoleEntity> roles) {
this.roles = roles;
}
public Collection<RoleEntity> getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
this.defaultRoles = defaultRoles;
}
public boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
}

View file

@ -0,0 +1,79 @@
package org.keycloak.models.realms.jpa.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@Entity
@Table(name="AuthProviderEntity")
public class AuthenticationProviderEntity {
@Id
@GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.realms.jpa.utils.JpaIdGenerator")
@GeneratedValue(generator = "keycloak_generator")
protected String id;
private String providerName;
private boolean passwordUpdateSupported;
private int priority;
@ElementCollection
@MapKeyColumn(name="name")
@Column(name="value")
@CollectionTable(name="AuthProviderEntity_cfg", joinColumns = {
@JoinColumn(name = "AuthProviderEntity_id")
})
private Map<String, String> config;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProviderName() {
return providerName;
}
public void setProviderName(String providerName) {
this.providerName = providerName;
}
public boolean isPasswordUpdateSupported() {
return passwordUpdateSupported;
}
public void setPasswordUpdateSupported(boolean passwordUpdateSupported) {
this.passwordUpdateSupported = passwordUpdateSupported;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}

View file

@ -0,0 +1,126 @@
package org.keycloak.models.realms.jpa.entities;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"realm", "name"})})
public abstract class ClientEntity {
@Id
private String id;
@Column(name = "name")
private String name;
private boolean enabled;
private String secret;
private long allowedClaimsMask;
private int notBefore;
private boolean publicClient;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "realm")
protected RealmEntity realm;
@ElementCollection
@CollectionTable
protected Set<String> webOrigins = new HashSet<String>();
@ElementCollection
@CollectionTable
protected Set<String> redirectUris = new HashSet<String>();
public RealmEntity getRealm() {
return realm;
}
public void setRealm(RealmEntity realm) {
this.realm = realm;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAllowedClaimsMask() {
return allowedClaimsMask;
}
public void setAllowedClaimsMask(long allowedClaimsMask) {
this.allowedClaimsMask = allowedClaimsMask;
}
public Set<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(Set<String> webOrigins) {
this.webOrigins = webOrigins;
}
public Set<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(Set<String> redirectUris) {
this.redirectUris = redirectUris;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
public boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
}

View file

@ -0,0 +1,27 @@
package org.keycloak.models.realms.jpa.entities;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="findOAuthClientByName", query="select o from OAuthClientEntity o where o.name=:name and o.realm = :realm"),
@NamedQuery(name="findOAuthClientByRealm", query="select o from OAuthClientEntity o where o.realm = :realm")
})
@Entity
public class OAuthClientEntity extends ClientEntity {
protected boolean directGrantsOnly;
public boolean isDirectGrantsOnly() {
return directGrantsOnly;
}
public void setDirectGrantsOnly(boolean directGrantsOnly) {
this.directGrantsOnly = directGrantsOnly;
}
}

View file

@ -0,0 +1,479 @@
package org.keycloak.models.realms.jpa.entities;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
@NamedQueries({
@NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"),
@NamedQuery(name="getRealmByName", query="select realm from RealmEntity realm where realm.name = :name"),
})
public class RealmEntity {
@Id
protected String id;
@Column(unique = true)
protected String name;
protected boolean enabled;
protected boolean sslNotRequired;
protected boolean registrationAllowed;
protected boolean passwordCredentialGrantAllowed;
protected boolean verifyEmail;
protected boolean resetPasswordAllowed;
protected boolean social;
protected boolean rememberMe;
//--- brute force settings
protected boolean bruteForceProtected;
protected int maxFailureWaitSeconds;
protected int minimumQuickLoginWaitSeconds;
protected int waitIncrementSeconds;
protected long quickLoginCheckMilliSeconds;
protected int maxDeltaTimeSeconds;
protected int failureFactor;
//--- end brute force settings
@Column(name="updateProfileOnInitSocLogin")
protected boolean updateProfileOnInitialSocialLogin;
protected String passwordPolicy;
private int ssoSessionIdleTimeout;
private int ssoSessionMaxLifespan;
protected int accessTokenLifespan;
protected int accessCodeLifespan;
protected int accessCodeLifespanUserAction;
protected int notBefore;
@Column(length = 2048)
protected String publicKeyPem;
@Column(length = 2048)
protected String privateKeyPem;
protected String loginTheme;
protected String accountTheme;
protected String adminTheme;
protected String emailTheme;
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="User_RequiredCreds")
Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="AuthProviders")
List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@ElementCollection
@MapKeyColumn(name="name")
@Column(name="value")
@CollectionTable
protected Map<String, String> smtpConfig = new HashMap<String, String>();
@ElementCollection
@MapKeyColumn(name="name")
@Column(name="value")
@CollectionTable
protected Map<String, String> socialConfig = new HashMap<String, String>();
@ElementCollection
@MapKeyColumn(name="name")
@Column(name="value")
@CollectionTable
protected Map<String, String> ldapServerConfig = new HashMap<String, String>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="RealmDefaultRoles")
protected Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
protected boolean auditEnabled;
protected long auditExpiration;
@ElementCollection
protected Set<String> auditListeners= new HashSet<String>();
@OneToOne
protected ApplicationEntity masterAdminApp;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isSslNotRequired() {
return sslNotRequired;
}
public void setSslNotRequired(boolean sslNotRequired) {
this.sslNotRequired = sslNotRequired;
}
public boolean isPasswordCredentialGrantAllowed() {
return passwordCredentialGrantAllowed;
}
public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
}
public boolean isRegistrationAllowed() {
return registrationAllowed;
}
public void setRegistrationAllowed(boolean registrationAllowed) {
this.registrationAllowed = registrationAllowed;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
public boolean isVerifyEmail() {
return verifyEmail;
}
public void setVerifyEmail(boolean verifyEmail) {
this.verifyEmail = verifyEmail;
}
public boolean isResetPasswordAllowed() {
return resetPasswordAllowed;
}
public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
this.resetPasswordAllowed = resetPasswordAllowed;
}
public boolean isSocial() {
return social;
}
public void setSocial(boolean social) {
this.social = social;
}
public boolean isUpdateProfileOnInitialSocialLogin() {
return updateProfileOnInitialSocialLogin;
}
public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
}
public int getSsoSessionIdleTimeout() {
return ssoSessionIdleTimeout;
}
public void setSsoSessionIdleTimeout(int ssoSessionIdleTimeout) {
this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
}
public int getSsoSessionMaxLifespan() {
return ssoSessionMaxLifespan;
}
public void setSsoSessionMaxLifespan(int ssoSessionMaxLifespan) {
this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
}
public int getAccessTokenLifespan() {
return accessTokenLifespan;
}
public void setAccessTokenLifespan(int accessTokenLifespan) {
this.accessTokenLifespan = accessTokenLifespan;
}
public int getAccessCodeLifespan() {
return accessCodeLifespan;
}
public void setAccessCodeLifespan(int accessCodeLifespan) {
this.accessCodeLifespan = accessCodeLifespan;
}
public int getAccessCodeLifespanUserAction() {
return accessCodeLifespanUserAction;
}
public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
public void setPublicKeyPem(String publicKeyPem) {
this.publicKeyPem = publicKeyPem;
}
public String getPrivateKeyPem() {
return privateKeyPem;
}
public void setPrivateKeyPem(String privateKeyPem) {
this.privateKeyPem = privateKeyPem;
}
public Collection<RequiredCredentialEntity> getRequiredCredentials() {
return requiredCredentials;
}
public void setRequiredCredentials(Collection<RequiredCredentialEntity> requiredCredentials) {
this.requiredCredentials = requiredCredentials;
}
public List<AuthenticationProviderEntity> getAuthenticationProviders() {
return authenticationProviders;
}
public void setAuthenticationProviders(List<AuthenticationProviderEntity> authenticationProviders) {
this.authenticationProviders = authenticationProviders;
}
public Collection<ApplicationEntity> getApplications() {
return applications;
}
public void setApplications(Collection<ApplicationEntity> applications) {
this.applications = applications;
}
public Collection<RoleEntity> getRoles() {
return roles;
}
public void setRoles(Collection<RoleEntity> roles) {
this.roles = roles;
}
public void addRole(RoleEntity role) {
if (roles == null) {
roles = new ArrayList<RoleEntity>();
}
roles.add(role);
}
public Map<String, String> getSmtpConfig() {
return smtpConfig;
}
public void setSmtpConfig(Map<String, String> smtpConfig) {
this.smtpConfig = smtpConfig;
}
public Map<String, String> getSocialConfig() {
return socialConfig;
}
public void setSocialConfig(Map<String, String> socialConfig) {
this.socialConfig = socialConfig;
}
public Map<String, String> getLdapServerConfig() {
return ldapServerConfig;
}
public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
this.ldapServerConfig = ldapServerConfig;
}
public Collection<RoleEntity> getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
this.defaultRoles = defaultRoles;
}
public String getPasswordPolicy() {
return passwordPolicy;
}
public void setPasswordPolicy(String passwordPolicy) {
this.passwordPolicy = passwordPolicy;
}
public String getLoginTheme() {
return loginTheme;
}
public void setLoginTheme(String theme) {
this.loginTheme = theme;
}
public String getAccountTheme() {
return accountTheme;
}
public void setAccountTheme(String theme) {
this.accountTheme = theme;
}
public String getAdminTheme() {
return adminTheme;
}
public void setAdminTheme(String adminTheme) {
this.adminTheme = adminTheme;
}
public String getEmailTheme() {
return emailTheme;
}
public void setEmailTheme(String emailTheme) {
this.emailTheme = emailTheme;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
public boolean isBruteForceProtected() {
return bruteForceProtected;
}
public void setBruteForceProtected(boolean bruteForceProtected) {
this.bruteForceProtected = bruteForceProtected;
}
public int getMaxFailureWaitSeconds() {
return maxFailureWaitSeconds;
}
public void setMaxFailureWaitSeconds(int maxFailureWaitSeconds) {
this.maxFailureWaitSeconds = maxFailureWaitSeconds;
}
public int getMinimumQuickLoginWaitSeconds() {
return minimumQuickLoginWaitSeconds;
}
public void setMinimumQuickLoginWaitSeconds(int minimumQuickLoginWaitSeconds) {
this.minimumQuickLoginWaitSeconds = minimumQuickLoginWaitSeconds;
}
public int getWaitIncrementSeconds() {
return waitIncrementSeconds;
}
public void setWaitIncrementSeconds(int waitIncrementSeconds) {
this.waitIncrementSeconds = waitIncrementSeconds;
}
public long getQuickLoginCheckMilliSeconds() {
return quickLoginCheckMilliSeconds;
}
public void setQuickLoginCheckMilliSeconds(long quickLoginCheckMilliSeconds) {
this.quickLoginCheckMilliSeconds = quickLoginCheckMilliSeconds;
}
public int getMaxDeltaTimeSeconds() {
return maxDeltaTimeSeconds;
}
public void setMaxDeltaTimeSeconds(int maxDeltaTimeSeconds) {
this.maxDeltaTimeSeconds = maxDeltaTimeSeconds;
}
public int getFailureFactor() {
return failureFactor;
}
public void setFailureFactor(int failureFactor) {
this.failureFactor = failureFactor;
}
public boolean isAuditEnabled() {
return auditEnabled;
}
public void setAuditEnabled(boolean auditEnabled) {
this.auditEnabled = auditEnabled;
}
public long getAuditExpiration() {
return auditExpiration;
}
public void setAuditExpiration(long auditExpiration) {
this.auditExpiration = auditExpiration;
}
public Set<String> getAuditListeners() {
return auditListeners;
}
public void setAuditListeners(Set<String> auditListeners) {
this.auditListeners = auditListeners;
}
public ApplicationEntity getMasterAdminApp() {
return masterAdminApp;
}
public void setMasterAdminApp(ApplicationEntity masterAdminApp) {
this.masterAdminApp = masterAdminApp;
}
}

View file

@ -0,0 +1,64 @@
package org.keycloak.models.realms.jpa.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
public class RequiredCredentialEntity {
@Id
@GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.realms.jpa.utils.JpaIdGenerator")
@GeneratedValue(generator = "keycloak_generator")
protected String id;
protected String type;
protected boolean input;
protected boolean secret;
protected String formLabel;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isInput() {
return input;
}
public void setInput(boolean input) {
this.input = input;
}
public boolean isSecret() {
return secret;
}
public void setSecret(boolean secret) {
this.secret = secret;
}
public String getFormLabel() {
return formLabel;
}
public void setFormLabel(String formLabel) {
this.formLabel = formLabel;
}
}

View file

@ -0,0 +1,152 @@
package org.keycloak.models.realms.jpa.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = { "name", "appRealmConstraint" })
})
@NamedQueries({
@NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
@NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.applicationRole = false and role.name = :name and role.realm = :realm")
})
public class RoleEntity {
@Id
@Column(name="id")
private String id;
private String name;
private String description;
// hax! couldn't get constraint to work properly
private String realmId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "realm")
private RealmEntity realm;
@Column(name="applicationRole")
private boolean applicationRole;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "application")
private ApplicationEntity application;
// Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
private String appRealmConstraint;
@ManyToMany(fetch = FetchType.LAZY, cascade = {})
@JoinTable(name = "CompositeRole", joinColumns = @JoinColumn(name = "composite"), inverseJoinColumns = @JoinColumn(name = "childRole"))
private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.realmId = realmId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Collection<RoleEntity> getCompositeRoles() {
return compositeRoles;
}
public void setCompositeRoles(Collection<RoleEntity> compositeRoles) {
this.compositeRoles = compositeRoles;
}
public boolean isApplicationRole() {
return applicationRole;
}
public void setApplicationRole(boolean applicationRole) {
this.applicationRole = applicationRole;
}
public RealmEntity getRealm() {
return realm;
}
public void setRealm(RealmEntity realm) {
this.realm = realm;
this.appRealmConstraint = realm.getId();
}
public ApplicationEntity getApplication() {
return application;
}
public void setApplication(ApplicationEntity application) {
this.application = application;
if (application != null) {
this.appRealmConstraint = application.getId();
}
}
public String getAppRealmConstraint() {
return appRealmConstraint;
}
public void setAppRealmConstraint(String appRealmConstraint) {
this.appRealmConstraint = appRealmConstraint;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoleEntity that = (RoleEntity) o;
if (!id.equals(that.id)) return false;
return true;
}
@Override
public int hashCode() {
return id.hashCode();
}
}

View file

@ -0,0 +1,60 @@
package org.keycloak.models.realms.jpa.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="hasScope", query="select m from ScopeMappingEntity m where m.client = :client and m.role = :role"),
@NamedQuery(name="clientScopeMappings", query="select m from ScopeMappingEntity m where m.client = :client"),
@NamedQuery(name="clientScopeMappingIds", query="select m.role.id from ScopeMappingEntity m where m.client = :client")
})
@Entity
public class ScopeMappingEntity {
@Id
@GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.realms.jpa.utils.JpaIdGenerator")
@GeneratedValue(generator = "keycloak_generator")
protected String id;
@ManyToOne(fetch= FetchType.LAZY)
protected ClientEntity client;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="roleId")
protected RoleEntity role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public ClientEntity getClient() {
return client;
}
public void setClient(ClientEntity client) {
this.client = client;
}
public RoleEntity getRole() {
return role;
}
public void setRole(RoleEntity role) {
this.role = role;
}
}

View file

@ -0,0 +1,19 @@
package org.keycloak.models.realms.jpa.utils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.io.Serializable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JpaIdGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return KeycloakModelUtils.generateId();
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.realms.jpa.JpaRealmProviderFactory

View file

@ -0,0 +1,70 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
<class>org.keycloak.models.jpa.entities.UserEntity</class>
<class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
<class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
<!--
<persistence-unit name="picketlink-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
<class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
<class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
-->
</persistence>

45
model/sessions-jpa/pom.xml Executable file
View file

@ -0,0 +1,45 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-sessions-jpa</artifactId>
<name>Keycloak Model Sessions JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,53 @@
package org.keycloak.models.sessions.jpa;
import org.keycloak.models.KeycloakTransaction;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaKeycloakTransaction implements KeycloakTransaction {
protected EntityManager em;
public JpaKeycloakTransaction(EntityManager em) {
this.em = em;
}
@Override
public void begin() {
em.getTransaction().begin();
}
@Override
public void commit() {
try {
em.getTransaction().commit();
} catch (PersistenceException e) {
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
}
}
@Override
public void rollback() {
em.getTransaction().rollback();
}
@Override
public void setRollbackOnly() {
em.getTransaction().setRollbackOnly();
}
@Override
public boolean getRollbackOnly() {
return em.getTransaction().getRollbackOnly();
}
@Override
public boolean isActive() {
return em.getTransaction().isActive();
}
}

View file

@ -0,0 +1,155 @@
package org.keycloak.models.sessions.jpa;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.sessions.LoginFailure;
import org.keycloak.models.sessions.Session;
import org.keycloak.models.sessions.SessionProvider;
import org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity;
import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
import org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity;
import org.keycloak.util.Time;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaSessionProvider implements SessionProvider {
protected final EntityManager em;
public JpaSessionProvider(EntityManager em) {
this.em = PersistenceExceptionConverter.create(em);
}
@Override
public LoginFailure getUserLoginFailure(String username, String realm) {
String id = username + "-" + realm;
UsernameLoginFailureEntity entity = em.find(UsernameLoginFailureEntity.class, id);
if (entity == null) return null;
return new UsernameLoginFailureAdapter(entity);
}
@Override
public LoginFailure addUserLoginFailure(String username, String realm) {
LoginFailure model = getUserLoginFailure(username, realm);
if (model != null) return model;
String id = username + "-" + realm;
UsernameLoginFailureEntity entity = new UsernameLoginFailureEntity();
entity.setId(id);
entity.setUsername(username);
entity.setRealm(realm);
em.persist(entity);
return new UsernameLoginFailureAdapter(entity);
}
@Override
public List<LoginFailure> getAllUserLoginFailures(String realm) {
TypedQuery<UsernameLoginFailureEntity> query = em.createNamedQuery("getAllFailures", UsernameLoginFailureEntity.class);
List<UsernameLoginFailureEntity> entities = query.getResultList();
List<LoginFailure> models = new ArrayList<LoginFailure>();
for (UsernameLoginFailureEntity entity : entities) {
models.add(new UsernameLoginFailureAdapter(entity));
}
return models;
}
@Override
public Session createUserSession(String realm, String id, String user, String ipAddress) {
UserSessionEntity entity = new UserSessionEntity();
entity.setRealmId(realm);
entity.setUserId(user);
entity.setIpAddress(ipAddress);
int currentTime = Time.currentTime();
entity.setStarted(currentTime);
entity.setLastSessionRefresh(currentTime);
em.persist(entity);
return new UserSessionAdapter(em, realm, entity);
}
@Override
public Session getUserSession(String id, String realm) {
UserSessionEntity entity = em.find(UserSessionEntity.class, id);
return entity != null ? new UserSessionAdapter(em, realm, entity) : null;
}
@Override
public List<Session> getUserSessionsByUser(String user, String realm) {
List<Session> sessions = new LinkedList<Session>();
for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class)
.setParameter("userId", user).getResultList()) {
sessions.add(new UserSessionAdapter(em, realm, e));
}
return sessions;
}
@Override
public Set<Session> getUserSessionsByClient(String realm, String client) {
Set<Session> list = new HashSet<Session>();
TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
query.setParameter("clientId", client);
List<ClientUserSessionAssociationEntity> results = query.getResultList();
for (ClientUserSessionAssociationEntity entity : results) {
list.add(new UserSessionAdapter(em, realm, entity.getSession()));
}
return list;
}
@Override
public int getActiveUserSessions(String realm, String client) {
Query query = em.createNamedQuery("getActiveClientSessions");
query.setParameter("clientId", client);
Object count = query.getSingleResult();
return ((Number)count).intValue();
}
@Override
public void removeUserSession(Session session) {
em.remove(((UserSessionAdapter) session).getEntity());
}
@Override
public void removeUserSessions(String realm, String user) {
em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user).executeUpdate();
em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user).executeUpdate();
}
@Override
public void removeExpiredUserSessions(String realm, long refreshTimeout, long sessionTimeout) {
TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionExpired", UserSessionEntity.class)
.setParameter("maxTime", sessionTimeout)
.setParameter("idleTime", refreshTimeout);
List<UserSessionEntity> results = query.getResultList();
for (UserSessionEntity entity : results) {
em.remove(entity);
}
}
@Override
public void removeUserSessions(String realm) {
em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm).executeUpdate();
em.createNamedQuery("removeRealmUserSessions").setParameter("realmId", realm).executeUpdate();
}
@Override
public KeycloakTransaction getTransaction() {
return new JpaKeycloakTransaction(em);
}
@Override
public void close() {
if (em.getTransaction().isActive()) em.getTransaction().rollback();
if (em.isOpen()) em.close();
}
}

View file

@ -0,0 +1,40 @@
package org.keycloak.models.sessions.jpa;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.sessions.SessionProvider;
import org.keycloak.models.sessions.SessionProviderFactory;
import org.keycloak.util.JpaUtils;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaSessionProviderFactory implements SessionProviderFactory {
protected EntityManagerFactory emf;
@Override
public void init(Config.Scope config) {
String persistenceUnit = config.get("persistenceUnit", "jpa-keycloak-identity-store");
emf = Persistence.createEntityManagerFactory(persistenceUnit, JpaUtils.getHibernateProperties());
}
@Override
public String getId() {
return "jpa";
}
@Override
public SessionProvider create(KeycloakSession session) {
return new JpaSessionProvider(emf.createEntityManager());
}
@Override
public void close() {
emf.close();
}
}

View file

@ -0,0 +1,48 @@
package org.keycloak.models.sessions.jpa;
import org.hibernate.exception.ConstraintViolationException;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PersistenceExceptionConverter implements InvocationHandler {
private EntityManager em;
public static EntityManager create(EntityManager em) {
return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
}
private PersistenceExceptionConverter(EntityManager em) {
this.em = em;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(em, args);
} catch (InvocationTargetException e) {
throw convert(e.getCause());
}
}
public static ModelException convert(Throwable t) {
if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
throw new ModelDuplicateException(t);
} if (t instanceof EntityExistsException) {
throw new ModelDuplicateException(t);
} else {
throw new ModelException(t);
}
}
}

View file

@ -0,0 +1,122 @@
package org.keycloak.models.sessions.jpa;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.sessions.Session;
import org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity;
import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserSessionAdapter implements Session {
private String realm;
private UserSessionEntity entity;
private EntityManager em;
public UserSessionAdapter(EntityManager em, String realm, UserSessionEntity entity) {
this.entity = entity;
this.em = em;
this.realm = realm;
}
public UserSessionEntity getEntity() {
return entity;
}
@Override
public String getId() {
return entity.getId();
}
@Override
public void setId(String id) {
entity.setId(id);
}
@Override
public String getUser() {
return entity.getUserId();
}
@Override
public void setUser(String user) {
entity.setUserId(user);
}
@Override
public String getIpAddress() {
return entity.getIpAddress();
}
@Override
public void setIpAddress(String ipAddress) {
entity.setIpAddress(ipAddress);
}
@Override
public int getStarted() {
return entity.getStarted();
}
@Override
public void setStarted(int started) {
entity.setStarted(started);
}
@Override
public int getLastSessionRefresh() {
return entity.getLastSessionRefresh();
}
@Override
public void setLastSessionRefresh(int seconds) {
entity.setLastSessionRefresh(seconds);
}
@Override
public void associateClient(String client) {
for (ClientUserSessionAssociationEntity ass : entity.getClients()) {
if (ass.getClientId().equals(client)) return;
}
ClientUserSessionAssociationEntity association = new ClientUserSessionAssociationEntity();
association.setClientId(client);
association.setSession(entity);
association.setUserId(entity.getUserId());
association.setRealmId(realm);
em.persist(association);
entity.getClients().add(association);
}
@Override
public void removeAssociatedClient(String client) {
em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", client).executeUpdate();
}
@Override
public List<String> getClientAssociations() {
List<String> clients = new ArrayList<String>();
for (ClientUserSessionAssociationEntity association : entity.getClients()) {
clients.add(association.getClientId());
}
return clients;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof UserSessionModel)) return false;
UserSessionModel that = (UserSessionModel) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
}

View file

@ -0,0 +1,69 @@
package org.keycloak.models.sessions.jpa;
import org.keycloak.models.sessions.LoginFailure;
import org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UsernameLoginFailureAdapter implements LoginFailure
{
protected UsernameLoginFailureEntity user;
public UsernameLoginFailureAdapter(UsernameLoginFailureEntity user) {
this.user = user;
}
@Override
public String getUsername()
{
return user.getUsername();
}
@Override
public int getFailedLoginNotBefore() {
return user.getFailedLoginNotBefore();
}
@Override
public void setFailedLoginNotBefore(int notBefore) {
user.setFailedLoginNotBefore(notBefore);
}
@Override
public int getNumFailures() {
return user.getNumFailures();
}
@Override
public void incrementFailures() {
user.setNumFailures(getNumFailures() + 1);
}
@Override
public void clearFailures() {
user.setNumFailures(0);
}
@Override
public long getLastFailure() {
return user.getLastFailure();
}
@Override
public void setLastFailure(long lastFailure) {
user.setLastFailure(lastFailure);
}
@Override
public String getLastIPFailure() {
return user.getLastIPFailure();
}
@Override
public void setLastIPFailure(String ip) {
user.setLastIPFailure(ip);
}
}

View file

@ -0,0 +1,84 @@
package org.keycloak.models.sessions.jpa.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
@Table(name = "ClientUserSessionAscEntity")
@NamedQueries({
@NamedQuery(name = "getAllClientUserSessions", query = "select s from ClientUserSessionAssociationEntity s"),
@NamedQuery(name = "getClientUserSessionBySession", query = "select s from ClientUserSessionAssociationEntity s where s.session = :session"),
@NamedQuery(name = "getClientUserSessionByClient", query = "select s from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
@NamedQuery(name = "getActiveClientSessions", query = "select COUNT(s) from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
@NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
@NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity s where s.userId = :userId"),
@NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity s where s.realmId = :realmId")})
public class ClientUserSessionAssociationEntity {
@Id
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.sessions.jpa.utils.JpaIdGenerator")
@GeneratedValue(generator = "uuid_generator")
private String id;
// we use ids to avoid select for update contention
private String userId;
private String realmId;
@ManyToOne(fetch= FetchType.LAZY)
private UserSessionEntity session;
private String clientId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public UserSessionEntity getSession() {
return session;
}
public void setSession(UserSessionEntity session) {
this.session = session;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.realmId = realmId;
}
}

View file

@ -0,0 +1,105 @@
package org.keycloak.models.sessions.jpa.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Entity
@NamedQueries({
@NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.userId = :userId"),
@NamedQuery(name = "removeRealmUserSessions", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
@NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.userId = :userId"),
@NamedQuery(name = "getUserSessionExpired", query = "select s from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime"),
@NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
})
public class UserSessionEntity {
@Id
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.sessions.jpa.utils.JpaIdGenerator")
@GeneratedValue(generator = "uuid_generator")
private String id;
// we use ids to avoid select for update contention
private String userId;
private String realmId;
private String ipAddress;
private int started;
private int lastSessionRefresh;
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="session")
private Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.realmId = realmId;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getStarted() {
return started;
}
public void setStarted(int started) {
this.started = started;
}
public int getLastSessionRefresh() {
return lastSessionRefresh;
}
public void setLastSessionRefresh(int lastSessionRefresh) {
this.lastSessionRefresh = lastSessionRefresh;
}
public Collection<ClientUserSessionAssociationEntity> getClients() {
return clients;
}
public void setClients(Collection<ClientUserSessionAssociationEntity> clients) {
this.clients = clients;
}
}

View file

@ -0,0 +1,86 @@
package org.keycloak.models.sessions.jpa.entities;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
@NamedQueries({
@NamedQuery(name="getAllFailures", query="select failure from UsernameLoginFailureEntity failure"),
})
public class UsernameLoginFailureEntity {
// we manually set the id to be username-realmid
// we may have a concurrent creation of the same login failure entry that we want to avoid
@Id
protected String id;
protected String username;
protected int failedLoginNotBefore;
protected int numFailures;
protected long lastFailure;
protected String lastIPFailure;
protected String realm;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getFailedLoginNotBefore() {
return failedLoginNotBefore;
}
public void setFailedLoginNotBefore(int failedLoginNotBefore) {
this.failedLoginNotBefore = failedLoginNotBefore;
}
public int getNumFailures() {
return numFailures;
}
public void setNumFailures(int numFailures) {
this.numFailures = numFailures;
}
public long getLastFailure() {
return lastFailure;
}
public void setLastFailure(long lastFailure) {
this.lastFailure = lastFailure;
}
public String getLastIPFailure() {
return lastIPFailure;
}
public void setLastIPFailure(String lastIPFailure) {
this.lastIPFailure = lastIPFailure;
}
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
}

View file

@ -0,0 +1,19 @@
package org.keycloak.models.sessions.jpa.utils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.io.Serializable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JpaIdGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return KeycloakModelUtils.generateId();
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.sessions.jpa.JpaSessionProviderFactory

35
model/sessions-mem/pom.xml Executable file
View file

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-sessions-mem</artifactId>
<name>Keycloak Model Sessions Mem</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,81 @@
package org.keycloak.models.sessions.mem;
import org.keycloak.models.sessions.LoginFailure;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class LoginFailureAdapter implements LoginFailure {
private final String username;
private final String realm;
private AtomicInteger failedLoginNotBefore = new AtomicInteger();
private AtomicInteger numFailures = new AtomicInteger();
private AtomicLong lastFailure = new AtomicLong();
private AtomicReference<String> lastIpFailure = new AtomicReference<String>();
public LoginFailureAdapter(String username, String realm) {
this.username = username;
this.realm = realm;
}
@Override
public String getUsername() {
return username;
}
public String getRealm() {
return realm;
}
@Override
public int getFailedLoginNotBefore() {
return failedLoginNotBefore.get();
}
@Override
public void setFailedLoginNotBefore(int notBefore) {
failedLoginNotBefore.set(notBefore);
}
@Override
public int getNumFailures() {
return numFailures.get();
}
@Override
public void incrementFailures() {
numFailures.incrementAndGet();
}
@Override
public void clearFailures() {
numFailures.set(0);
}
@Override
public long getLastFailure() {
return lastFailure.get();
}
@Override
public void setLastFailure(long lastFailure) {
this.lastFailure.set(lastFailure);
}
@Override
public String getLastIPFailure() {
return lastIpFailure.get();
}
@Override
public void setLastIPFailure(String ip) {
lastIpFailure.set(ip);
}
}

View file

@ -0,0 +1,36 @@
package org.keycloak.models.sessions.mem;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class LoginFailureKey {
private final String realm;
private final String username;
public LoginFailureKey(String realm, String username) {
this.realm = realm;
this.username = username;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LoginFailureKey key = (LoginFailureKey) o;
if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
if (username != null ? !username.equals(key.username) : key.username != null) return false;
return true;
}
@Override
public int hashCode() {
int result = realm != null ? realm.hashCode() : 0;
result = 31 * result + (username != null ? username.hashCode() : 0);
return result;
}
}

View file

@ -0,0 +1,194 @@
package org.keycloak.models.sessions.mem;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.sessions.LoginFailure;
import org.keycloak.models.sessions.Session;
import org.keycloak.models.sessions.SessionProvider;
import org.keycloak.util.Time;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class MemSessionProvider implements SessionProvider {
private final ConcurrentHashMap<SessionKey, SessionAdapter> sessions;
private final ConcurrentHashMap<LoginFailureKey, LoginFailureAdapter> loginFailures;
private DummyKeycloakTransaction tx;
public MemSessionProvider(ConcurrentHashMap<SessionKey, SessionAdapter> sessions, ConcurrentHashMap<LoginFailureKey, LoginFailureAdapter> loginFailures) {
this.sessions = sessions;
this.loginFailures = loginFailures;
}
@Override
public LoginFailure getUserLoginFailure(String username, String realm) {
return loginFailures.get(new LoginFailureKey(username, realm));
}
@Override
public LoginFailure addUserLoginFailure(String username, String realm) {
LoginFailureKey key = new LoginFailureKey(username, realm);
return loginFailures.putIfAbsent(key, new LoginFailureAdapter(username, realm));
}
@Override
public List<LoginFailure> getAllUserLoginFailures(String realm) {
List<LoginFailure> failures = new LinkedList<LoginFailure>();
for (LoginFailureAdapter failure : loginFailures.values()) {
if (failure.getRealm().equals(realm)) {
failures.add(failure);
}
}
return failures;
}
@Override
public Session createUserSession(String realm, String id, String user, String ipAddress) {
SessionAdapter adapter = new SessionAdapter();
adapter.setRealm(realm);
adapter.setId(id);
adapter.setUser(user);
adapter.setIpAddress(ipAddress);
int currentTime = Time.currentTime();
adapter.setStarted(currentTime);
adapter.setLastSessionRefresh(currentTime);
sessions.put(new SessionKey(realm, id), adapter);
return adapter;
}
@Override
public Session getUserSession(String id, String realm) {
return sessions.get(new SessionKey(realm, id));
}
@Override
public List<Session> getUserSessionsByUser(String user, String realm) {
List<Session> userSessions = new LinkedList<Session>();
for (SessionAdapter s : sessions.values()) {
if (s.getRealm().equals(realm) && s.getUser().equals(user)) {
userSessions.add(s);
}
}
return userSessions;
}
@Override
public Set<Session> getUserSessionsByClient(String realm, String client) {
Set<Session> clientSessions = new HashSet<Session>();
for (SessionAdapter s : sessions.values()) {
if (s.getRealm().equals(realm) && s.getClientAssociations().contains(client)) {
clientSessions.add(s);
}
}
return clientSessions;
}
@Override
public int getActiveUserSessions(String realm, String client) {
int count = 0;
for (SessionAdapter s : sessions.values()) {
if (s.getRealm().equals(realm) && s.getClientAssociations().contains(client)) {
count++;
}
}
return count;
}
@Override
public void removeUserSession(Session session) {
String realm = ((SessionAdapter) session).getRealm();
sessions.remove(new SessionKey(realm, session.getId()));
}
@Override
public void removeUserSessions(String realm, String user) {
Iterator<SessionAdapter> itr = sessions.values().iterator();
while (itr.hasNext()) {
SessionAdapter s = itr.next();
if (s.getRealm().equals(realm) && s.getUser().equals(user)) {
itr.remove();
}
}
}
@Override
public void removeExpiredUserSessions(String realm, long refreshTimeout, long sessionTimeout) {
Iterator<SessionAdapter> itr = sessions.values().iterator();
while (itr.hasNext()) {
SessionAdapter s = itr.next();
if (s.getLastSessionRefresh() < refreshTimeout || s.getStarted() < sessionTimeout) {
itr.remove();
}
}
}
@Override
public void removeUserSessions(String realm) {
Iterator<SessionAdapter> itr = sessions.values().iterator();
while (itr.hasNext()) {
SessionAdapter s = itr.next();
if (s.getRealm().equals(realm)) {
itr.remove();
}
}
}
@Override
public KeycloakTransaction getTransaction() {
if (tx == null) {
tx = new DummyKeycloakTransaction();
}
return tx;
}
@Override
public void close() {
}
public static class DummyKeycloakTransaction implements KeycloakTransaction {
public boolean rollBackOnly;
public boolean active;
@Override
public void begin() {
this.active = true;
}
@Override
public void commit() {
}
@Override
public void rollback() {
}
@Override
public void setRollbackOnly() {
this.rollBackOnly = true;
}
@Override
public boolean getRollbackOnly() {
return rollBackOnly;
}
@Override
public boolean isActive() {
return active;
}
}
}

View file

@ -0,0 +1,43 @@
package org.keycloak.models.sessions.mem;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.sessions.LoginFailure;
import org.keycloak.models.sessions.SessionProvider;
import org.keycloak.models.sessions.SessionProviderFactory;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class MemSessionProviderFactory implements SessionProviderFactory {
private ConcurrentHashMap<SessionKey, SessionAdapter> sessions = new ConcurrentHashMap<SessionKey, SessionAdapter>();
private ConcurrentHashMap<LoginFailureKey, LoginFailureAdapter> loginFailures = new ConcurrentHashMap<LoginFailureKey, LoginFailureAdapter>();
@Override
public SessionProvider create(KeycloakSession session) {
return new MemSessionProvider(sessions, loginFailures);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void close() {
sessions.clear();
loginFailures.clear();
}
@Override
public String getId() {
return "mem";
}
}

View file

@ -0,0 +1,86 @@
package org.keycloak.models.sessions.mem;
import org.keycloak.models.sessions.Session;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class SessionAdapter implements Session {
private String realm;
private String id;
private String user;
private String ipAddress;
private int started;
private int lastSessionRefresh;
private List<String> clients = new LinkedList<String>();
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getStarted() {
return started;
}
public void setStarted(int started) {
this.started = started;
}
public int getLastSessionRefresh() {
return lastSessionRefresh;
}
public void setLastSessionRefresh(int lastSessionRefresh) {
this.lastSessionRefresh = lastSessionRefresh;
}
@Override
public void associateClient(String client) {
if (!clients.contains(client)) {
clients.add(client);
}
}
@Override
public List<String> getClientAssociations() {
return clients;
}
@Override
public void removeAssociatedClient(String client) {
clients.remove(client);
}
}

View file

@ -0,0 +1,36 @@
package org.keycloak.models.sessions.mem;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class SessionKey {
private final String realm;
private final String id;
public SessionKey(String realm, String id) {
this.realm = realm;
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SessionKey key = (SessionKey) o;
if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
if (id != null ? !id.equals(key.id) : key.id != null) return false;
return true;
}
@Override
public int hashCode() {
int result = realm != null ? realm.hashCode() : 0;
result = 31 * result + (id != null ? id.hashCode() : 0);
return result;
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.sessions.mem.MemSessionProviderFactory

55
model/tests-hybrid/pom.xml Executable file
View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-tests-hybrid</artifactId>
<name>Keycloak Model Tests</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,274 @@
package org.keycloak.model.test;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.users.Credentials;
import org.keycloak.models.users.Feature;
import org.keycloak.models.users.User;
import org.keycloak.models.users.UserProvider;
import org.keycloak.models.users.UserProviderFactory;
import org.keycloak.models.users.UserSpi;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.provider.ProviderFactory;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public abstract class AbstractUserProviderTest {
private ProviderFactory<UserProvider> factory;
private UserProvider provider;
private String userId;
private String userId2;
private String userId3;
@Before
public void before() {
String providerId = getProviderId();
ServiceLoader<UserProviderFactory> factories = ServiceLoader.load(UserProviderFactory.class);
for (UserProviderFactory f : factories) {
if (f.getId().equals(providerId)) {
factory = f;
factory.init(Config.scope(new UserSpi().getName(), providerId));
}
}
provider = factory.create(null);
userId = "1";
userId2 = "2";
userId3 = "1";
}
@After
public void after() {
provider.getTransaction().begin();
provider.onRealmRemoved("test-realm");
provider.getTransaction().commit();
}
protected abstract String getProviderId();
@Test
public void persistUsers() {
provider.getTransaction().begin();
Set<String> roles = new HashSet<String>();
roles.add("a");
roles.add("a1");
User user = provider.addUser(userId, "user", roles, "test-realm");
user.setFirstName("first-name");
user.setLastName("last-name");
user.setEmail("email");
user.setAttribute("a", "a1");
user.setAttribute("b", "b1");
Set<String> roles2 = new HashSet<String>();
roles2.add("a");
roles2.add("a2");
User user2 = provider.addUser(userId2, "user2", roles2, "test-realm");
user2.setFirstName("first-name2");
user2.setLastName("last-name2");
user2.setEmail("email2");
user2.setAttribute("a", "a2");
user2.setAttribute("b", "b2");
User user3 = provider.addUser(userId3, "user", roles2, "test-realm2");
user3.setFirstName("first-name");
user3.setLastName("last-name");
user3.setEmail("email");
user3.setAttribute("a", "a1");
user3.setAttribute("b", "b1");
provider.getTransaction().commit();
User persisted = provider.getUserById(userId, "test-realm");
User persisted2 = provider.getUserById(userId2, "test-realm");
User persisted3 = provider.getUserById(userId3, "test-realm2");
assertUser(user, persisted);
assertUser(user2, persisted2);
assertUser(user3, persisted3);
}
@Test
public void getUserByEmail() {
persistUsers();
assertEquals(userId, provider.getUserByEmail("email", "test-realm").getId());
assertEquals(userId2, provider.getUserByEmail("email2", "test-realm").getId());
}
@Test
public void getUserByUsername() {
persistUsers();
assertEquals(userId, provider.getUserByUsername("user", "test-realm").getId());
assertEquals(userId2, provider.getUserByUsername("user2", "test-realm").getId());
}
@Test
public void readAndUpdateCredentials() {
if (provider.supports(Feature.READ_CREDENTIALS) && provider.supports(Feature.UPDATE_CREDENTIALS)) {
persistUsers();
provider.getTransaction().begin();
User user = provider.getUserById(userId, "test-realm");
User user2 = provider.getUserById(userId2, "test-realm");
User user3 = provider.getUserById(userId3, "test-realm2");
byte[] salt = Pbkdf2PasswordEncoder.getSalt();
Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(salt);
user.updateCredential(new Credentials(UserCredentialModel.PASSWORD, salt, encoder.encode("password", 1000), 1000, null));
user.updateCredential(new Credentials(UserCredentialModel.TOTP, "totp-secret", null));
user2.updateCredential(new Credentials(UserCredentialModel.PASSWORD, salt, encoder.encode("password2", 1000), 1000, null));
user3.updateCredential(new Credentials(UserCredentialModel.PASSWORD, salt, encoder.encode("password3", 1000), 1000, null));
provider.getTransaction().commit();
User persisted = provider.getUserById(userId, "test-realm");
assertEquals(2, persisted.getCredentials().size());
assertPassword(persisted, "password");
assertTotp(user, "totp-secret");
User persisted2 = provider.getUserById(userId2, "test-realm");
assertEquals(1, persisted2.getCredentials().size());
assertPassword(persisted2, "password2");
User persisted3 = provider.getUserById(userId3, "test-realm2");
assertEquals(1, persisted3.getCredentials().size());
assertPassword(persisted3, "password3");
}
}
@Test
public void userSearch() throws Exception {
provider.getTransaction().begin();
{
User user = provider.addUser("bill-burke", "bill-burke", null, "test-realm");
user.setLastName("Burke");
user.setFirstName("Bill");
user.setEmail("bburke@redhat.com");
User user2 = provider.addUser("knut-ole", "knut-ole", null, "test-realm");
user2.setFirstName("Knut Ole");
user2.setLastName("Alver");
user2.setEmail("knut@redhat.com");
User user3 = provider.addUser("ole-alver", "ole-alver", null, "test-realm");
user3.setFirstName("Ole");
user3.setLastName("Alver Veland");
user3.setEmail("knut2@redhat.com");
}
provider.getTransaction().commit();
assertUsers(provider.searchForUser("total junk query", "test-realm"));
assertUsers(provider.searchForUser("Bill Burke", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("bill burk", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("bill burk", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("ole alver", "test-realm"), "knut-ole", "ole-alver");
assertUsers(provider.searchForUser("bburke@redhat.com", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("rke@redhat.com", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("bburke", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("BurK", "test-realm"), "bill-burke");
assertUsers(provider.searchForUser("Burke", "test-realm"), "bill-burke");
provider.getTransaction().begin();
{
User user = provider.addUser("monica-burke", "monica-burke", null, "test-realm");
user.setLastName("Burke");
user.setFirstName("Monica");
user.setEmail("mburke@redhat.com");
}
{
User user = provider.addUser("stian-thorgersen", "stian-thorgersen", null, "test-realm");
user.setLastName("Thorgersen");
user.setFirstName("Stian");
user.setEmail("thor@redhat.com");
}
provider.getTransaction().commit();
assertUsers(provider.searchForUser("Monica Burke", "test-realm"), "monica-burke");
assertUsers(provider.searchForUser("mburke@redhat.com", "test-realm"), "monica-burke");
assertUsers(provider.searchForUser("mburke", "test-realm"), "monica-burke");
assertUsers(provider.searchForUser("Burke", "test-realm"), "bill-burke", "monica-burke");
provider.getTransaction().begin();
provider.addUser("bill-burke", "bill-burke", null, "test-realm2");
provider.getTransaction().commit();
Assert.assertEquals(1, provider.getUsers("test-realm2").size());
assertUsers(provider.searchForUser("Burke", "test-realm2"), "bill-burke");
}
private static void assertUsers(List<User> users, String... expectedIds) {
if (expectedIds == null) {
expectedIds = new String[0];
}
String[] actualIds = new String[users.size()];
for (int i = 0; i < users.size(); i++) {
actualIds[i] = users.get(i).getId();
}
Arrays.sort(actualIds);
Arrays.sort(expectedIds);
assertArrayEquals(expectedIds, actualIds);
}
public static void assertUser(User expected, User actual) {
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getUsername(), actual.getUsername());
assertEquals(expected.getFirstName(), actual.getFirstName());
assertEquals(expected.getLastName(), actual.getLastName());
assertEquals(expected.getEmail(), actual.getEmail());
assertEquals(expected.getAttributes(), actual.getAttributes());
assertEquals(expected.getRoleMappings(), actual.getRoleMappings());
}
public static void assertPassword(User user, String expectedPassword) {
for (Credentials cred : user.getCredentials()) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
assertTrue("Invalid credentials", new Pbkdf2PasswordEncoder(cred.getSalt(), 1000).verify(expectedPassword, cred.getValue()));
return;
}
}
fail("Password credentials not found");
}
public static void assertTotp(User user, String expectedTotpSecret) {
for (Credentials cred : user.getCredentials()) {
if (cred.getType().equals(UserCredentialModel.TOTP)) {
assertEquals(expectedTotpSecret, cred.getValue());
return;
}
}
fail("Totp credentials not found");
}
}

79
model/users-jpa/pom.xml Executable file
View file

@ -0,0 +1,79 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-users-jpa</artifactId>
<name>Keycloak Model Users JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-tests-hybrid</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,53 @@
package org.keycloak.models.users.jpa;
import org.keycloak.models.KeycloakTransaction;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaKeycloakTransaction implements KeycloakTransaction {
protected EntityManager em;
public JpaKeycloakTransaction(EntityManager em) {
this.em = em;
}
@Override
public void begin() {
em.getTransaction().begin();
}
@Override
public void commit() {
try {
em.getTransaction().commit();
} catch (PersistenceException e) {
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
}
}
@Override
public void rollback() {
em.getTransaction().rollback();
}
@Override
public void setRollbackOnly() {
em.getTransaction().setRollbackOnly();
}
@Override
public boolean getRollbackOnly() {
return em.getTransaction().getRollbackOnly();
}
@Override
public boolean isActive() {
return em.getTransaction().isActive();
}
}

View file

@ -0,0 +1,184 @@
package org.keycloak.models.users.jpa;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.users.Credentials;
import org.keycloak.models.users.Feature;
import org.keycloak.models.users.User;
import org.keycloak.models.users.UserProvider;
import org.keycloak.models.users.jpa.entities.UserEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaUserProvider implements UserProvider {
protected final EntityManager em;
public JpaUserProvider(EntityManager em) {
this.em = PersistenceExceptionConverter.create(em);
}
@Override
public KeycloakTransaction getTransaction() {
return new JpaKeycloakTransaction(em);
}
@Override
public User addUser(String id, String username, Set<String> initialRoles, String realm) {
UserEntity entity = new UserEntity();
entity.setId(id);
entity.setUsername(username);
entity.setEmailConstraint(id);
entity.setRealm(realm);
em.persist(entity);
UserAdapter adapter = new UserAdapter(realm, em, entity);
if (initialRoles != null && !initialRoles.isEmpty()) {
for (String role : initialRoles) {
adapter.grantRole(role);
}
}
return adapter;
}
@Override
public boolean removeUser(String name, String realm) {
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByLoginName", UserEntity.class);
query.setParameter("username", name);
query.setParameter("realm", realm);
List<UserEntity> results = query.getResultList();
if (results.size() == 0) return false;
em.remove(results.get(0));
return true;
}
@Override
public User getUserById(String id, String realm) {
UserEntity user = em.find(UserEntity.class, new UserEntity.Key(id, realm));
return user != null ? new UserAdapter(realm, em, user) : null;
}
@Override
public User getUserByUsername(String username, String realm) {
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
query.setParameter("username", username);
query.setParameter("realm", realm);
List<UserEntity> results = query.getResultList();
if (results.size() == 0) return null;
return new UserAdapter(realm, em, results.get(0));
}
@Override
public User getUserByEmail(String email, String realm) {
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
query.setParameter("email", email);
query.setParameter("realm", realm);
List<UserEntity> results = query.getResultList();
return results.isEmpty() ? null : new UserAdapter(realm, em, results.get(0));
}
@Override
public User getUserByAttribute(String name, String value, String realm) {
List<UserEntity> results = em.createNamedQuery("getRealmUserByAttribute", UserEntity.class)
.setParameter("realm", realm)
.setParameter("name", name)
.setParameter("value", value)
.getResultList();
return results.isEmpty() ? null : new UserAdapter(realm, em, results.get(0));
}
@Override
public void close() {
if (em.getTransaction().isActive()) em.getTransaction().rollback();
if (em.isOpen()) em.close();
}
@Override
public List<User> getUsers(String realm) {
TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
query.setParameter("realm", realm);
List<UserEntity> results = query.getResultList();
List<User> users = new ArrayList<User>();
for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
return users;
}
@Override
public List<User> searchForUser(String search, String realm) {
TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search )", UserEntity.class);
query.setParameter("realm", realm);
query.setParameter("search", "%" + search.toLowerCase() + "%");
List<UserEntity> results = query.getResultList();
List<User> users = new ArrayList<User>();
for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
return users;
}
@Override
public List<User> searchForUserByAttributes(Map<String, String> attributes, String realm) {
StringBuilder builder = new StringBuilder("select u from UserEntity u");
boolean first = true;
for (Map.Entry<String, String> entry : attributes.entrySet()) {
String attribute = null;
if (entry.getKey().equals(User.USERNAME)) {
attribute = "lower(username)";
} else if (entry.getKey().equalsIgnoreCase(User.FIRST_NAME)) {
attribute = "lower(firstName)";
} else if (entry.getKey().equalsIgnoreCase(User.LAST_NAME)) {
attribute = "lower(lastName)";
} else if (entry.getKey().equalsIgnoreCase(User.EMAIL)) {
attribute = "lower(email)";
}
if (attribute == null) continue;
if (first) {
first = false;
builder.append(" where realm = :realm");
} else {
builder.append(" and ");
}
builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
}
String q = builder.toString();
TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
query.setParameter("realm", realm);
List<UserEntity> results = query.getResultList();
List<User> users = new ArrayList<User>();
for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
return users;
}
@Override
public boolean supports(Feature feature) {
return (feature == Feature.READ_CREDENTIALS || feature == Feature.UPDATE_CREDENTIALS);
}
@Override
public boolean verifyCredentials(User user, Credentials... credentials) {
return false;
}
@Override
public void onRealmRemoved(String realm) {
TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
query.setParameter("realm", realm);
for (UserEntity u : query.getResultList()) {
em.remove(u);
}
}
@Override
public void onRoleRemoved(String role) {
em.createQuery("delete from RoleEntity r where r.role = :role").setParameter("role", role).executeUpdate();
}
}

View file

@ -0,0 +1,44 @@
package org.keycloak.models.users.jpa;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.users.Feature;
import org.keycloak.models.users.UserProvider;
import org.keycloak.models.users.UserProviderFactory;
import org.keycloak.util.JpaUtils;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaUserProviderFactory implements UserProviderFactory {
public static final String ID = "jpa";
protected EntityManagerFactory emf;
@Override
public void init(Config.Scope config) {
String persistenceUnit = config.get("persistenceUnit", "jpa-keycloak-identity-store");
emf = Persistence.createEntityManagerFactory(persistenceUnit, JpaUtils.getHibernateProperties());
}
@Override
public String getId() {
return ID;
}
@Override
public UserProvider create(KeycloakSession session) {
return new JpaUserProvider(emf.createEntityManager());
}
@Override
public void close() {
emf.close();
}
}

View file

@ -0,0 +1,48 @@
package org.keycloak.models.users.jpa;
import org.hibernate.exception.ConstraintViolationException;
import org.keycloak.models.ModelException;
import org.keycloak.models.ModelDuplicateException;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PersistenceExceptionConverter implements InvocationHandler {
private EntityManager em;
public static EntityManager create(EntityManager em) {
return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
}
private PersistenceExceptionConverter(EntityManager em) {
this.em = em;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(em, args);
} catch (InvocationTargetException e) {
throw convert(e.getCause());
}
}
public static ModelException convert(Throwable t) {
if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
throw new ModelDuplicateException(t);
} if (t instanceof EntityExistsException) {
throw new ModelDuplicateException(t);
} else {
throw new ModelException(t);
}
}
}

View file

@ -0,0 +1,215 @@
package org.keycloak.models.users.jpa;
import org.keycloak.models.UserModel;
import org.keycloak.models.users.Credentials;
import org.keycloak.models.users.User;
import org.keycloak.models.users.jpa.entities.UserAttributeEntity;
import org.keycloak.models.users.jpa.entities.UserCredentialEntity;
import org.keycloak.models.users.jpa.entities.UserRoleMappingEntity;
import org.keycloak.models.users.jpa.entities.UserEntity;
import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAdapter implements User {
protected UserEntity user;
protected EntityManager em;
protected String realm;
public UserAdapter(String realm, EntityManager em, UserEntity user) {
this.em = em;
this.user = user;
this.realm = realm;
}
public UserEntity getUser() {
return user;
}
@Override
public String getId() {
return user.getId();
}
@Override
public boolean isEnabled() {
return user.isEnabled();
}
@Override
public void setEnabled(boolean enabled) {
user.setEnabled(enabled);
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public void setUsername(String username) {
user.setUsername(username);
}
@Override
public String getFirstName() {
return user.getFirstName();
}
@Override
public void setFirstName(String firstName) {
user.setFirstName(firstName);
}
@Override
public String getLastName() {
return user.getLastName();
}
@Override
public void setLastName(String lastName) {
user.setLastName(lastName);
}
@Override
public String getEmail() {
return user.getEmail();
}
@Override
public void setEmail(String email) {
user.setEmail(email);
}
@Override
public void setAttribute(String name, String value) {
List<UserAttributeEntity> attributes = user.getAttributes();
for (UserAttributeEntity a : user.getAttributes()) {
if (a.getName().equals(name)) {
a.setValue(value);
return;
}
}
attributes.add(new UserAttributeEntity(user, name, value));
}
@Override
public String getAttribute(String name) {
for (UserAttributeEntity a : user.getAttributes()) {
if (a.getName().equals(name)) {
return a.getValue();
}
}
return null;
}
@Override
public Map<String, String> getAttributes() {
Map<String, String> result = new HashMap<String, String>();
for (UserAttributeEntity a : user.getAttributes()) {
result.put(a.getName(), a.getValue());
}
return result;
}
@Override
public void removeAttribute(String name) {
Iterator<UserAttributeEntity> itr = user.getAttributes().iterator();
while(itr.hasNext()) {
if (itr.next().getName().equals(name)) {
itr.remove();
return;
}
}
}
private UserCredentialEntity getCredentialEntity(String credType) {
for (UserCredentialEntity entity : user.getCredentials()) {
if (entity.getType().equals(credType)) {
return entity;
}
}
return null;
}
@Override
public List<Credentials> getCredentials() {
List<Credentials> result = new ArrayList<Credentials>();
for (UserCredentialEntity entity : user.getCredentials()) {
result.add(new Credentials(entity.getType(), entity.getSalt(), entity.getValue(), entity.getHashIterations(), entity.getDevice()));
}
return result;
}
@Override
public void updateCredential(Credentials credentials) {
UserCredentialEntity entity = getCredentialEntity(credentials.getType());
if (entity == null) {
entity = new UserCredentialEntity(user, credentials.getType());
user.getCredentials().add(entity);
}
entity.setValue(credentials.getValue());
entity.setSalt(credentials.getSalt());
entity.setHashIterations(credentials.getHashIterations());
entity.setDevice(credentials.getDevice());
}
@Override
public void grantRole(String role) {
for (UserRoleMappingEntity r : user.getRoles()) {
if (r.getRole().equals(role)) {
return;
}
}
user.getRoles().add(new UserRoleMappingEntity(user, role));
}
@Override
public Set<String> getRoleMappings() {
Set<String> roles = new HashSet<String>();
for (UserRoleMappingEntity r : user.getRoles()) {
roles.add(r.getRole());
}
return roles;
}
@Override
public void deleteRoleMapping(String role) {
Iterator<UserRoleMappingEntity> itr = user.getRoles().iterator();
while (itr.hasNext()) {
if (itr.next().getRole().equals(role)) {
itr.remove();
return;
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof UserModel)) return false;
UserModel that = (UserModel) o;
return that.getId().equals(getId());
}
@Override
public int hashCode() {
return getId().hashCode();
}
}

View file

@ -0,0 +1,79 @@
package org.keycloak.models.users.jpa.entities;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import java.io.Serializable;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Entity
@IdClass(UserAttributeEntity.Key.class)
public class UserAttributeEntity {
@Id
protected String name;
@Id
@ManyToOne
protected UserEntity user;
protected String value;
public UserAttributeEntity() {
}
public UserAttributeEntity(UserEntity user, String name, String value) {
this.user = user;
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public static class Key implements Serializable {
private String name;
private UserEntity user;
public Key() {
}
public Key(String name, UserEntity user) {
this.name = name;
this.user = user;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (name != null ? !name.equals(key.name) : key.name != null) return false;
if (user != null ? !user.equals(key.user) : key.user != null) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (user != null ? user.hashCode() : 0);
return result;
}
}
}

View file

@ -0,0 +1,107 @@
package org.keycloak.models.users.jpa.entities;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Entity
@IdClass(UserCredentialEntity.Key.class)
public class UserCredentialEntity {
@Id
protected String type;
protected String value;
protected String device;
protected byte[] salt;
protected int hashIterations;
@Id
@ManyToOne
protected UserEntity user;
public UserCredentialEntity() {
}
public UserCredentialEntity(UserEntity user, String type) {
this.user = user;
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public byte[] getSalt() {
return salt;
}
public void setSalt(byte[] salt) {
this.salt = salt;
}
public int getHashIterations() {
return hashIterations;
}
public void setHashIterations(int hashIterations) {
this.hashIterations = hashIterations;
}
public static class Key implements Serializable {
private String type;
private UserEntity user;
public Key() {
}
public Key(String type, UserEntity user) {
this.type = type;
this.user = user;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (type != null ? !type.equals(key.type) : key.type != null) return false;
if (user != null ? !user.equals(key.user) : key.user != null) return false;
return true;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (user != null ? user.hashCode() : 0);
return result;
}
}
}

View file

@ -0,0 +1,179 @@
package org.keycloak.models.users.jpa.entities;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realm = :realm"),
@NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realm = :realm"),
@NamedQuery(name="getRealmUserByAttribute", query="select u from UserEntity u join u.attributes a where u.realm = :realm and a.name = :name and a.value = :value")
})
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = { "realm", "username" }),
@UniqueConstraint(columnNames = { "realm", "emailConstraint" })
})
@IdClass(UserEntity.Key.class)
public class UserEntity {
@Id
protected String id;
protected boolean enabled;
protected String username;
protected String firstName;
protected String lastName;
protected String email;
// Hack just to workaround the fact that on MS-SQL you can't have unique constraint with multiple NULL values TODO: Find better solution (like unique index with 'where' but that's proprietary)
protected String emailConstraint;
@Id
protected String realm;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="user")
protected List<UserAttributeEntity> attributes = new LinkedList<UserAttributeEntity>();
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="user")
protected List<UserCredentialEntity> credentials = new LinkedList<UserCredentialEntity>();
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="user")
protected List<UserRoleMappingEntity> roles = new LinkedList<UserRoleMappingEntity>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
this.emailConstraint = email != null ? email : id;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getEmailConstraint() {
return emailConstraint;
}
public void setEmailConstraint(String emailConstraint) {
this.emailConstraint = emailConstraint;
}
public List<UserAttributeEntity> getAttributes() {
return attributes;
}
public void setAttributes(List<UserAttributeEntity> attributes) {
this.attributes = attributes;
}
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public Collection<UserCredentialEntity> getCredentials() {
return credentials;
}
public void setCredentials(List<UserCredentialEntity> credentials) {
this.credentials = credentials;
}
public List<UserRoleMappingEntity> getRoles() {
return roles;
}
public void setRoles(List<UserRoleMappingEntity> roles) {
this.roles = roles;
}
public static class Key implements Serializable {
private String id;
private String realm;
public Key() {
}
public Key(String id, String realm) {
this.id = id;
this.realm = realm;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (id != null ? !id.equals(key.id) : key.id != null) return false;
if (realm != null ? !realm.equals(key.realm) : key.realm != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (realm != null ? realm.hashCode() : 0);
return result;
}
}
}

View file

@ -0,0 +1,68 @@
package org.keycloak.models.users.jpa.entities;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import java.io.Serializable;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Entity
@IdClass(UserRoleMappingEntity.Key.class)
public class UserRoleMappingEntity {
@Id
protected String role;
@Id
@ManyToOne
protected UserEntity user;
public UserRoleMappingEntity() {
}
public UserRoleMappingEntity(UserEntity user, String role) {
this.user = user;
this.role = role;
}
public String getRole() {
return role;
}
public static class Key implements Serializable {
private String role;
private UserEntity user;
public Key() {
}
public Key(String role, UserEntity user) {
this.role = role;
this.user = user;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (role != null ? !role.equals(key.role) : key.role != null) return false;
if (user != null ? !user.equals(key.user) : key.user != null) return false;
return true;
}
@Override
public int hashCode() {
int result = role != null ? role.hashCode() : 0;
result = 31 * result + (user != null ? user.hashCode() : 0);
return result;
}
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.users.jpa.JpaUserProviderFactory

View file

@ -0,0 +1,15 @@
package org.keycloak.models.users.jpa;
import org.keycloak.model.test.AbstractUserProviderTest;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JpaUserProviderTest extends AbstractUserProviderTest {
@Override
protected String getProviderId() {
return JpaUserProviderFactory.ID;
}
}

View file

@ -0,0 +1,25 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>

View file

@ -49,7 +49,27 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-realms-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-users-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-sessions-mem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-sessions-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View file

@ -11,13 +11,25 @@
},
"model": {
"provider": "jpa"
"provider": "hybrid"
},
"modelCache": {
"provider": "${keycloak.model.cache.provider:}"
},
"modelRealms": {
"provider": "jpa"
},
"modelUsers": {
"provider": "jpa"
},
"modelSessions": {
"provider": "mem"
},
"timer": {
"provider": "basic"
},

View file

@ -4,21 +4,23 @@
version="1.0">
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
<class>org.keycloak.models.jpa.entities.UserEntity</class>
<class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
<class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
<class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<class>org.keycloak.models.realms.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.realms.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.realms.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity</class>
<class>org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity</class>
<class>org.keycloak.models.realms.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.realms.jpa.entities.ScopeMappingEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
@ -37,5 +39,4 @@
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>

View file

@ -48,6 +48,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.Auth;
@ -220,7 +221,15 @@ public class AccountService {
} else if (types.contains(MediaType.APPLICATION_JSON_TYPE)) {
requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
return Cors.add(request, Response.ok(ModelToRepresentation.toRepresentation(auth.getUser()))).auth().allowedOrigins(auth.getToken()).build();
UserRepresentation rep = ModelToRepresentation.toRepresentation(auth.getUser());
Iterator<String> itr = rep.getAttributes().keySet().iterator();
while (itr.hasNext()) {
if (itr.next().startsWith("keycloak.")) {
itr.remove();
}
}
return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build();
} else {
return Response.notAcceptable(Variant.VariantListBuilder.newInstance().mediaTypes(MediaType.TEXT_HTML_TYPE, MediaType.APPLICATION_JSON_TYPE).build()).build();
}

View file

@ -122,7 +122,27 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<artifactId>keycloak-model-hybrid</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-realms-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-users-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-sessions-mem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-sessions-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View file

@ -14,7 +14,7 @@
},
"model": {
"provider": "${keycloak.model.provider:jpa}",
"provider": "${keycloak.model.provider:hybrid}",
"mongo": {
"host": "${keycloak.model.mongo.host:127.0.0.1}",
"port": "${keycloak.model.mongo.port:27017}",
@ -27,6 +27,18 @@
"provider": "${keycloak.model.cache.provider:simple}"
},
"modelRealms": {
"provider": "jpa"
},
"modelUsers": {
"provider": "jpa"
},
"modelSessions": {
"provider": "mem"
},
"timer": {
"provider": "basic"
},

View file

@ -5,21 +5,22 @@
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
<class>org.keycloak.models.jpa.entities.UserEntity</class>
<class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
<class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
<class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<class>org.keycloak.models.realms.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.realms.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.realms.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity</class>
<class>org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity</class>
<class>org.keycloak.models.realms.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.realms.jpa.entities.ScopeMappingEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.users.jpa.entities.UserEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
@ -51,38 +52,4 @@
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
<!--
<persistence-unit name="picketlink-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
<class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
<class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
-->
</persistence>

Some files were not shown because too many files have changed in this diff Show more