Merge pull request #3 from patriot1burke/master

more stuff
This commit is contained in:
Bill Burke 2013-07-15 06:39:37 -07:00
commit bdfb480279
43 changed files with 5062 additions and 2124 deletions

View file

@ -36,7 +36,7 @@ public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
@Override @Override
public ObjectMapper getContext(Class<?> type) public ObjectMapper getContext(Class<?> type)
{ {
if (type.getPackage().getName().startsWith("org.jboss.resteasy.skeleton.key")) return mapper; if (type.getPackage().getName().startsWith(getClass().getPackage().getName())) return mapper;
return null; return null;
} }
} }

View file

@ -0,0 +1,85 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-as7-adapter</artifactId>
<name>Keycloak AS7 Integration</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.1.2.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
<version>1.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.web</groupId>
<artifactId>jbossweb</artifactId>
<version>7.0.17.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-web</artifactId>
<version>7.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.picketbox</groupId>
<artifactId>picketbox</artifactId>
<scope>provided</scope>
<version>4.0.7.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,14 @@
package org.keycloak.adapters.as7;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Actions
{
public static final String J_OAUTH_ADMIN_FORCED_LOGOUT = "j_oauth_admin_forced_logout";
public static final String J_OAUTH_LOGOUT = "j_oauth_logout";
public static final String J_OAUTH_RESOLVE_ACCESS_CODE = "j_oauth_resolve_access_code";
public static final String J_OAUTH_REMOTE_LOGOUT = "j_oauth_remote_logout";
public static final String J_OAUTH_TOKEN_GRANT = "j_oauth_token_grant";
}

View file

@ -0,0 +1,87 @@
package org.keycloak.adapters.as7;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.authenticator.AuthenticatorBase;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig;
import org.jboss.logging.Logger;
import org.keycloak.ResourceMetadata;
import org.keycloak.adapters.as7.config.ManagedResourceConfig;
import org.keycloak.adapters.as7.config.ManagedResourceConfigLoader;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import javax.security.auth.login.LoginException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Uses a configured remote auth server to do Bearer token authentication only. SkeletonKeyTokens are used
* to provide user data and role mappings.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements LifecycleListener
{
private static final Logger log = Logger.getLogger(BearerTokenAuthenticatorValve.class);
protected ManagedResourceConfig remoteSkeletonKeyConfig;
protected ResourceMetadata resourceMetadata;
@Override
public void start() throws LifecycleException
{
super.start();
StandardContext standardContext = (StandardContext)context;
standardContext.addLifecycleListener(this);
}
@Override
public void lifecycleEvent(LifecycleEvent event)
{
if (event.getType() == Lifecycle.AFTER_START_EVENT) init();
}
protected void init()
{
ManagedResourceConfigLoader managedResourceConfigLoader = new ManagedResourceConfigLoader(context);
resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException
{
try
{
super.invoke(request, response);
}
finally
{
ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession
}
}
@Override
protected boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException
{
try
{
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, !remoteSkeletonKeyConfig.isCancelPropagation(), true);
if (bearer.login(request, response))
{
return true;
}
return false;
}
catch (LoginException e)
{
}
return false;
}
}

View file

@ -0,0 +1,163 @@
package org.keycloak.adapters.as7;
import org.apache.catalina.connector.Request;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;
import org.keycloak.VerificationException;
import org.keycloak.representations.SkeletonKeyToken;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CatalinaBearerTokenAuthenticator
{
protected ResourceMetadata resourceMetadata;
protected boolean challenge;
protected Logger log = Logger.getLogger(CatalinaBearerTokenAuthenticator.class);
protected String tokenString;
protected SkeletonKeyToken token;
private Principal principal;
protected boolean propagateToken;
public CatalinaBearerTokenAuthenticator(ResourceMetadata resourceMetadata, boolean propagateToken, boolean challenge)
{
this.resourceMetadata = resourceMetadata;
this.challenge = challenge;
this.propagateToken = propagateToken;
}
public ResourceMetadata getResourceMetadata()
{
return resourceMetadata;
}
public String getTokenString()
{
return tokenString;
}
public SkeletonKeyToken getToken()
{
return token;
}
public Principal getPrincipal()
{
return principal;
}
public boolean login(Request request, HttpServletResponse response) throws LoginException, IOException
{
String authHeader = request.getHeader("Authorization");
if (authHeader == null)
{
if (challenge)
{
challengeResponse(response, null, null);
return false;
}
else
{
return false;
}
}
String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) challengeResponse(response, null, null);
if (!split[0].equalsIgnoreCase("Bearer")) challengeResponse(response, null, null);
tokenString = split[1];
try
{
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
}
catch (VerificationException e)
{
log.error("Failed to verify token", e);
challengeResponse(response, "invalid_token", e.getMessage());
}
boolean verifyCaller = false;
Set<String> roles = null;
if (resourceMetadata.getResourceName() != null)
{
SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
if (access != null) roles = access.getRoles();
verifyCaller = token.isVerifyCaller(resourceMetadata.getResourceName());
}
else
{
verifyCaller = token.isVerifyCaller();
SkeletonKeyToken.Access access = token.getRealmAccess();
if (access != null) roles = access.getRoles();
}
String surrogate = null;
if (verifyCaller)
{
if (token.getTrustedCertificates() == null || token.getTrustedCertificates().size() == 0)
{
response.sendError(400);
throw new LoginException("No trusted certificates in token");
}
// for now, we just make sure JBoss Web did two-way SSL
// assume JBoss Web verifies the client cert
X509Certificate[] chain = request.getCertificateChain();
if (chain == null || chain.length == 0)
{
response.sendError(400);
throw new LoginException("No certificates provided by jboss web to verify the caller");
}
surrogate = chain[0].getSubjectX500Principal().getName();
}
SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
request.setUserPrincipal(principal);
request.setAuthType("OAUTH_BEARER");
if (propagateToken)
{
SkeletonKeySession skSession = new SkeletonKeySession(tokenString, resourceMetadata);
request.setAttribute(SkeletonKeySession.class.getName(), skSession);
ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
}
return true;
}
protected void challengeResponse(HttpServletResponse response, String error, String description) throws LoginException
{
StringBuilder header = new StringBuilder("Bearer realm=\"");
header.append(resourceMetadata.getRealm()).append("\"");
if (error != null)
{
header.append(", error=\"").append(error).append("\"");
}
if (description != null)
{
header.append(", error_description=\"").append(description).append("\"");
}
response.setHeader("WWW-Authenticate", header.toString());
try
{
response.sendError(401);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
throw new LoginException("Challenged");
}
}

View file

@ -0,0 +1,138 @@
package org.keycloak.adapters.as7;
import org.apache.catalina.Realm;
import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.as.web.security.JBossGenericPrincipal;
import org.jboss.security.NestableGroup;
import org.jboss.security.SecurityConstants;
import org.jboss.security.SecurityContext;
import org.jboss.security.SecurityContextAssociation;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import javax.security.auth.Subject;
import java.security.Principal;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CatalinaSecurityContextHelper
{
public GenericPrincipal createPrincipal(Realm realm, Principal identity, Collection<String> roleSet)
{
Subject subject = new Subject();
String credentials = "";
Set<Principal> principals = subject.getPrincipals();
principals.add(identity);
Group[] roleSets = getRoleSets(roleSet);
for(int g = 0; g < roleSets.length; g ++)
{
Group group = roleSets[g];
String name = group.getName();
Group subjectGroup = createGroup(name, principals);
if( subjectGroup instanceof NestableGroup)
{
/* A NestableGroup only allows Groups to be added to it so we
need to add a SimpleGroup to subjectRoles to contain the roles
*/
SimpleGroup tmp = new SimpleGroup("Roles");
subjectGroup.addMember(tmp);
subjectGroup = tmp;
}
// Copy the group members to the Subject group
Enumeration<? extends Principal> members = group.members();
while( members.hasMoreElements() )
{
Principal role = (Principal) members.nextElement();
subjectGroup.addMember(role);
}
}
// add the CallerPrincipal group if none has been added in getRoleSets
Group callerGroup = new SimpleGroup(SecurityConstants.CALLER_PRINCIPAL_GROUP);
callerGroup.addMember(identity);
principals.add(callerGroup);
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
Principal userPrincipal = getPrincipal(subject);
sc.getUtil().createSubjectInfo(userPrincipal, credentials, subject);
List<String> rolesAsStringList = new ArrayList<String>();
rolesAsStringList.addAll(roleSet);
return new JBossGenericPrincipal(realm, userPrincipal.getName(), null, rolesAsStringList,
userPrincipal, null, credentials, null, subject);
}
/**
* Get the Principal given the authenticated Subject. Currently the first principal that is not of type {@code Group} is
* considered or the single principal inside the CallerPrincipal group.
*
* @param subject
* @return the authenticated principal
*/
protected Principal getPrincipal(Subject subject) {
Principal principal = null;
Principal callerPrincipal = null;
if (subject != null) {
Set<Principal> principals = subject.getPrincipals();
if (principals != null && !principals.isEmpty()) {
for (Principal p : principals) {
if (!(p instanceof Group) && principal == null) {
principal = p;
}
if (p instanceof Group) {
Group g = Group.class.cast(p);
if (g.getName().equals(SecurityConstants.CALLER_PRINCIPAL_GROUP) && callerPrincipal == null) {
Enumeration<? extends Principal> e = g.members();
if (e.hasMoreElements())
callerPrincipal = e.nextElement();
}
}
}
}
}
return callerPrincipal == null ? principal : callerPrincipal;
}
protected Group createGroup(String name, Set<Principal> principals)
{
Group roles = null;
Iterator<Principal> iter = principals.iterator();
while( iter.hasNext() )
{
Object next = iter.next();
if( (next instanceof Group) == false )
continue;
Group grp = (Group) next;
if( grp.getName().equals(name) )
{
roles = grp;
break;
}
}
// If we did not find a group create one
if( roles == null )
{
roles = new SimpleGroup(name);
principals.add(roles);
}
return roles;
}
protected Group[] getRoleSets(Collection<String> roleSet)
{
SimpleGroup roles = new SimpleGroup("Roles");
Group[] roleSets = {roles};
for (String role : roleSet)
{
roles.addMember(new SimplePrincipal(role));
}
return roleSets;
}
}

View file

@ -0,0 +1,303 @@
package org.keycloak.adapters.as7;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.logging.Logger;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.keycloak.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.as7.config.ManagedResourceConfig;
import org.keycloak.adapters.as7.config.ManagedResourceConfigLoader;
import org.keycloak.representations.SkeletonKeyToken;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import javax.security.auth.login.LoginException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
/**
* Web deployment whose security is managed by a remote OAuth Skeleton Key authentication server
* <p/>
* Redirects browser to remote authentication server if not logged in. Also allows OAuth Bearer Token requests
* that contain a Skeleton Key bearer tokens.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthManagedResourceValve extends FormAuthenticator implements LifecycleListener
{
protected RealmConfiguration realmConfiguration;
private static final Logger log = Logger.getLogger(OAuthManagedResourceValve.class);
protected UserSessionManagement userSessionManagement = new UserSessionManagement();
protected ManagedResourceConfig remoteSkeletonKeyConfig;
protected ResourceMetadata resourceMetadata;
@Override
public void start() throws LifecycleException
{
super.start();
StandardContext standardContext = (StandardContext) context;
standardContext.addLifecycleListener(this);
}
@Override
public void lifecycleEvent(LifecycleEvent event)
{
if (event.getType() == Lifecycle.AFTER_START_EVENT) init();
}
protected void init()
{
ManagedResourceConfigLoader managedResourceConfigLoader = new ManagedResourceConfigLoader(context);
resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
String client_id = remoteSkeletonKeyConfig.getClientId();
if (client_id == null)
{
throw new IllegalArgumentException("Must set client-id to use with auth server");
}
realmConfiguration = new RealmConfiguration();
String authUrl = remoteSkeletonKeyConfig.getAuthUrl();
if (authUrl == null)
{
throw new RuntimeException("You must specify auth-url");
}
String tokenUrl = remoteSkeletonKeyConfig.getCodeUrl();
if (tokenUrl == null)
{
throw new RuntimeException("You mut specify code-url");
}
realmConfiguration.setMetadata(resourceMetadata);
realmConfiguration.setClientId(client_id);
for (Map.Entry<String, String> entry : managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getClientCredentials().entrySet())
{
realmConfiguration.getCredentials().param(entry.getKey(), entry.getValue());
}
int size = 10;
if (managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getConnectionPoolSize() > 0)
size = managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getConnectionPoolSize();
ResteasyClientBuilder.HostnameVerificationPolicy policy = ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD;
if (managedResourceConfigLoader.getRemoteSkeletonKeyConfig().isAllowAnyHostname())
policy = ResteasyClientBuilder.HostnameVerificationPolicy.ANY;
ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(OAuthManagedResourceValve.class.getClassLoader());
try
{
ResteasyProviderFactory.getInstance(); // initialize builtins
RegisterBuiltin.register(providerFactory);
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
ResteasyClient client = new ResteasyClientBuilder()
.providerFactory(providerFactory)
.connectionPoolSize(size)
.hostnameVerification(policy)
.trustStore(resourceMetadata.getTruststore())
.keyStore(resourceMetadata.getClientKeystore(), resourceMetadata.getClientKeyPassword())
.build();
realmConfiguration.setClient(client);
realmConfiguration.setAuthUrl(UriBuilder.fromUri(authUrl).queryParam("client_id", client_id));
realmConfiguration.setCodeUrl(client.target(tokenUrl));
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException
{
try
{
String requestURI = request.getDecodedRequestURI();
if (requestURI.endsWith("j_oauth_remote_logout"))
{
remoteLogout(request, response);
return;
}
super.invoke(request, response);
}
finally
{
ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession
}
}
@Override
public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException
{
try
{
if (bearer(false, request, response)) return true;
else if (checkLoggedIn(request, response))
{
if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null)
{
if (restoreRequest(request, request.getSessionInternal()))
{
log.debug("restoreRequest");
return (true);
}
else
{
log.debug("Restore of original request failed");
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return (false);
}
}
else
{
return true;
}
}
// initiate or continue oauth2 protocol
oauth(request, response);
}
catch (LoginException e)
{
}
return false;
}
protected void remoteLogout(Request request, HttpServletResponse response) throws IOException
{
try
{
log.debug("->> remoteLogout: ");
if (!bearer(true, request, response))
{
log.debug("remoteLogout: bearer auth failed");
return;
}
GenericPrincipal gp = (GenericPrincipal) request.getPrincipal();
if (!gp.hasRole(remoteSkeletonKeyConfig.getAdminRole()))
{
log.debug("remoteLogout: role failure");
response.sendError(403);
return;
}
String user = request.getParameter("user");
if (user != null)
{
userSessionManagement.logout(user);
}
else
{
userSessionManagement.logoutAll();
}
}
catch (Exception e)
{
log.error("failed to logout", e);
}
response.setStatus(204);
}
protected boolean bearer(boolean challenge, Request request, HttpServletResponse response) throws LoginException, IOException
{
CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(realmConfiguration.getMetadata(), !remoteSkeletonKeyConfig.isCancelPropagation(), challenge);
if (bearer.login(request, response))
{
return true;
}
return false;
}
protected boolean checkLoggedIn(Request request, HttpServletResponse response)
{
if (request.getSessionInternal() == null || request.getSessionInternal().getPrincipal() == null)
return false;
log.debug("remote logged in already");
GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
request.setUserPrincipal(principal);
request.setAuthType("OAUTH");
Session session = request.getSessionInternal();
if (session != null && !remoteSkeletonKeyConfig.isCancelPropagation())
{
SkeletonKeySession skSession = (SkeletonKeySession) session.getNote(SkeletonKeySession.class.getName());
if (skSession != null)
{
request.setAttribute(SkeletonKeySession.class.getName(), skSession);
ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
}
}
return true;
}
/**
* This method always set the HTTP response, so do not continue after invoking
*/
protected void oauth(Request request, HttpServletResponse response) throws IOException
{
ServletOAuthLogin oauth = new ServletOAuthLogin(realmConfiguration, request, response, request.getConnector().getRedirectPort());
String code = oauth.getCode();
if (code == null)
{
String error = oauth.getError();
if (error != null)
{
response.sendError(400, "OAuth " + error);
return;
}
else
{
saveRequest(request, request.getSessionInternal(true));
oauth.loginRedirect();
}
return;
}
else
{
if (!oauth.resolveCode(code)) return;
SkeletonKeyToken token = oauth.getToken();
Set<String> roles = null;
if (resourceMetadata.getResourceName() != null)
{
SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
if (access != null) roles = access.getRoles();
}
else
{
SkeletonKeyToken.Access access = token.getRealmAccess();
if (access != null) roles = access.getRoles();
}
SkeletonKeyPrincipal skp = new SkeletonKeyPrincipal(token.getPrincipal(), null);
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(context.getRealm(), skp, roles);
Session session = request.getSessionInternal(true);
session.setPrincipal(principal);
session.setAuthType("OAUTH");
if (!remoteSkeletonKeyConfig.isCancelPropagation())
{
SkeletonKeySession skSession = new SkeletonKeySession(oauth.getTokenString(), realmConfiguration.getMetadata());
session.setNote(SkeletonKeySession.class.getName(), skSession);
}
String username = token.getPrincipal();
log.debug("userSessionManage.login: " + username);
userSessionManagement.login(session, username);
}
}
}

View file

@ -0,0 +1,320 @@
package org.keycloak.adapters.as7;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.RealmConfiguration;
import org.keycloak.VerificationException;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyToken;
import org.jboss.resteasy.util.BasicAuthHelper;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ServletOAuthLogin
{
private static final Logger log = Logger.getLogger(ServletOAuthLogin.class);
protected HttpServletRequest request;
protected HttpServletResponse response;
protected boolean codePresent;
protected RealmConfiguration realmInfo;
protected int redirectPort;
protected String tokenString;
protected SkeletonKeyToken token;
public ServletOAuthLogin(RealmConfiguration realmInfo, HttpServletRequest request, HttpServletResponse response, int redirectPort)
{
this.request = request;
this.response = response;
this.realmInfo = realmInfo;
this.redirectPort = redirectPort;
}
public String getTokenString()
{
return tokenString;
}
public SkeletonKeyToken getToken()
{
return token;
}
public RealmConfiguration getRealmInfo()
{
return realmInfo;
}
protected String getDefaultCookiePath()
{
String path = request.getContextPath();
if ("".equals(path) || path == null) path = "/";
return path;
}
protected String getRequestUrl()
{
return request.getRequestURL().toString();
}
protected boolean isRequestSecure()
{
return request.isSecure();
}
protected void sendError(int code)
{
try
{
response.sendError(code);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
protected void sendRedirect(String url)
{
try
{
response.sendRedirect(url);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
protected Cookie getCookie(String cookieName)
{
if (request.getCookies() == null) return null;
for (Cookie cookie : request.getCookies())
{
if (cookie.getName().equals(cookieName))
{
return cookie;
}
}
return null;
}
protected String getCookieValue(String cookieName)
{
Cookie cookie = getCookie(cookieName);
if (cookie == null) return null;
return cookie.getValue();
}
protected String getQueryParamValue(String paramName)
{
String query = request.getQueryString();
if (query == null) return null;
String[] params = query.split("&");
for (String param : params)
{
int eq = param.indexOf('=');
if (eq == -1) continue;
String name = param.substring(0, eq);
if (!name.equals(paramName)) continue;
return param.substring(eq + 1);
}
return null;
}
public String getError()
{
return getQueryParamValue("error");
}
public String getCode()
{
return getQueryParamValue("code");
}
protected void setCookie(String name, String value, String domain, String path, boolean secure)
{
Cookie cookie = new Cookie(name, value);
if (domain != null) cookie.setDomain(domain);
if (path != null) cookie.setPath(path);
if (secure) cookie.setSecure(true);
response.addCookie(cookie);
}
protected String getRedirectUri(String state)
{
String url = getRequestUrl();
if (!isRequestSecure() && realmInfo.isSslRequired())
{
int port = redirectPort;
if (port < 0)
{
// disabled?
return null;
}
UriBuilder secureUrl = UriBuilder.fromUri(url).scheme("https").port(-1);
if (port != 443) secureUrl.port(port);
url = secureUrl.build().toString();
}
return realmInfo.getAuthUrl().clone()
.queryParam("client_id", realmInfo.getClientId())
.queryParam("redirect_uri", url)
.queryParam("state", state)
.queryParam("login", "true")
.build().toString();
}
protected static final AtomicLong counter = new AtomicLong();
protected String getStateCode()
{
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
}
public void loginRedirect()
{
String state = getStateCode();
String redirect = getRedirectUri(state);
if (redirect == null)
{
sendError(Response.Status.FORBIDDEN.getStatusCode());
return;
}
setCookie(realmInfo.getStateCookieName(), state, null, getDefaultCookiePath(), realmInfo.isSslRequired());
sendRedirect(redirect);
}
public boolean checkStateCookie()
{
Cookie stateCookie = getCookie(realmInfo.getStateCookieName());
if (stateCookie == null)
{
sendError(400);
log.warn("No state cookie");
return false;
}
// reset the cookie
Cookie reset = new Cookie(stateCookie.getName(), stateCookie.getValue());
reset.setPath(stateCookie.getPath());
reset.setMaxAge(0);
response.addCookie(reset);
String stateCookieValue = getCookieValue(realmInfo.getStateCookieName());
// its ok to call request.getParameter() because this should be a redirect
String state = request.getParameter("state");
if (state == null)
{
sendError(400);
log.warn("state parameter was null");
return false;
}
if (!state.equals(stateCookieValue))
{
sendError(400);
log.warn("state parameter invalid");
log.warn("cookie: " + stateCookieValue);
log.warn("queryParam: " + state);
return false;
}
return true;
}
/**
* Start or continue the oauth login process.
*
* if code query parameter is not present, then browser is redirected to authUrl. The redirect URL will be
* the URL of the current request.
*
* If code query parameter is present, then an access token is obtained by invoking a secure request to the codeUrl.
* If the access token is obtained, the browser is again redirected to the current request URL, but any OAuth
* protocol specific query parameters are removed.
*
* @return true if an access token was obtained
*/
public boolean resolveCode(String code)
{
// abort if not HTTPS
if (realmInfo.isSslRequired() && !isRequestSecure())
{
log.error("SSL is required");
sendError(Response.Status.FORBIDDEN.getStatusCode());
return false;
}
if (!checkStateCookie()) return false;
String client_id = realmInfo.getClientId();
String password = realmInfo.getCredentials().asMap().getFirst("password");
String authHeader = BasicAuthHelper.createHeader(client_id, password);
String redirectUri = stripOauthParametersFromRedirect();
Form form = new Form();
form.param("grant_type", "authorization_code")
.param("code", code)
.param("redirect_uri", redirectUri);
Response res = realmInfo.getCodeUrl().request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(form));
AccessTokenResponse tokenResponse;
try
{
if (res.getStatus() != 200)
{
log.error("failed to turn code into token");
sendError(Response.Status.FORBIDDEN.getStatusCode());
return false;
}
log.debug("media type: " + res.getMediaType());
log.debug("Content-Type header: " + res.getHeaderString("Content-Type"));
tokenResponse = res.readEntity(AccessTokenResponse.class);
}
finally
{
res.close();
}
tokenString = tokenResponse.getToken();
try
{
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata());
log.debug("Verification succeeded!");
}
catch (VerificationException e)
{
log.error("failed verification of token");
sendError(Response.Status.FORBIDDEN.getStatusCode());
return false;
}
// redirect to URL without oauth query parameters
sendRedirect(redirectUri);
return true;
}
/**
* strip out unwanted query parameters and redirect so bookmarks don't retain oauth protocol bits
*/
protected String stripOauthParametersFromRedirect()
{
StringBuffer buf = request.getRequestURL().append("?").append(request.getQueryString());
UriBuilder builder = UriBuilder.fromUri(buf.toString())
.replaceQueryParam("code", null)
.replaceQueryParam("state", null);
return builder.build().toString();
}
}

View file

@ -0,0 +1,111 @@
package org.keycloak.adapters.as7;
import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.logging.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manages relationship to users and sessions so that forced admin logout can be implemented
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserSessionManagement implements SessionListener
{
private static final Logger log = Logger.getLogger(UserSessionManagement.class);
protected ConcurrentHashMap<String, Map<String, Session>> userSessionMap = new ConcurrentHashMap<String, Map<String, Session>>();
protected void login(Session session, String username)
{
Map<String, Session> map = userSessionMap.get(username);
if (map == null)
{
final Map<String, Session> value = new HashMap<String, Session>();
map = userSessionMap.putIfAbsent(username, value);
if (map == null)
{
map = value;
}
}
synchronized (map)
{
map.put(session.getId(), session);
}
session.addSessionListener(this);
}
public void logoutAll()
{
List<String> users = new ArrayList<String>();
users.addAll(userSessionMap.keySet());
for (String user : users) logout(user);
}
public void logoutAllBut(String but)
{
List<String> users = new ArrayList<String>();
users.addAll(userSessionMap.keySet());
for (String user : users)
{
if (!but.equals(user)) logout(user);
}
}
public void logout(String user)
{
log.debug("logoutUser: " + user);
Map<String, Session> map = userSessionMap.remove(user);
if (map == null)
{
log.debug("no session for user: " + user);
return;
}
log.debug("found session for user");
synchronized (map)
{
for (Session session : map.values())
{
log.debug("invalidating session for user: " + user);
session.setPrincipal(null);
session.setAuthType(null);
session.getSession().invalidate();
}
}
}
public void sessionEvent(SessionEvent event)
{
// We only care about session destroyed events
if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType())
&& (!Session.SESSION_PASSIVATED_EVENT.equals(event.getType())))
return;
// Look up the single session id associated with this session (if any)
Session session = event.getSession();
GenericPrincipal principal = (GenericPrincipal) session.getPrincipal();
if (principal == null) return;
session.setPrincipal(null);
session.setAuthType(null);
String username = principal.getUserPrincipal().getName();
Map<String, Session> map = userSessionMap.get(username);
if (map == null) return;
synchronized (map)
{
map.remove(session.getId());
if (map.isEmpty()) userSessionMap.remove(username);
}
}
}

View file

@ -0,0 +1,279 @@
package org.keycloak.adapters.as7.config;
import org.codehaus.jackson.annotate.JsonProperty;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthServerConfig
{
@JsonProperty("realm")
protected String realm;
@JsonProperty("realm-private-key")
protected String realmPrivateKey;
@JsonProperty("realm-public-key")
protected String realmPublicKey;
@JsonProperty("realm-keystore")
protected String realmKeyStore;
@JsonProperty("realm-keystore-password")
protected String realmKeystorePassword;
@JsonProperty("realm-key-alias")
protected String realmKeyAlias;
@JsonProperty("realm-private-key-password")
protected String realmPrivateKeyPassword;
@JsonProperty("access-code-lifetime")
protected int accessCodeLifetime;
@JsonProperty("token-lifetime")
protected int tokenLifetime;
@JsonProperty("admin-role")
protected String adminRole;
@JsonProperty("login-role")
protected String loginRole;
@JsonProperty("oauth-client-role")
protected String clientRole;
@JsonProperty("wildcard-role")
protected String wildcardRole;
@JsonProperty("cancel-propagation")
protected boolean cancelPropagation;
@JsonProperty("sso-disabled")
protected boolean ssoDisabled;
// these properties are optional and used to provide connection metadata when the server wants to make
// remote SSL connections
protected String truststore;
@JsonProperty("truststore-password")
protected String truststorePassword;
@JsonProperty("client-keystore")
protected String clientKeystore;
@JsonProperty("client-keystore-password")
protected String clientKeystorePassword;
@JsonProperty("client-key-password")
protected String clientKeyPassword;
protected List<String> resources = new ArrayList<String>();
public String getRealm()
{
return realm;
}
public void setRealm(String realm)
{
this.realm = realm;
}
public String getRealmPrivateKey()
{
return realmPrivateKey;
}
public void setRealmPrivateKey(String realmPrivateKey)
{
this.realmPrivateKey = realmPrivateKey;
}
public String getRealmPublicKey()
{
return realmPublicKey;
}
public void setRealmPublicKey(String realmPublicKey)
{
this.realmPublicKey = realmPublicKey;
}
public int getAccessCodeLifetime()
{
return accessCodeLifetime;
}
public void setAccessCodeLifetime(int accessCodeLifetime)
{
this.accessCodeLifetime = accessCodeLifetime;
}
public String getTruststore()
{
return truststore;
}
public void setTruststore(String truststore)
{
this.truststore = truststore;
}
public String getTruststorePassword()
{
return truststorePassword;
}
public void setTruststorePassword(String truststorePassword)
{
this.truststorePassword = truststorePassword;
}
public String getClientKeystore()
{
return clientKeystore;
}
public void setClientKeystore(String clientKeystore)
{
this.clientKeystore = clientKeystore;
}
public String getClientKeystorePassword()
{
return clientKeystorePassword;
}
public void setClientKeystorePassword(String clientKeystorePassword)
{
this.clientKeystorePassword = clientKeystorePassword;
}
public String getClientKeyPassword()
{
return clientKeyPassword;
}
public void setClientKeyPassword(String clientKeyPassword)
{
this.clientKeyPassword = clientKeyPassword;
}
public boolean isCancelPropagation()
{
return cancelPropagation;
}
public void setCancelPropagation(boolean cancelPropagation)
{
this.cancelPropagation = cancelPropagation;
}
public boolean isSsoDisabled()
{
return ssoDisabled;
}
public void setSsoDisabled(boolean ssoDisabled)
{
this.ssoDisabled = ssoDisabled;
}
public List<String> getResources()
{
return resources;
}
public String getAdminRole()
{
return adminRole;
}
public void setAdminRole(String adminRole)
{
this.adminRole = adminRole;
}
public String getLoginRole()
{
return loginRole;
}
public void setLoginRole(String loginRole)
{
this.loginRole = loginRole;
}
public String getClientRole()
{
return clientRole;
}
public void setClientRole(String clientRole)
{
this.clientRole = clientRole;
}
public String getWildcardRole()
{
return wildcardRole;
}
public void setWildcardRole(String wildcardRole)
{
this.wildcardRole = wildcardRole;
}
public String getRealmKeyStore()
{
return realmKeyStore;
}
public void setRealmKeyStore(String realmKeyStore)
{
this.realmKeyStore = realmKeyStore;
}
public String getRealmKeystorePassword()
{
return realmKeystorePassword;
}
public void setRealmKeystorePassword(String realmKeystorePassword)
{
this.realmKeystorePassword = realmKeystorePassword;
}
public String getRealmKeyAlias()
{
return realmKeyAlias;
}
public void setRealmKeyAlias(String realmKeyAlias)
{
this.realmKeyAlias = realmKeyAlias;
}
public String getRealmPrivateKeyPassword()
{
return realmPrivateKeyPassword;
}
public void setRealmPrivateKeyPassword(String realmPrivateKeyPassword)
{
this.realmPrivateKeyPassword = realmPrivateKeyPassword;
}
public int getTokenLifetime()
{
return tokenLifetime;
}
public void setTokenLifetime(int tokenLifetime)
{
this.tokenLifetime = tokenLifetime;
}
}

View file

@ -0,0 +1,213 @@
package org.keycloak.adapters.as7.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "resource", "realm-public-key", "admin-role", "auth-url", "code-url", "truststore", "truststore-password", "client-id", "client-credentials"})
public class ManagedResourceConfig
{
@JsonProperty("realm")
protected String realm;
@JsonProperty("resource")
protected String resource;
@JsonProperty("realm-public-key")
protected String realmKey;
@JsonProperty("admin-role")
protected String adminRole;
@JsonProperty("auth-url")
protected String authUrl;
@JsonProperty("code-url")
protected String codeUrl;
@JsonProperty("allow-any-hostname")
protected boolean allowAnyHostname;
@JsonProperty("truststore")
protected String truststore;
@JsonProperty("truststore-password")
protected String truststorePassword;
@JsonProperty("client-id")
protected String clientId;
@JsonProperty("client-keystore")
protected String clientKeystore;
@JsonProperty("client-keystore-password")
protected String clientKeystorePassword;
@JsonProperty("client-key-password")
protected String clientKeyPassword;
@JsonProperty("client-credentials")
protected Map<String, String> clientCredentials = new HashMap<String, String>();
@JsonProperty("connection-pool-size")
protected int connectionPoolSize;
@JsonProperty("cancel-propagation")
protected boolean cancelPropagation;
public String getRealm()
{
return realm;
}
public void setRealm(String realm)
{
this.realm = realm;
}
public String getResource()
{
return resource;
}
public void setResource(String resource)
{
this.resource = resource;
}
public String getRealmKey()
{
return realmKey;
}
public void setRealmKey(String realmKey)
{
this.realmKey = realmKey;
}
public String getAuthUrl()
{
return authUrl;
}
public void setAuthUrl(String authUrl)
{
this.authUrl = authUrl;
}
public String getCodeUrl()
{
return codeUrl;
}
public void setCodeUrl(String codeUrl)
{
this.codeUrl = codeUrl;
}
public boolean isAllowAnyHostname()
{
return allowAnyHostname;
}
public void setAllowAnyHostname(boolean allowAnyHostname)
{
this.allowAnyHostname = allowAnyHostname;
}
public String getTruststore()
{
return truststore;
}
public void setTruststore(String truststore)
{
this.truststore = truststore;
}
public String getTruststorePassword()
{
return truststorePassword;
}
public void setTruststorePassword(String truststorePassword)
{
this.truststorePassword = truststorePassword;
}
public String getClientId()
{
return clientId;
}
public void setClientId(String clientId)
{
this.clientId = clientId;
}
public Map<String, String> getClientCredentials()
{
return clientCredentials;
}
public String getClientKeystore()
{
return clientKeystore;
}
public void setClientKeystore(String clientKeystore)
{
this.clientKeystore = clientKeystore;
}
public String getClientKeystorePassword()
{
return clientKeystorePassword;
}
public void setClientKeystorePassword(String clientKeystorePassword)
{
this.clientKeystorePassword = clientKeystorePassword;
}
public String getClientKeyPassword()
{
return clientKeyPassword;
}
public void setClientKeyPassword(String clientKeyPassword)
{
this.clientKeyPassword = clientKeyPassword;
}
public int getConnectionPoolSize()
{
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize)
{
this.connectionPoolSize = connectionPoolSize;
}
public boolean isCancelPropagation()
{
return cancelPropagation;
}
public void setCancelPropagation(boolean cancelPropagation)
{
this.cancelPropagation = cancelPropagation;
}
public String getAdminRole()
{
return adminRole;
}
public void setAdminRole(String adminRole)
{
this.adminRole = adminRole;
}
}

View file

@ -0,0 +1,139 @@
package org.keycloak.adapters.as7.config;
import org.apache.catalina.Context;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.jboss.logging.Logger;
import org.keycloak.EnvUtil;
import org.keycloak.PemUtils;
import org.keycloak.ResourceMetadata;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PublicKey;
public class ManagedResourceConfigLoader
{
static final Logger log = Logger.getLogger(ManagedResourceConfigLoader.class);
protected ManagedResourceConfig remoteSkeletonKeyConfig;
protected ResourceMetadata resourceMetadata;
public ManagedResourceConfigLoader(Context context)
{
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
InputStream is = null;
String path = context.getServletContext().getInitParameter("skeleton.key.config.file");
if (path == null)
{
is = context.getServletContext().getResourceAsStream("/WEB-INF/resteasy-oauth.json");
}
else
{
try
{
is = new FileInputStream(path);
}
catch (FileNotFoundException e)
{
throw new RuntimeException(e);
}
}
remoteSkeletonKeyConfig = null;
try
{
remoteSkeletonKeyConfig = mapper.readValue(is, ManagedResourceConfig.class);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
String name = remoteSkeletonKeyConfig.getResource();
String realm = remoteSkeletonKeyConfig.getRealm();
if (realm == null) throw new RuntimeException("Must set 'realm' in config");
String realmKeyPem = remoteSkeletonKeyConfig.getRealmKey();
if (realmKeyPem == null)
{
throw new IllegalArgumentException("You must set the realm-public-key");
}
PublicKey realmKey = null;
try
{
realmKey = PemUtils.decodePublicKey(realmKeyPem);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
resourceMetadata = new ResourceMetadata();
resourceMetadata.setRealm(realm);
resourceMetadata.setResourceName(name);
resourceMetadata.setRealmKey(realmKey);
String truststore = remoteSkeletonKeyConfig.getTruststore();
if (truststore != null)
{
truststore = EnvUtil.replace(truststore);
String truststorePassword = remoteSkeletonKeyConfig.getTruststorePassword();
KeyStore trust = null;
try
{
trust = loadKeyStore(truststore, truststorePassword);
}
catch (Exception e)
{
throw new RuntimeException("Failed to load truststore", e);
}
resourceMetadata.setTruststore(trust);
}
String clientKeystore = remoteSkeletonKeyConfig.getClientKeystore();
String clientKeyPassword = null;
if (clientKeystore != null)
{
clientKeystore = EnvUtil.replace(clientKeystore);
String clientKeystorePassword = remoteSkeletonKeyConfig.getClientKeystorePassword();
KeyStore serverKS = null;
try
{
serverKS = loadKeyStore(clientKeystore, clientKeystorePassword);
}
catch (Exception e)
{
throw new RuntimeException("Failed to load keystore", e);
}
resourceMetadata.setClientKeystore(serverKS);
clientKeyPassword = remoteSkeletonKeyConfig.getClientKeyPassword();
resourceMetadata.setClientKeyPassword(clientKeyPassword);
}
}
public static KeyStore loadKeyStore(String filename, String password) throws Exception
{
KeyStore trustStore = KeyStore.getInstance(KeyStore
.getDefaultType());
File truststoreFile = new File(filename);
FileInputStream trustStream = new FileInputStream(truststoreFile);
trustStore.load(trustStream, password.toCharArray());
trustStream.close();
return trustStore;
}
public ManagedResourceConfig getRemoteSkeletonKeyConfig()
{
return remoteSkeletonKeyConfig;
}
public ResourceMetadata getResourceMetadata()
{
return resourceMetadata;
}
}

21
integration/pom.xml Executable file
View file

@ -0,0 +1,21 @@
<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-alpha-1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Keycloak Integration</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>integration-pom</artifactId>
<packaging>pom</packaging>
<modules>
<module>as7-eap6/adapter</module>
<!-- <module>as7-eap6/jboss-modules</module> -->
</modules>
</project>

12
pom.xml
View file

@ -54,6 +54,7 @@
<modules> <modules>
<module>core</module> <module>core</module>
<module>services</module> <module>services</module>
<module>integration</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
@ -108,6 +109,12 @@
<artifactId>jose-jwt</artifactId> <artifactId>jose-jwt</artifactId>
<version>${resteasy.version}</version> <version>${resteasy.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>tjws</artifactId>
<version>${resteasy.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.codehaus.jackson</groupId> <groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId> <artifactId>jackson-core-asl</artifactId>
@ -163,6 +170,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.11</version> <version>4.11</version>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View file

@ -43,6 +43,25 @@
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId> <artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope> <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>tjws</artifactId>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
@ -94,6 +113,23 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.161</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.6.Final</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

View file

@ -0,0 +1,38 @@
package org.keycloak.services.filters;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.IdentitySessionFactory;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import java.io.IOException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@PreMatching
public class IdentitySessionFilter implements ContainerRequestFilter, ContainerResponseFilter {
protected IdentitySessionFactory factory;
public IdentitySessionFilter(IdentitySessionFactory factory) {
this.factory = factory;
}
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
IdentitySession ctx = factory.createIdentitySession();
requestContext.setProperty(IdentitySession.class.getName(), ctx);
ResteasyProviderFactory.pushContext(IdentitySession.class, ctx);
}
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
IdentitySession ctx = (IdentitySession)requestContext.getProperty(IdentitySession.class.getName());
if (ctx != null) ctx.close();
}
}

View file

@ -0,0 +1,49 @@
package org.keycloak.services.managers;
import org.keycloak.representations.SkeletonKeyToken;
import org.picketlink.idm.model.User;
import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AccessCodeEntry {
protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
protected long expiration;
protected SkeletonKeyToken token;
protected User client;
public boolean isExpired() {
return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration;
}
public String getId() {
return id;
}
public long getExpiration() {
return expiration;
}
public void setExpiration(long expiration) {
this.expiration = expiration;
}
public SkeletonKeyToken getToken() {
return token;
}
public void setToken(SkeletonKeyToken token) {
this.token = token;
}
public User getClient() {
return client;
}
public void setClient(User client) {
this.client = client;
}
}

View file

@ -24,27 +24,28 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AuthenticationManager public class AuthenticationManager {
{
protected Logger logger = Logger.getLogger(AuthenticationManager.class); protected Logger logger = Logger.getLogger(AuthenticationManager.class);
public static final String FORM_USERNAME = "username"; public static final String FORM_USERNAME = "username";
protected RealmManager adapter;
public AuthenticationManager(RealmManager adapter) /**
{ * Grabs token from headers, authenticates, authorizes
this.adapter = adapter; *
* @param realm
* @param headers
* @return
*/
public boolean isRealmAdmin(RealmModel realm, HttpHeaders headers) {
User user = authenticateToken(realm, headers);
return realm.isRealmAdmin(user);
} }
public User authenticateToken(RealmModel realm, HttpHeaders headers) public User authenticateToken(RealmModel realm, HttpHeaders headers) {
{
String tokenString = null; String tokenString = null;
String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION); String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authHeader == null) if (authHeader == null) {
{
throw new NotAuthorizedException("Bearer"); throw new NotAuthorizedException("Bearer");
} } else {
else
{
String[] split = authHeader.trim().split("\\s+"); String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer"); if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer");
if (!split[0].equalsIgnoreCase("Bearer")) throw new NotAuthorizedException("Bearer"); if (!split[0].equalsIgnoreCase("Bearer")) throw new NotAuthorizedException("Bearer");
@ -52,51 +53,40 @@ public class AuthenticationManager
} }
try try {
{
SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getId()); SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getId());
if (!token.isActive()) if (!token.isActive()) {
{
throw new NotAuthorizedException("token_expired"); throw new NotAuthorizedException("token_expired");
} }
User user = realm.getIdm().getUser(token.getPrincipal()); User user = realm.getIdm().getUser(token.getPrincipal());
if (user == null || !user.isEnabled()) if (user == null || !user.isEnabled()) {
{
throw new NotAuthorizedException("invalid_user"); throw new NotAuthorizedException("invalid_user");
} }
return user; return user;
} } catch (VerificationException e) {
catch (VerificationException e)
{
logger.error("Failed to verify token", e); logger.error("Failed to verify token", e);
throw new NotAuthorizedException("invalid_token"); throw new NotAuthorizedException("invalid_token");
} }
} }
public boolean authenticateForm(RealmModel realm, User user, MultivaluedMap<String, String> formData) public boolean authenticateForm(RealmModel realm, User user, MultivaluedMap<String, String> formData) {
{
String username = user.getLoginName(); String username = user.getLoginName();
Set<String> types = new HashSet<String>(); Set<String> types = new HashSet<String>();
for (RequiredCredentialModel credential : realm.getRequiredCredentials()) for (RequiredCredentialModel credential : realm.getRequiredCredentials()) {
{
types.add(credential.getType()); types.add(credential.getType());
} }
if (types.contains(RequiredCredentialRepresentation.PASSWORD)) if (types.contains(RequiredCredentialRepresentation.PASSWORD)) {
{
String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD); String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD);
if (password == null) if (password == null) {
{
logger.warn("Password not provided"); logger.warn("Password not provided");
return false; return false;
} }
if (types.contains(RequiredCredentialRepresentation.TOTP)) if (types.contains(RequiredCredentialRepresentation.TOTP)) {
{
String token = formData.getFirst(RequiredCredentialRepresentation.TOTP); String token = formData.getFirst(RequiredCredentialRepresentation.TOTP);
if (token == null) if (token == null) {
{
logger.warn("TOTP token not provided"); logger.warn("TOTP token not provided");
return false; return false;
} }
@ -105,23 +95,17 @@ public class AuthenticationManager
creds.setUsername(username); creds.setUsername(username);
creds.setPassword(new Password(password)); creds.setPassword(new Password(password));
realm.getIdm().validateCredentials(creds); realm.getIdm().validateCredentials(creds);
if (creds.getStatus() != Credentials.Status.VALID) if (creds.getStatus() != Credentials.Status.VALID) {
{
return false; return false;
} }
} } else {
else
{
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, new Password(password)); UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, new Password(password));
realm.getIdm().validateCredentials(creds); realm.getIdm().validateCredentials(creds);
if (creds.getStatus() != Credentials.Status.VALID) if (creds.getStatus() != Credentials.Status.VALID) {
{
return false; return false;
} }
} }
} } else {
else
{
logger.warn("Do not know how to authenticate user"); logger.warn("Do not know how to authenticate user");
return false; return false;
} }

View file

@ -0,0 +1,33 @@
package org.keycloak.services.managers;
import org.keycloak.services.models.RealmManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.resources.RegistrationService;
import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.SimpleRole;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class InstallationManager {
public void install(RealmManager manager) {
RealmModel defaultRealm = manager.createRealm(Realm.DEFAULT_REALM, Realm.DEFAULT_REALM);
defaultRealm.setName(Realm.DEFAULT_REALM);
defaultRealm.setEnabled(true);
defaultRealm.setTokenLifespan(300);
defaultRealm.setAccessCodeLifespan(60);
defaultRealm.setSslNotRequired(false);
defaultRealm.setCookieLoginAllowed(true);
defaultRealm.setRegistrationAllowed(true);
manager.generateRealmKeys(defaultRealm);
defaultRealm.updateRealm();
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
defaultRealm.getIdm().add(new SimpleRole(RegistrationService.REALM_CREATOR_ROLE));
}
public boolean isInstalled(RealmManager manager) {
return manager.defaultRealm() != null;
}
}

View file

@ -21,43 +21,30 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class TokenManager public class TokenManager {
{
protected RealmManager adapter;
public TokenManager(RealmManager adapter) public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user) {
{
this.adapter = adapter;
}
public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user)
{
SkeletonKeyToken token = new SkeletonKeyToken(); SkeletonKeyToken token = new SkeletonKeyToken();
token.id(adapter.generateId()); token.id(RealmManager.generateId());
token.principal(user.getLoginName()); token.principal(user.getLoginName());
token.audience(realm.getName()); token.audience(realm.getName());
token.issuedNow(); token.issuedNow();
token.issuedFor(client.getLoginName()); token.issuedFor(client.getLoginName());
if (realm.getTokenLifespan() > 0) if (realm.getTokenLifespan() > 0) {
{
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
} }
Map<String, ResourceModel> resourceMap = realm.getResourceMap(); Map<String, ResourceModel> resourceMap = realm.getResourceMap();
for (String res : scope.keySet()) for (String res : scope.keySet()) {
{
ResourceModel resource = resourceMap.get(res); ResourceModel resource = resourceMap.get(res);
Set<String> scopeMapping = resource.getScope(client); Set<String> scopeMapping = resource.getScope(client);
Set<String> roleMapping = resource.getRoleMappings(user); Set<String> roleMapping = resource.getRoleMappings(user);
SkeletonKeyToken.Access access = token.addAccess(resource.getName()); SkeletonKeyToken.Access access = token.addAccess(resource.getName());
for (String role : scope.get(res)) for (String role : scope.get(res)) {
{ if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) {
if (!scopeMapping.contains("*") && !scopeMapping.contains(role))
{
throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build()); throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build());
} }
if (!roleMapping.contains(role)) if (!roleMapping.contains(role)) {
{
throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build()); throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build());
} }
@ -67,17 +54,14 @@ public class TokenManager
return token; return token;
} }
public SkeletonKeyToken createScopedToken(String scopeParam, RealmModel realm, User client, User user) public SkeletonKeyToken createScopedToken(String scopeParam, RealmModel realm, User client, User user) {
{
SkeletonKeyScope scope = decodeScope(scopeParam); SkeletonKeyScope scope = decodeScope(scopeParam);
return createScopedToken(scope, realm, client, user); return createScopedToken(scope, realm, client, user);
} }
public SkeletonKeyToken createLoginToken(RealmModel realm, User client, User user) public SkeletonKeyToken createLoginToken(RealmModel realm, User client, User user) {
{
Set<String> mapping = realm.getScope(client); Set<String> mapping = realm.getScope(client);
if (!mapping.contains("*")) if (!mapping.contains("*")) {
{
throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized to request a user login.</p>").type("text/html").build()); throw new ForbiddenException(Response.status(403).entity("<h1>Security Alert</h1><p>Known client not authorized to request a user login.</p>").type("text/html").build());
} }
SkeletonKeyToken token = createAccessToken(realm, user); SkeletonKeyToken token = createAccessToken(realm, user);
@ -86,56 +70,45 @@ public class TokenManager
} }
public SkeletonKeyScope decodeScope(String scopeParam) public SkeletonKeyScope decodeScope(String scopeParam) {
{
SkeletonKeyScope scope = null; SkeletonKeyScope scope = null;
byte[] bytes = Base64Url.decode(scopeParam); byte[] bytes = Base64Url.decode(scopeParam);
try try {
{
scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes); scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes);
} } catch (IOException e) {
catch (IOException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return scope; return scope;
} }
public SkeletonKeyToken createAccessToken(RealmModel realm, User user) public SkeletonKeyToken createAccessToken(RealmModel realm, User user) {
{
List<ResourceModel> resources = realm.getResources(); List<ResourceModel> resources = realm.getResources();
SkeletonKeyToken token = new SkeletonKeyToken(); SkeletonKeyToken token = new SkeletonKeyToken();
token.id(adapter.generateId()); token.id(RealmManager.generateId());
token.issuedNow(); token.issuedNow();
token.principal(user.getLoginName()); token.principal(user.getLoginName());
token.audience(realm.getId()); token.audience(realm.getId());
if (realm.getTokenLifespan() > 0) if (realm.getTokenLifespan() > 0) {
{
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
} }
Set<String> realmMapping = realm.getRoleMappings(user); Set<String> realmMapping = realm.getRoleMappings(user);
if (realmMapping != null && realmMapping.size() > 0) if (realmMapping != null && realmMapping.size() > 0) {
{
SkeletonKeyToken.Access access = new SkeletonKeyToken.Access(); SkeletonKeyToken.Access access = new SkeletonKeyToken.Access();
for (String role : realmMapping) for (String role : realmMapping) {
{
access.addRole(role); access.addRole(role);
} }
token.setRealmAccess(access); token.setRealmAccess(access);
} }
if (resources != null) if (resources != null) {
{ for (ResourceModel resource : resources) {
for (ResourceModel resource : resources)
{
Set<String> mapping = resource.getRoleMappings(user); Set<String> mapping = resource.getRoleMappings(user);
if (mapping == null) continue; if (mapping == null) continue;
SkeletonKeyToken.Access access = token.addAccess(resource.getName()) SkeletonKeyToken.Access access = token.addAccess(resource.getName())
.verifyCaller(resource.isSurrogateAuthRequired()); .verifyCaller(resource.isSurrogateAuthRequired());
for (String role : mapping) for (String role : mapping) {
{
access.addRole(role); access.addRole(role);
} }
} }
@ -143,29 +116,23 @@ public class TokenManager
return token; return token;
} }
public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
{
SkeletonKeyToken token = new SkeletonKeyToken(); SkeletonKeyToken token = new SkeletonKeyToken();
token.id(adapter.generateId()); token.id(RealmManager.generateId());
token.issuedNow(); token.issuedNow();
token.principal(username); token.principal(username);
token.audience(realm.getId()); token.audience(realm.getId());
if (realm.getTokenLifespan() > 0) if (realm.getTokenLifespan() > 0) {
{
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
} }
return token; return token;
} }
public String encodeToken(RealmModel realm, SkeletonKeyToken token) public String encodeToken(RealmModel realm, SkeletonKeyToken token) {
{
byte[] tokenBytes = null; byte[] tokenBytes = null;
try try {
{
tokenBytes = JsonSerialization.toByteArray(token, false); tokenBytes = JsonSerialization.toByteArray(token, false);
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String encodedToken = new JWSBuilder() String encodedToken = new JWSBuilder()

View file

@ -1,49 +1,71 @@
package org.keycloak.services.models; package org.keycloak.services.models;
import org.keycloak.representations.idm.UserRepresentation;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.IdentityManager; import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.internal.IdentityManagerFactory;
import org.picketlink.idm.model.Realm; import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Role;
import org.picketlink.idm.model.SimpleAgent; import org.picketlink.idm.model.SimpleAgent;
import org.picketlink.idm.model.SimpleUser;
import org.picketlink.idm.model.User;
import javax.ws.rs.core.Response;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RealmManager public class RealmManager {
{
private static AtomicLong counter = new AtomicLong(1); private static AtomicLong counter = new AtomicLong(1);
public static String generateId() public static String generateId() {
{
return counter.getAndIncrement() + "-" + System.currentTimeMillis(); return counter.getAndIncrement() + "-" + System.currentTimeMillis();
} }
protected IdentityManagerFactory factory; protected IdentitySession IdentitySession;
public RealmManager(IdentityManagerFactory factory) public RealmManager(IdentitySession IdentitySession) {
{ this.IdentitySession = IdentitySession;
this.factory = factory;
} }
public RealmModel getRealm(String id) public RealmModel defaultRealm() {
{ return getRealm(Realm.DEFAULT_REALM);
Realm existing = factory.findRealm(id); }
if (existing == null)
{ public RealmModel getRealm(String id) {
Realm existing = IdentitySession.findRealm(id);
if (existing == null) {
return null; return null;
} }
return new RealmModel(existing, factory); return new RealmModel(existing, IdentitySession);
} }
public RealmModel create(String name) public RealmModel createRealm(String name) {
{ return createRealm(generateId(), name);
Realm newRealm = factory.createRealm(generateId()); }
IdentityManager idm = factory.createIdentityManager(newRealm);
public RealmModel createRealm(String id, String name) {
Realm newRealm = IdentitySession.createRealm(id);
IdentityManager idm = IdentitySession.createIdentityManager(newRealm);
SimpleAgent agent = new SimpleAgent(RealmModel.REALM_AGENT_ID); SimpleAgent agent = new SimpleAgent(RealmModel.REALM_AGENT_ID);
idm.add(agent); idm.add(agent);
return new RealmModel(newRealm, factory); RealmModel realm = new RealmModel(newRealm, IdentitySession);
return realm;
} }
public void generateRealmKeys(RealmModel realm) {
KeyPair keyPair = null;
try {
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
realm.setPrivateKey(keyPair.getPrivate());
realm.setPublicKey(keyPair.getPublic());
realm.updateRealm();
}
} }

View file

@ -4,20 +4,19 @@ import org.bouncycastle.openssl.PEMWriter;
import org.jboss.resteasy.security.PemUtils; import org.jboss.resteasy.security.PemUtils;
import org.keycloak.representations.idm.RequiredCredentialRepresentation; import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.services.models.relationships.RealmAdminRelationship; import org.keycloak.services.models.relationships.RealmAdminRelationship;
import org.keycloak.services.models.relationships.RealmResourceRelationship; import org.keycloak.services.models.relationships.ResourceRelationship;
import org.keycloak.services.models.relationships.RequiredCredentialRelationship; import org.keycloak.services.models.relationships.RequiredCredentialRelationship;
import org.keycloak.services.models.relationships.ScopeRelationship; import org.keycloak.services.models.relationships.ScopeRelationship;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.IdentityManager; import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.TOTPCredential; import org.picketlink.idm.credential.TOTPCredential;
import org.picketlink.idm.credential.X509CertificateCredentials; import org.picketlink.idm.credential.X509CertificateCredentials;
import org.picketlink.idm.internal.IdentityManagerFactory;
import org.picketlink.idm.model.Agent; import org.picketlink.idm.model.Agent;
import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.Grant; import org.picketlink.idm.model.Grant;
import org.picketlink.idm.model.Realm; import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Role; import org.picketlink.idm.model.Role;
import org.picketlink.idm.model.SimpleAgent;
import org.picketlink.idm.model.Tier; import org.picketlink.idm.model.Tier;
import org.picketlink.idm.model.User; import org.picketlink.idm.model.User;
import org.picketlink.idm.query.IdentityQuery; import org.picketlink.idm.query.IdentityQuery;
@ -39,8 +38,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RealmModel public class RealmModel {
{
public static final String REALM_AGENT_ID = "_realm_"; public static final String REALM_AGENT_ID = "_realm_";
public static final String REALM_NAME = "name"; public static final String REALM_NAME = "name";
public static final String REALM_ACCESS_CODE_LIFESPAN = "accessCodeLifespan"; public static final String REALM_ACCESS_CODE_LIFESPAN = "accessCodeLifespan";
@ -49,199 +47,168 @@ public class RealmModel
public static final String REALM_PUBLIC_KEY = "publicKey"; public static final String REALM_PUBLIC_KEY = "publicKey";
public static final String REALM_IS_SSL_NOT_REQUIRED = "isSSLNotRequired"; public static final String REALM_IS_SSL_NOT_REQUIRED = "isSSLNotRequired";
public static final String REALM_IS_COOKIE_LOGIN_ALLOWED = "isCookieLoginAllowed"; public static final String REALM_IS_COOKIE_LOGIN_ALLOWED = "isCookieLoginAllowed";
public static final String REALM_IS_REGISTRATION_ALLOWED = "isRegistrationAllowed";
protected Realm realm; protected Realm realm;
protected Agent realmAgent; protected Agent realmAgent;
protected IdentityManagerFactory factory; protected IdentitySession identitySession;
protected volatile transient PublicKey publicKey; protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey; protected volatile transient PrivateKey privateKey;
public RealmModel(Realm realm, IdentityManagerFactory factory) public RealmModel(Realm realm, IdentitySession session) {
{
this.realm = realm; this.realm = realm;
this.factory = factory; this.identitySession = session;
realmAgent = getIdm().getAgent(REALM_AGENT_ID); realmAgent = getIdm().getAgent(REALM_AGENT_ID);
} }
public IdentityManager getIdm() public IdentityManager getIdm() {
{ return identitySession.createIdentityManager(realm);
return factory.createIdentityManager(realm);
} }
public void updateRealm() public void updateRealm() {
{
getIdm().update(realmAgent); getIdm().update(realmAgent);
} }
public String getId() public String getId() {
{
return realm.getId(); return realm.getId();
} }
public String getName() public String getName() {
{
return (String) realmAgent.getAttribute(REALM_NAME).getValue(); return (String) realmAgent.getAttribute(REALM_NAME).getValue();
} }
public void setName(String name) public void setName(String name) {
{
realmAgent.setAttribute(new Attribute<String>(REALM_NAME, name)); realmAgent.setAttribute(new Attribute<String>(REALM_NAME, name));
} }
public boolean isEnabled() public boolean isEnabled() {
{
return realmAgent.isEnabled(); return realmAgent.isEnabled();
} }
public void setEnabled(boolean enabled) public void setEnabled(boolean enabled) {
{
realmAgent.setEnabled(enabled); realmAgent.setEnabled(enabled);
} }
public boolean isSslNotRequired() public boolean isSslNotRequired() {
{
return (Boolean) realmAgent.getAttribute(REALM_IS_SSL_NOT_REQUIRED).getValue(); return (Boolean) realmAgent.getAttribute(REALM_IS_SSL_NOT_REQUIRED).getValue();
} }
public void setSslNotRequired(boolean sslNotRequired) public void setSslNotRequired(boolean sslNotRequired) {
{
realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_SSL_NOT_REQUIRED, sslNotRequired)); realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_SSL_NOT_REQUIRED, sslNotRequired));
} }
public boolean isCookieLoginAllowed() public boolean isCookieLoginAllowed() {
{
return (Boolean) realmAgent.getAttribute(REALM_IS_COOKIE_LOGIN_ALLOWED).getValue(); return (Boolean) realmAgent.getAttribute(REALM_IS_COOKIE_LOGIN_ALLOWED).getValue();
} }
public void setCookieLoginAllowed(boolean cookieLoginAllowed) public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
{
realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed)); realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed));
} }
public long getTokenLifespan() public boolean isRegistrationAllowed() {
{ return (Boolean) realmAgent.getAttribute(REALM_IS_REGISTRATION_ALLOWED).getValue();
}
public void setRegistrationAllowed(boolean registrationAllowed) {
realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_REGISTRATION_ALLOWED, registrationAllowed));
}
public long getTokenLifespan() {
return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue(); return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue();
} }
public void setTokenLifespan(long tokenLifespan) public void setTokenLifespan(long tokenLifespan) {
{
realmAgent.setAttribute(new Attribute<Long>(REALM_TOKEN_LIFESPAN, tokenLifespan)); realmAgent.setAttribute(new Attribute<Long>(REALM_TOKEN_LIFESPAN, tokenLifespan));
} }
public long getAccessCodeLifespan() public long getAccessCodeLifespan() {
{
return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue(); return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue();
} }
public void setAccessCodeLifespan(long accessCodeLifespan) public void setAccessCodeLifespan(long accessCodeLifespan) {
{
realmAgent.setAttribute(new Attribute<Long>(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan)); realmAgent.setAttribute(new Attribute<Long>(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan));
} }
public String getPublicKeyPem() public String getPublicKeyPem() {
{
return (String) realmAgent.getAttribute(REALM_PUBLIC_KEY).getValue(); return (String) realmAgent.getAttribute(REALM_PUBLIC_KEY).getValue();
} }
public void setPublicKeyPem(String publicKeyPem) public void setPublicKeyPem(String publicKeyPem) {
{
realmAgent.setAttribute(new Attribute<String>(REALM_PUBLIC_KEY, publicKeyPem)); realmAgent.setAttribute(new Attribute<String>(REALM_PUBLIC_KEY, publicKeyPem));
this.publicKey = null; this.publicKey = null;
} }
public String getPrivateKeyPem() public String getPrivateKeyPem() {
{
return (String) realmAgent.getAttribute(REALM_PRIVATE_KEY).getValue(); return (String) realmAgent.getAttribute(REALM_PRIVATE_KEY).getValue();
} }
public void setPrivateKeyPem(String privateKeyPem) public void setPrivateKeyPem(String privateKeyPem) {
{
realmAgent.setAttribute(new Attribute<String>(REALM_PRIVATE_KEY, privateKeyPem)); realmAgent.setAttribute(new Attribute<String>(REALM_PRIVATE_KEY, privateKeyPem));
this.privateKey = null; this.privateKey = null;
} }
public PublicKey getPublicKey() public PublicKey getPublicKey() {
{
if (publicKey != null) return publicKey; if (publicKey != null) return publicKey;
String pem = getPublicKeyPem(); String pem = getPublicKeyPem();
if (pem != null) if (pem != null) {
{ try {
try
{
publicKey = PemUtils.decodePublicKey(pem); publicKey = PemUtils.decodePublicKey(pem);
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
return publicKey; return publicKey;
} }
public void setPublicKey(PublicKey publicKey) public void setPublicKey(PublicKey publicKey) {
{
this.publicKey = publicKey; this.publicKey = publicKey;
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
PEMWriter pemWriter = new PEMWriter(writer); PEMWriter pemWriter = new PEMWriter(writer);
try try {
{
pemWriter.writeObject(publicKey); pemWriter.writeObject(publicKey);
pemWriter.flush(); pemWriter.flush();
} } catch (IOException e) {
catch (IOException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String s = writer.toString(); String s = writer.toString();
setPublicKeyPem(PemUtils.removeBeginEnd(s)); setPublicKeyPem(PemUtils.removeBeginEnd(s));
} }
public PrivateKey getPrivateKey() public PrivateKey getPrivateKey() {
{
if (privateKey != null) return privateKey; if (privateKey != null) return privateKey;
String pem = getPrivateKeyPem(); String pem = getPrivateKeyPem();
if (pem != null) if (pem != null) {
{ try {
try
{
privateKey = PemUtils.decodePrivateKey(pem); privateKey = PemUtils.decodePrivateKey(pem);
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
return privateKey; return privateKey;
} }
public void setPrivateKey(PrivateKey privateKey) public void setPrivateKey(PrivateKey privateKey) {
{
this.privateKey = privateKey; this.privateKey = privateKey;
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
PEMWriter pemWriter = new PEMWriter(writer); PEMWriter pemWriter = new PEMWriter(writer);
try try {
{
pemWriter.writeObject(privateKey); pemWriter.writeObject(privateKey);
pemWriter.flush(); pemWriter.flush();
} } catch (IOException e) {
catch (IOException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String s = writer.toString(); String s = writer.toString();
setPrivateKeyPem(PemUtils.removeBeginEnd(s)); setPrivateKeyPem(PemUtils.removeBeginEnd(s));
} }
public List<RequiredCredentialModel> getRequiredCredentials() public List<RequiredCredentialModel> getRequiredCredentials() {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
Agent realmAgent = idm.getAgent(REALM_AGENT_ID); Agent realmAgent = idm.getAgent(REALM_AGENT_ID);
RelationshipQuery<RequiredCredentialRelationship> query = idm.createRelationshipQuery(RequiredCredentialRelationship.class); RelationshipQuery<RequiredCredentialRelationship> query = idm.createRelationshipQuery(RequiredCredentialRelationship.class);
query.setParameter(RequiredCredentialRelationship.REALM_AGENT, realmAgent); query.setParameter(RequiredCredentialRelationship.REALM_AGENT, realmAgent);
List<RequiredCredentialRelationship> results = query.getResultList(); List<RequiredCredentialRelationship> results = query.getResultList();
List<RequiredCredentialModel> rtn = new ArrayList<RequiredCredentialModel>(); List<RequiredCredentialModel> rtn = new ArrayList<RequiredCredentialModel>();
for (RequiredCredentialRelationship relationship : results) for (RequiredCredentialRelationship relationship : results) {
{
RequiredCredentialModel model = new RequiredCredentialModel(); RequiredCredentialModel model = new RequiredCredentialModel();
model.setInput(relationship.isInput()); model.setInput(relationship.isInput());
model.setSecret(relationship.isSecret()); model.setSecret(relationship.isSecret());
@ -251,8 +218,7 @@ public class RealmModel
return rtn; return rtn;
} }
public void addRequiredCredential(RequiredCredentialModel cred) public void addRequiredCredential(RequiredCredentialModel cred) {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
Agent realmAgent = idm.getAgent(REALM_AGENT_ID); Agent realmAgent = idm.getAgent(REALM_AGENT_ID);
RequiredCredentialRelationship relationship = new RequiredCredentialRelationship(); RequiredCredentialRelationship relationship = new RequiredCredentialRelationship();
@ -263,28 +229,19 @@ public class RealmModel
idm.add(relationship); idm.add(relationship);
} }
public void updateCredential(User user, UserCredentialModel cred) public void updateCredential(User user, UserCredentialModel cred) {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) {
{
Password password = new Password(cred.getValue()); Password password = new Password(cred.getValue());
idm.updateCredential(user, password); idm.updateCredential(user, password);
} } else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) {
else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP))
{
TOTPCredential totp = new TOTPCredential(cred.getValue()); TOTPCredential totp = new TOTPCredential(cred.getValue());
idm.updateCredential(user, totp); idm.updateCredential(user, totp);
} } else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT)) {
else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT))
{
X509Certificate cert = null; X509Certificate cert = null;
try try {
{
cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue()); cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
X509CertificateCredentials creds = new X509CertificateCredentials(cert); X509CertificateCredentials creds = new X509CertificateCredentials(cert);
@ -292,8 +249,7 @@ public class RealmModel
} }
} }
public List<Role> getRoles() public List<Role> getRoles() {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
IdentityQuery<Role> query = idm.createIdentityQuery(Role.class); IdentityQuery<Role> query = idm.createIdentityQuery(Role.class);
query.setParameter(Role.PARTITION, realm); query.setParameter(Role.PARTITION, realm);
@ -301,73 +257,57 @@ public class RealmModel
} }
protected ResourceModel loadResource(Agent resource)
{
Tier tier = factory.findTier(resource.getPartition().getId());
return new ResourceModel(tier, resource, this, factory);
}
/** /**
* Key name, value resource * Key name, value resource
* *
* @return * @return
*/ */
public Map<String, ResourceModel> getResourceMap() public Map<String, ResourceModel> getResourceMap() {
{
Map<String, ResourceModel> resourceMap = new HashMap<String, ResourceModel>(); Map<String, ResourceModel> resourceMap = new HashMap<String, ResourceModel>();
for (ResourceModel resource : getResources()) for (ResourceModel resource : getResources()) {
{
resourceMap.put(resource.getName(), resource); resourceMap.put(resource.getName(), resource);
} }
return resourceMap; return resourceMap;
} }
public List<ResourceModel> getResources() public List<ResourceModel> getResources() {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
RelationshipQuery<RealmResourceRelationship> query = idm.createRelationshipQuery(RealmResourceRelationship.class); RelationshipQuery<ResourceRelationship> query = idm.createRelationshipQuery(ResourceRelationship.class);
query.setParameter(RealmResourceRelationship.REALM_AGENT, realmAgent); query.setParameter(ResourceRelationship.REALM_AGENT, realmAgent);
List<RealmResourceRelationship> results = query.getResultList(); List<ResourceRelationship> results = query.getResultList();
List<ResourceModel> resources = new ArrayList<ResourceModel>(); List<ResourceModel> resources = new ArrayList<ResourceModel>();
for (RealmResourceRelationship relationship : results) for (ResourceRelationship relationship : results) {
{ Tier resourceTier = identitySession.findTier(relationship.getResourceId());
ResourceModel model = loadResource(relationship.getResourceAgent()); ResourceModel model = new ResourceModel(resourceTier,relationship, this, identitySession);
resources.add(model); resources.add(model);
} }
return resources; return resources;
} }
public ResourceModel addResource(String name) public ResourceModel addResource(String name) {
{ Tier newTier = identitySession.createTier(RealmManager.generateId());
Tier newTier = factory.createTier(RealmManager.generateId()); IdentityManager idm = getIdm();
IdentityManager idm = factory.createIdentityManager(newTier); ResourceRelationship relationship = new ResourceRelationship();
SimpleAgent resourceAgent = new SimpleAgent(ResourceModel.RESOURCE_AGENT_ID); relationship.setResourceName(name);
resourceAgent.setAttribute(new Attribute<String>(ResourceModel.RESOURCE_NAME, name));
idm.add(resourceAgent);
idm = getIdm();
RealmResourceRelationship relationship = new RealmResourceRelationship();
relationship.setRealmAgent(realmAgent); relationship.setRealmAgent(realmAgent);
relationship.setResourceAgent(resourceAgent); relationship.setResourceId(newTier.getId());
idm.add(relationship); idm.add(relationship);
return new ResourceModel(newTier, resourceAgent, this, factory); return new ResourceModel(newTier, relationship, this, identitySession);
} }
public Set<String> getRoleMappings(User user) public Set<String> getRoleMappings(User user) {
{
RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class); RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class);
query.setParameter(Grant.ASSIGNEE, user); query.setParameter(Grant.ASSIGNEE, user);
List<Grant> grants = query.getResultList(); List<Grant> grants = query.getResultList();
HashSet<String> set = new HashSet<String>(); HashSet<String> set = new HashSet<String>();
for (Grant grant : grants) for (Grant grant : grants) {
{
if (grant.getRole().getPartition().getId().equals(realm.getId())) set.add(grant.getRole().getName()); if (grant.getRole().getPartition().getId().equals(realm.getId())) set.add(grant.getRole().getName());
} }
return set; return set;
} }
public void addScope(Agent agent, String roleName) public void addScope(Agent agent, String roleName) {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
Role role = idm.getRole(roleName); Role role = idm.getRole(roleName);
if (role == null) throw new RuntimeException("role not found"); if (role == null) throw new RuntimeException("role not found");
@ -378,35 +318,31 @@ public class RealmModel
} }
public Set<String> getScope(Agent agent) public Set<String> getScope(Agent agent) {
{
RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class); RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class);
query.setParameter(ScopeRelationship.CLIENT, agent); query.setParameter(ScopeRelationship.CLIENT, agent);
List<ScopeRelationship> scope = query.getResultList(); List<ScopeRelationship> scope = query.getResultList();
HashSet<String> set = new HashSet<String>(); HashSet<String> set = new HashSet<String>();
for (ScopeRelationship rel : scope) for (ScopeRelationship rel : scope) {
{
if (rel.getScope().getPartition().getId().equals(realm.getId())) set.add(rel.getScope().getName()); if (rel.getScope().getPartition().getId().equals(realm.getId())) set.add(rel.getScope().getName());
} }
return set; return set;
} }
public boolean isRealmAdmin(Agent agent) public boolean isRealmAdmin(Agent agent) {
{ IdentityManager idm = new RealmManager(identitySession).defaultRealm().getIdm();
IdentityManager idm = getIdm();
RelationshipQuery<RealmAdminRelationship> query = idm.createRelationshipQuery(RealmAdminRelationship.class); RelationshipQuery<RealmAdminRelationship> query = idm.createRelationshipQuery(RealmAdminRelationship.class);
query.setParameter(RealmAdminRelationship.REALM, realm); query.setParameter(RealmAdminRelationship.REALM, realm.getId());
query.setParameter(RealmAdminRelationship.ADMIN, agent); query.setParameter(RealmAdminRelationship.ADMIN, agent);
List<RealmAdminRelationship> results = query.getResultList(); List<RealmAdminRelationship> results = query.getResultList();
return results.size() > 0; return results.size() > 0;
} }
public void addRealmAdmin(Agent agent) public void addRealmAdmin(Agent agent) {
{ IdentityManager idm = new RealmManager(identitySession).defaultRealm().getIdm();
IdentityManager idm = getIdm();
RealmAdminRelationship relationship = new RealmAdminRelationship(); RealmAdminRelationship relationship = new RealmAdminRelationship();
relationship.setAdmin(agent); relationship.setAdmin(agent);
relationship.setRealm(realm); relationship.setRealm(realm.getId());
idm.add(relationship); idm.add(relationship);
} }
} }

View file

@ -1,42 +1,48 @@
package org.keycloak.services.models; package org.keycloak.services.models;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RequiredCredentialModel public class RequiredCredentialModel {
{
protected String type; protected String type;
protected boolean input; protected boolean input;
protected boolean secret; protected boolean secret;
public String getType() public RequiredCredentialModel() {
{ }
public RequiredCredentialModel(String type, boolean input, boolean secret) {
this.type = type;
this.input = input;
this.secret = secret;
}
public String getType() {
return type; return type;
} }
public void setType(String type) public void setType(String type) {
{
this.type = type; this.type = type;
} }
public boolean isInput() public boolean isInput() {
{
return input; return input;
} }
public void setInput(boolean input) public void setInput(boolean input) {
{
this.input = input; this.input = input;
} }
public boolean isSecret() public boolean isSecret() {
{
return secret; return secret;
} }
public void setSecret(boolean secret) public void setSecret(boolean secret) {
{
this.secret = secret; this.secret = secret;
} }
public static final RequiredCredentialModel PASSWORD = new RequiredCredentialModel(RequiredCredentialRepresentation.PASSWORD, true, true);
} }

View file

@ -1,8 +1,9 @@
package org.keycloak.services.models; package org.keycloak.services.models;
import org.keycloak.services.models.relationships.ResourceRelationship;
import org.keycloak.services.models.relationships.ScopeRelationship; import org.keycloak.services.models.relationships.ScopeRelationship;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.IdentityManager; import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.internal.IdentityManagerFactory;
import org.picketlink.idm.model.Agent; import org.picketlink.idm.model.Agent;
import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.Grant; import org.picketlink.idm.model.Grant;
@ -20,93 +21,77 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class ResourceModel public class ResourceModel {
{
public static final String RESOURCE_AGENT_ID = "_resource_"; public static final String RESOURCE_AGENT_ID = "_resource_";
public static final String RESOURCE_NAME = "name"; public static final String RESOURCE_NAME = "name";
public static final String RESOURCE_SURROGATE_AUTH = "surrogate_auth"; public static final String RESOURCE_SURROGATE_AUTH = "surrogate_auth";
protected Tier tier; protected Tier tier;
protected Agent agent; protected ResourceRelationship agent;
protected RealmModel realm; protected RealmModel realm;
protected IdentityManagerFactory factory; protected IdentitySession IdentitySession;
public ResourceModel(Tier tier, Agent agent, RealmModel realm, IdentityManagerFactory factory) public ResourceModel(Tier tier, ResourceRelationship agent, RealmModel realm, IdentitySession factory) {
{
this.tier = tier; this.tier = tier;
this.agent = agent; this.agent = agent;
this.realm = realm; this.realm = realm;
this.factory = factory; this.IdentitySession = factory;
} }
public IdentityManager getIdm() public IdentityManager getIdm() {
{ return IdentitySession.createIdentityManager(tier);
return factory.createIdentityManager(tier);
} }
public void updateResource() public void updateResource() {
{
getIdm().update(agent); getIdm().update(agent);
} }
public String getId() public String getId() {
{
return tier.getId(); return tier.getId();
} }
public String getName() public String getName() {
{ return agent.getResourceName();
return (String)agent.getAttribute(RESOURCE_NAME).getValue();
} }
public void setName(String name) public void setName(String name) {
{ agent.setResourceName(name);
agent.setAttribute(new Attribute<String>(RESOURCE_NAME, name));
getIdm().update(agent);
} }
public boolean isEnabled() public boolean isEnabled() {
{ return agent.getEnabled();
return agent.isEnabled();
} }
public void setEnabled(boolean enabled) public void setEnabled(boolean enabled) {
{
agent.setEnabled(enabled); agent.setEnabled(enabled);
} }
public boolean isSurrogateAuthRequired() public boolean isSurrogateAuthRequired() {
{ return agent.getSurrogateAuthRequired();
return (Boolean)agent.getAttribute(RESOURCE_SURROGATE_AUTH).getValue();
} }
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
{ agent.setSurrogateAuthRequired(surrogateAuthRequired);
agent.setAttribute(new Attribute<Boolean>(RESOURCE_SURROGATE_AUTH, surrogateAuthRequired));
} }
public List<Role> getRoles() public List<Role> getRoles() {
{
IdentityQuery<Role> query = getIdm().createIdentityQuery(Role.class); IdentityQuery<Role> query = getIdm().createIdentityQuery(Role.class);
query.setParameter(Role.PARTITION, tier); query.setParameter(Role.PARTITION, tier);
return query.getResultList(); return query.getResultList();
} }
public Set<String> getRoleMappings(User user) public Set<String> getRoleMappings(User user) {
{
RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class); RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class);
query.setParameter(Grant.ASSIGNEE, user); query.setParameter(Grant.ASSIGNEE, user);
List<Grant> grants = query.getResultList(); List<Grant> grants = query.getResultList();
HashSet<String> set = new HashSet<String>(); HashSet<String> set = new HashSet<String>();
for (Grant grant : grants) for (Grant grant : grants) {
{
if (grant.getRole().getPartition().getId().equals(tier.getId())) set.add(grant.getRole().getName()); if (grant.getRole().getPartition().getId().equals(tier.getId())) set.add(grant.getRole().getName());
} }
return set; return set;
} }
public void addScope(Agent agent, String roleName) public void addScope(Agent agent, String roleName) {
{
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
Role role = idm.getRole(roleName); Role role = idm.getRole(roleName);
if (role == null) throw new RuntimeException("role not found"); if (role == null) throw new RuntimeException("role not found");
@ -116,14 +101,12 @@ public class ResourceModel
} }
public Set<String> getScope(Agent agent) public Set<String> getScope(Agent agent) {
{
RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class); RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class);
query.setParameter(ScopeRelationship.CLIENT, agent); query.setParameter(ScopeRelationship.CLIENT, agent);
List<ScopeRelationship> scope = query.getResultList(); List<ScopeRelationship> scope = query.getResultList();
HashSet<String> set = new HashSet<String>(); HashSet<String> set = new HashSet<String>();
for (ScopeRelationship rel : scope) for (ScopeRelationship rel : scope) {
{
if (rel.getScope().getPartition().getId().equals(tier.getId())) set.add(rel.getScope().getName()); if (rel.getScope().getPartition().getId().equals(tier.getId())) set.add(rel.getScope().getName());
} }
return set; return set;

View file

@ -4,29 +4,24 @@ package org.keycloak.services.models;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserCredentialModel public class UserCredentialModel {
{
protected String type; protected String type;
protected String value; protected String value;
public String getType() public String getType() {
{
return type; return type;
} }
public void setType(String type) public void setType(String type) {
{
this.type = type; this.type = type;
} }
public String getValue() public String getValue() {
{
return value; return value;
} }
public void setValue(String value) public void setValue(String value) {
{
this.value = value; this.value = value;
} }
} }

View file

@ -4,6 +4,7 @@ import org.picketlink.idm.model.AbstractAttributedType;
import org.picketlink.idm.model.Agent; import org.picketlink.idm.model.Agent;
import org.picketlink.idm.model.Realm; import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Relationship; import org.picketlink.idm.model.Relationship;
import org.picketlink.idm.model.annotation.AttributeProperty;
import org.picketlink.idm.model.annotation.IdentityProperty; import org.picketlink.idm.model.annotation.IdentityProperty;
import org.picketlink.idm.query.RelationshipQueryParameter; import org.picketlink.idm.query.RelationshipQueryParameter;
@ -11,8 +12,7 @@ import org.picketlink.idm.query.RelationshipQueryParameter;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RealmAdminRelationship extends AbstractAttributedType implements Relationship public class RealmAdminRelationship extends AbstractAttributedType implements Relationship {
{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final RelationshipQueryParameter REALM = new RelationshipQueryParameter() { public static final RelationshipQueryParameter REALM = new RelationshipQueryParameter() {
@ -31,28 +31,24 @@ public class RealmAdminRelationship extends AbstractAttributedType implements Re
} }
}; };
protected Realm realm; protected String realm;
protected Agent admin; protected Agent admin;
@IdentityProperty @AttributeProperty
public Realm getRealm() public String getRealm() {
{
return realm; return realm;
} }
public void setRealm(Realm realm) public void setRealm(String realm) {
{
this.realm = realm; this.realm = realm;
} }
@IdentityProperty @IdentityProperty
public Agent getAdmin() public Agent getAdmin() {
{
return admin; return admin;
} }
public void setAdmin(Agent admin) public void setAdmin(Agent admin) {
{
this.admin = admin; this.admin = admin;
} }
} }

View file

@ -1,57 +0,0 @@
package org.keycloak.services.models.relationships;
import org.picketlink.idm.model.AbstractAttributedType;
import org.picketlink.idm.model.Agent;
import org.picketlink.idm.model.Relationship;
import org.picketlink.idm.model.annotation.IdentityProperty;
import org.picketlink.idm.query.RelationshipQueryParameter;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmResourceRelationship extends AbstractAttributedType implements Relationship
{
private static final long serialVersionUID = 1L;
public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() {
@Override
public String getName() {
return "realmAgent";
}
};
public static final RelationshipQueryParameter RESOURCE_AGENT = new RelationshipQueryParameter() {
@Override
public String getName() {
return "resourceAgent";
}
};
protected Agent realmAgent;
protected Agent resourceAgent;
@IdentityProperty
public Agent getRealmAgent()
{
return realmAgent;
}
public void setRealmAgent(Agent realmAgent)
{
this.realmAgent = realmAgent;
}
@IdentityProperty
public Agent getResourceAgent()
{
return resourceAgent;
}
public void setResourceAgent(Agent resourceAgent)
{
this.resourceAgent = resourceAgent;
}
}

View file

@ -11,8 +11,7 @@ import org.picketlink.idm.query.RelationshipQueryParameter;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RequiredCredentialRelationship extends AbstractAttributedType implements Relationship public class RequiredCredentialRelationship extends AbstractAttributedType implements Relationship {
{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() { public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() {
@ -29,51 +28,42 @@ public class RequiredCredentialRelationship extends AbstractAttributedType imple
protected boolean input; protected boolean input;
protected boolean secret; protected boolean secret;
public RequiredCredentialRelationship() public RequiredCredentialRelationship() {
{
} }
@IdentityProperty @IdentityProperty
public Agent getRealmAgent() public Agent getRealmAgent() {
{
return realmAgent; return realmAgent;
} }
public void setRealmAgent(Agent realmAgent) public void setRealmAgent(Agent realmAgent) {
{
this.realmAgent = realmAgent; this.realmAgent = realmAgent;
} }
@AttributeProperty @AttributeProperty
public String getCredentialType() public String getCredentialType() {
{
return credentialType; return credentialType;
} }
public void setCredentialType(String credentialType) public void setCredentialType(String credentialType) {
{
this.credentialType = credentialType; this.credentialType = credentialType;
} }
@AttributeProperty @AttributeProperty
public boolean isInput() public boolean isInput() {
{
return input; return input;
} }
public void setInput(boolean input) public void setInput(boolean input) {
{
this.input = input; this.input = input;
} }
@AttributeProperty @AttributeProperty
public boolean isSecret() public boolean isSecret() {
{
return secret; return secret;
} }
public void setSecret(boolean secret) public void setSecret(boolean secret) {
{
this.secret = secret; this.secret = secret;
} }
} }

View file

@ -0,0 +1,75 @@
package org.keycloak.services.models.relationships;
import org.picketlink.idm.model.AbstractAttributedType;
import org.picketlink.idm.model.Agent;
import org.picketlink.idm.model.Relationship;
import org.picketlink.idm.model.annotation.AttributeProperty;
import org.picketlink.idm.model.annotation.IdentityProperty;
import org.picketlink.idm.query.RelationshipQueryParameter;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ResourceRelationship extends AbstractAttributedType implements Relationship {
private static final long serialVersionUID = 1L;
public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() {
@Override
public String getName() {
return "realmAgent";
}
};
protected Agent realmAgent;
protected String resourceId;
protected String resourceName;
protected boolean surrogateAuthRequired;
protected boolean enabled;
@IdentityProperty
public Agent getRealmAgent() {
return realmAgent;
}
public void setRealmAgent(Agent realmAgent) {
this.realmAgent = realmAgent;
}
@AttributeProperty
public String getResourceId() {
return resourceId;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
@AttributeProperty
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
@AttributeProperty
public boolean getSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
@AttributeProperty
public boolean getEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View file

@ -11,8 +11,7 @@ import org.picketlink.idm.query.RelationshipQueryParameter;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class ScopeRelationship extends AbstractAttributedType implements Relationship public class ScopeRelationship extends AbstractAttributedType implements Relationship {
{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final RelationshipQueryParameter CLIENT = new RelationshipQueryParameter() { public static final RelationshipQueryParameter CLIENT = new RelationshipQueryParameter() {
@ -27,24 +26,20 @@ public class ScopeRelationship extends AbstractAttributedType implements Relatio
protected Role scope; protected Role scope;
@IdentityProperty @IdentityProperty
public Agent getClient() public Agent getClient() {
{
return client; return client;
} }
public void setClient(Agent client) public void setClient(Agent client) {
{
this.client = client; this.client = client;
} }
@IdentityProperty @IdentityProperty
public Role getScope() public Role getScope() {
{
return scope; return scope;
} }
public void setScope(Role scope) public void setScope(Role scope) {
{
this.scope = scope; this.scope = scope;
} }
} }

View file

@ -0,0 +1,90 @@
package org.keycloak.services.resources;
import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.services.filters.IdentitySessionFilter;
import org.keycloak.services.models.relationships.RealmAdminRelationship;
import org.keycloak.services.models.relationships.ResourceRelationship;
import org.keycloak.services.models.relationships.RequiredCredentialRelationship;
import org.keycloak.services.models.relationships.ScopeRelationship;
import org.picketlink.idm.IdentitySessionFactory;
import org.picketlink.idm.config.IdentityConfiguration;
import org.picketlink.idm.config.IdentityConfigurationBuilder;
import org.picketlink.idm.internal.DefaultIdentitySessionFactory;
import org.picketlink.idm.jpa.internal.ResourceLocalJpaIdentitySessionHandler;
import org.picketlink.idm.jpa.schema.CredentialObject;
import org.picketlink.idm.jpa.schema.CredentialObjectAttribute;
import org.picketlink.idm.jpa.schema.IdentityObject;
import org.picketlink.idm.jpa.schema.IdentityObjectAttribute;
import org.picketlink.idm.jpa.schema.PartitionObject;
import org.picketlink.idm.jpa.schema.RelationshipIdentityObject;
import org.picketlink.idm.jpa.schema.RelationshipObject;
import org.picketlink.idm.jpa.schema.RelationshipObjectAttribute;
import javax.annotation.PreDestroy;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakApplication extends Application {
protected Set<Object> singletons = new HashSet<Object>();
protected Set<Class<?>> classes = new HashSet<Class<?>>();
protected IdentitySessionFactory factory;
public KeycloakApplication() {
this.factory = createFactory();
IdentitySessionFilter filter = new IdentitySessionFilter(factory);
singletons.add(new RealmsResource());
singletons.add(filter);
classes.add(SkeletonKeyContextResolver.class);
classes.add(RegistrationService.class);
}
public IdentitySessionFactory getFactory() {
return factory;
}
@PreDestroy
public void destroy() {
factory.close();
}
public static IdentitySessionFactory createFactory() {
ResourceLocalJpaIdentitySessionHandler handler = new ResourceLocalJpaIdentitySessionHandler("keycloak-identity-store");
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
builder
.stores()
.jpa()
.identityClass(IdentityObject.class)
.attributeClass(IdentityObjectAttribute.class)
.relationshipClass(RelationshipObject.class)
.relationshipIdentityClass(RelationshipIdentityObject.class)
.relationshipAttributeClass(RelationshipObjectAttribute.class)
.credentialClass(CredentialObject.class)
.credentialAttributeClass(CredentialObjectAttribute.class)
.partitionClass(PartitionObject.class)
.supportAllFeatures()
.supportRelationshipType(RealmAdminRelationship.class, ResourceRelationship.class, RequiredCredentialRelationship.class, ScopeRelationship.class)
.setIdentitySessionHandler(handler);
IdentityConfiguration build = builder.build();
return new DefaultIdentitySessionFactory(build);
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}

View file

@ -1,343 +0,0 @@
package org.keycloak.services.resources;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.RoleMappingRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.models.RealmManager;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.UserCredentialModel;
import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Role;
import org.picketlink.idm.model.SimpleRole;
import org.picketlink.idm.model.SimpleUser;
import org.picketlink.idm.model.User;
import javax.ws.rs.Consumes;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Path("/realmfactory")
public class RealmFactory
{
protected Logger logger = Logger.getLogger(RealmFactory.class);
protected RealmManager adapter;
protected AuthenticationManager authenticationManager;
protected RealmModel defaultRealm;
@Context
protected UriInfo uriInfo;
@Context
protected HttpHeaders headers;
public RealmFactory(RealmManager adapter)
{
this.adapter = adapter;
defaultRealm = adapter.getRealm(Realm.DEFAULT_REALM);
}
@POST
@Consumes("application/json")
public Response importDomain(RealmRepresentation rep)
{
RealmModel realm = createRealm(rep);
UriBuilder builder = uriInfo.getRequestUriBuilder().path(realm.getId());
return Response.created(builder.build())
.entity(RealmResource.realmRep(realm, uriInfo))
.type(MediaType.APPLICATION_JSON_TYPE).build();
}
protected RealmModel createRealm(RealmRepresentation rep)
{
User realmCreator = authenticationManager.authenticateToken(defaultRealm, headers);
Role creatorRole = defaultRealm.getIdm().getRole(RegistrationService.REALM_CREATOR_ROLE);
if (!defaultRealm.getIdm().hasRole(realmCreator, creatorRole))
{
logger.warn("not a realm creator");
throw new NotAuthorizedException("Bearer");
}
verifyRealmRepresentation(rep);
RealmModel realm = adapter.create(rep.getRealm());
realm.addRealmAdmin(realmCreator);
KeyPair keyPair = null;
try
{
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException(e);
}
realm.setPrivateKey(keyPair.getPrivate());
realm.setPublicKey(keyPair.getPublic());
realm.setName(rep.getRealm());
realm.setEnabled(rep.isEnabled());
realm.setTokenLifespan(rep.getTokenLifespan());
realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
realm.setSslNotRequired(rep.isSslNotRequired());
realm.setCookieLoginAllowed(rep.isCookieLoginAllowed());
realm.updateRealm();
Map<String, User> userMap = new HashMap<String, User>();
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials())
{
RequiredCredentialModel credential = new RequiredCredentialModel();
credential.setType(requiredCred.getType());
credential.setInput(requiredCred.isInput());
credential.setSecret(requiredCred.isSecret());
realm.addRequiredCredential(credential);
}
for (UserRepresentation userRep : rep.getUsers())
{
User user = new SimpleUser(userRep.getUsername());
user.setEnabled(userRep.isEnabled());
if (userRep.getAttributes() != null)
{
for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet())
{
user.setAttribute(new Attribute<String>(entry.getKey(), entry.getValue()));
}
}
realm.getIdm().add(user);
if (userRep.getCredentials() != null)
{
for (UserRepresentation.Credential cred : userRep.getCredentials())
{
UserCredentialModel credential = new UserCredentialModel();
credential.setType(cred.getType());
credential.setValue(cred.getValue());
realm.updateCredential(user, credential);
}
}
userMap.put(user.getLoginName(), user);
}
Map<String, Role> roles = new HashMap<String, Role>();
if (rep.getRoles() != null)
{
for (String roleString : rep.getRoles())
{
SimpleRole role = new SimpleRole(roleString.trim());
realm.getIdm().add(role);
roles.put(role.getName(), role);
}
}
if (rep.getRoleMappings() != null)
{
for (RoleMappingRepresentation mapping : rep.getRoleMappings())
{
User user = userMap.get(mapping.getUsername());
for (String roleString : mapping.getRoles())
{
Role role = roles.get(roleString.trim());
if (role == null)
{
role = new SimpleRole(roleString.trim());
realm.getIdm().add(role);
roles.put(role.getName(), role);
}
realm.getIdm().grantRole(user, role);
}
}
}
if (rep.getScopeMappings() != null)
{
for (ScopeMappingRepresentation scope : rep.getScopeMappings())
{
for (String roleString : scope.getRoles())
{
Role role = roles.get(roleString.trim());
if (role == null)
{
role = new SimpleRole(roleString.trim());
realm.getIdm().add(role);
roles.put(role.getName(), role);
}
User user = userMap.get(scope.getUsername());
realm.addScope(user, role.getName());
}
}
}
if (!roles.containsKey("*"))
{
SimpleRole wildcard = new SimpleRole("*");
realm.getIdm().add(wildcard);
roles.put("*", wildcard);
}
if (rep.getResources() != null)
{
createResources(rep, realm, userMap);
}
return realm;
}
protected void createResources(RealmRepresentation rep, RealmModel realm, Map<String, User> userMap)
{
for (ResourceRepresentation resourceRep : rep.getResources())
{
ResourceModel resource = realm.addResource(resourceRep.getName());
resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
resource.updateResource();
Map<String, Role> roles = new HashMap<String, Role>();
if (resourceRep.getRoles() != null)
{
for (String roleString : rep.getRoles())
{
SimpleRole role = new SimpleRole(roleString.trim());
resource.getIdm().add(role);
roles.put(role.getName(), role);
}
}
if (resourceRep.getRoleMappings() != null)
{
for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings())
{
User user = userMap.get(mapping.getUsername());
for (String roleString : mapping.getRoles())
{
Role role = roles.get(roleString.trim());
if (role == null)
{
role = new SimpleRole(roleString.trim());
resource.getIdm().add(role);
roles.put(role.getName(), role);
}
resource.getIdm().grantRole(user, role);
}
}
}
if (resourceRep.getScopeMappings() != null)
{
for (ScopeMappingRepresentation mapping : resourceRep.getScopeMappings())
{
User user = userMap.get(mapping.getUsername());
for (String roleString : mapping.getRoles())
{
Role role = roles.get(roleString.trim());
if (role == null)
{
role = new SimpleRole(roleString.trim());
resource.getIdm().add(role);
roles.put(role.getName(), role);
}
resource.addScope(user, role.getName());
}
}
}
if (!roles.containsKey("*"))
{
SimpleRole wildcard = new SimpleRole("*");
resource.getIdm().add(wildcard);
roles.put("*", wildcard);
}
}
}
protected void verifyRealmRepresentation(RealmRepresentation rep)
{
if (rep.getRequiredCredentials() == null)
{
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity("Realm credential requirements not defined").type("text/plain").build());
}
HashMap<String, UserRepresentation> userReps = new HashMap<String, UserRepresentation>();
for (UserRepresentation userRep : rep.getUsers()) userReps.put(userRep.getUsername(), userRep);
// override enabled to false if user does not have at least all of browser or client credentials
for (UserRepresentation userRep : rep.getUsers())
{
if (userRep.getCredentials() == null)
{
userRep.setEnabled(false);
}
else
{
boolean hasBrowserCredentials = true;
for (RequiredCredentialRepresentation credential : rep.getRequiredCredentials())
{
boolean hasCredential = false;
for (UserRepresentation.Credential cred : userRep.getCredentials())
{
if (cred.getType().equals(credential.getType()))
{
hasCredential = true;
break;
}
}
if (!hasCredential)
{
hasBrowserCredentials = false;
break;
}
}
if (!hasBrowserCredentials)
{
userRep.setEnabled(false);
}
}
}
if (rep.getResources() != null)
{
// check mappings
for (ResourceRepresentation resourceRep : rep.getResources())
{
if (resourceRep.getRoleMappings() != null)
{
for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings())
{
if (!userReps.containsKey(mapping.getUsername()))
{
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity("No users declared for role mapping").type("text/plain").build());
}
}
}
}
}
}
}

View file

@ -1,122 +0,0 @@
package org.keycloak.services.resources;
import org.keycloak.services.models.RealmManager;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.PublishedRealmRepresentation;
import org.keycloak.services.models.RealmModel;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Path("/realms")
public class RealmResource
{
protected Logger logger = Logger.getLogger(RealmResource.class);
protected RealmManager adapter;
@Context
protected UriInfo uriInfo;
public RealmResource(RealmManager adapter)
{
this.adapter = adapter;
}
@GET
@Path("{realm}")
@Produces("application/json")
public PublishedRealmRepresentation getRealm(@PathParam("realm") String id)
{
RealmModel realm = adapter.getRealm(id);
if (realm == null)
{
logger.debug("realm not found");
throw new NotFoundException();
}
return realmRep(realm, uriInfo);
}
@GET
@Path("{realm}.html")
@Produces("text/html")
public String getRealmHtml(@PathParam("realm") String id)
{
RealmModel realm = adapter.getRealm(id);
if (realm == null)
{
logger.debug("realm not found");
throw new NotFoundException();
}
return realmHtml(realm);
}
private String realmHtml(RealmModel realm)
{
StringBuffer html = new StringBuffer();
UriBuilder auth = uriInfo.getBaseUriBuilder();
auth.path(TokenService.class)
.path(TokenService.class, "requestAccessCode");
String authUri = auth.build(realm.getId()).toString();
UriBuilder code = uriInfo.getBaseUriBuilder();
code.path(TokenService.class).path(TokenService.class, "accessRequest");
String codeUri = code.build(realm.getId()).toString();
UriBuilder grant = uriInfo.getBaseUriBuilder();
grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant");
String grantUrl = grant.build(realm.getId()).toString();
UriBuilder idGrant = uriInfo.getBaseUriBuilder();
grant.path(TokenService.class).path(TokenService.class, "identityTokenGrant");
String idGrantUrl = idGrant.build(realm.getId()).toString();
html.append("<html><body><h1>Realm: ").append(realm.getName()).append("</h1>");
html.append("<p>auth: ").append(authUri).append("</p>");
html.append("<p>code: ").append(codeUri).append("</p>");
html.append("<p>grant: ").append(grantUrl).append("</p>");
html.append("<p>identity grant: ").append(idGrantUrl).append("</p>");
html.append("<p>public key: ").append(realm.getPublicKeyPem()).append("</p>");
html.append("</body></html>");
return html.toString();
}
public static PublishedRealmRepresentation realmRep(RealmModel realm, UriInfo uriInfo)
{
PublishedRealmRepresentation rep = new PublishedRealmRepresentation();
rep.setRealm(realm.getName());
rep.setSelf(uriInfo.getRequestUri().toString());
rep.setPublicKeyPem(realm.getPublicKeyPem());
UriBuilder auth = uriInfo.getBaseUriBuilder();
auth.path(TokenService.class)
.path(TokenService.class, "requestAccessCode");
rep.setAuthorizationUrl(auth.build(realm.getId()).toString());
UriBuilder code = uriInfo.getBaseUriBuilder();
code.path(TokenService.class).path(TokenService.class, "accessRequest");
rep.setCodeUrl(code.build(realm.getId()).toString());
UriBuilder grant = uriInfo.getBaseUriBuilder();
grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant");
String grantUrl = grant.build(realm.getId()).toString();
rep.setGrantUrl(grantUrl);
UriBuilder idGrant = uriInfo.getBaseUriBuilder();
grant.path(TokenService.class).path(TokenService.class, "identityTokenGrant");
String idGrantUrl = idGrant.build(realm.getId()).toString();
rep.setIdentityGrantUrl(idGrantUrl);
return rep;
}
}

View file

@ -0,0 +1,105 @@
package org.keycloak.services.resources;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.PublishedRealmRepresentation;
import org.keycloak.services.models.RealmModel;
import org.picketlink.idm.IdentitySession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmSubResource {
protected static final Logger logger = Logger.getLogger(RealmSubResource.class);
@Context
protected UriInfo uriInfo;
@Context
protected IdentitySession IdentitySession;
protected RealmModel realm;
public RealmSubResource(RealmModel realm) {
this.realm = realm;
}
@GET
@Path("json")
@Produces("application/json")
public PublishedRealmRepresentation getRealm(@PathParam("realm") String id) {
return realmRep(realm, uriInfo);
}
@GET
@Path("html")
@Produces("text/html")
public String getRealmHtml(@PathParam("realm") String id) {
StringBuffer html = new StringBuffer();
UriBuilder auth = uriInfo.getBaseUriBuilder();
auth.path(TokenService.class)
.path(TokenService.class, "requestAccessCode");
String authUri = auth.build(realm.getId()).toString();
UriBuilder code = uriInfo.getBaseUriBuilder();
code.path(TokenService.class).path(TokenService.class, "accessRequest");
String codeUri = code.build(realm.getId()).toString();
UriBuilder grant = uriInfo.getBaseUriBuilder();
grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant");
String grantUrl = grant.build(realm.getId()).toString();
UriBuilder idGrant = uriInfo.getBaseUriBuilder();
grant.path(TokenService.class).path(TokenService.class, "identityTokenGrant");
String idGrantUrl = idGrant.build(realm.getId()).toString();
html.append("<html><body><h1>Realm: ").append(realm.getName()).append("</h1>");
html.append("<p>auth: ").append(authUri).append("</p>");
html.append("<p>code: ").append(codeUri).append("</p>");
html.append("<p>grant: ").append(grantUrl).append("</p>");
html.append("<p>identity grant: ").append(idGrantUrl).append("</p>");
html.append("<p>public key: ").append(realm.getPublicKeyPem()).append("</p>");
html.append("</body></html>");
return html.toString();
}
public static PublishedRealmRepresentation realmRep(RealmModel realm, UriInfo uriInfo) {
PublishedRealmRepresentation rep = new PublishedRealmRepresentation();
rep.setRealm(realm.getName());
rep.setSelf(uriInfo.getRequestUri().toString());
rep.setPublicKeyPem(realm.getPublicKeyPem());
UriBuilder auth = uriInfo.getBaseUriBuilder();
auth.path(RealmsResource.class).path(RealmsResource.class, "getTokenService")
.path(TokenService.class, "requestAccessCode");
rep.setAuthorizationUrl(auth.build(realm.getId()).toString());
UriBuilder code = uriInfo.getBaseUriBuilder();
code.path(RealmsResource.class).path(RealmsResource.class, "getTokenService").path(TokenService.class, "accessRequest");
rep.setCodeUrl(code.build(realm.getId()).toString());
UriBuilder grant = uriInfo.getBaseUriBuilder();
grant.path(RealmsResource.class).path(RealmsResource.class, "getTokenService").path(TokenService.class, "accessTokenGrant");
String grantUrl = grant.build(realm.getId()).toString();
rep.setGrantUrl(grantUrl);
UriBuilder idGrant = uriInfo.getBaseUriBuilder();
grant.path(RealmsResource.class).path(RealmsResource.class, "getTokenService").path(TokenService.class, "identityTokenGrant");
String idGrantUrl = idGrant.build(realm.getId()).toString();
rep.setIdentityGrantUrl(idGrantUrl);
return rep;
}
}

View file

@ -0,0 +1,323 @@
package org.keycloak.services.resources;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.RoleMappingRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.models.RealmManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.UserCredentialModel;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Role;
import org.picketlink.idm.model.SimpleRole;
import org.picketlink.idm.model.SimpleUser;
import org.picketlink.idm.model.User;
import javax.ws.rs.Consumes;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Path("/realms")
public class RealmsResource {
protected static Logger logger = Logger.getLogger(RealmsResource.class);
@Context
protected UriInfo uriInfo;
@Context
protected HttpHeaders headers;
@Context
protected
IdentitySession IdentitySession;
@Context
ResourceContext resourceContext;
protected Map<String, AccessCodeEntry> accessCodes = new ConcurrentHashMap<String, AccessCodeEntry>();
@Path("{realm}/tokens")
public TokenService getTokenService(@PathParam("realm") String id) {
RealmManager realmManager = new RealmManager(IdentitySession);
RealmModel realm = realmManager.getRealm(id);
if (realm == null) {
logger.debug("realm not found");
throw new NotFoundException();
}
TokenService tokenService = new TokenService(realm, accessCodes);
resourceContext.initResource(tokenService);
return tokenService;
}
@Path("{realm}")
public RealmSubResource getRealmResource(@PathParam("realm") String id) {
RealmManager realmManager = new RealmManager(IdentitySession);
RealmModel realm = realmManager.getRealm(id);
if (realm == null) {
logger.debug("realm not found");
throw new NotFoundException();
}
RealmSubResource realmResource = new RealmSubResource(realm);
resourceContext.initResource(realmResource);
return realmResource;
}
@POST
@Consumes("application/json")
public Response importRealm(RealmRepresentation rep) {
IdentitySession.getTransaction().begin();
RealmModel realm;
try {
realm = createRealm(rep);
IdentitySession.getTransaction().commit();
} catch (RuntimeException re) {
IdentitySession.getTransaction().rollback();
throw re;
}
UriBuilder builder = uriInfo.getRequestUriBuilder().path(realm.getId());
return Response.created(builder.build())
.entity(RealmSubResource.realmRep(realm, uriInfo))
.type(MediaType.APPLICATION_JSON_TYPE).build();
}
protected RealmModel createRealm(RealmRepresentation rep) {
RealmManager realmManager = new RealmManager(IdentitySession);
RealmModel defaultRealm = realmManager.getRealm(Realm.DEFAULT_REALM);
User realmCreator = new AuthenticationManager().authenticateToken(defaultRealm, headers);
Role creatorRole = defaultRealm.getIdm().getRole(RegistrationService.REALM_CREATOR_ROLE);
if (!defaultRealm.getIdm().hasRole(realmCreator, creatorRole)) {
logger.warn("not a realm creator");
throw new NotAuthorizedException("Bearer");
}
verifyRealmRepresentation(rep);
RealmModel realm = realmManager.createRealm(rep.getRealm());
realmManager.generateRealmKeys(realm);
realm.addRealmAdmin(realmCreator);
realm.setName(rep.getRealm());
realm.setEnabled(rep.isEnabled());
realm.setTokenLifespan(rep.getTokenLifespan());
realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
realm.setSslNotRequired(rep.isSslNotRequired());
realm.setCookieLoginAllowed(rep.isCookieLoginAllowed());
realm.updateRealm();
Map<String, User> userMap = new HashMap<String, User>();
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) {
RequiredCredentialModel credential = new RequiredCredentialModel();
credential.setType(requiredCred.getType());
credential.setInput(requiredCred.isInput());
credential.setSecret(requiredCred.isSecret());
realm.addRequiredCredential(credential);
}
for (UserRepresentation userRep : rep.getUsers()) {
User user = new SimpleUser(userRep.getUsername());
user.setEnabled(userRep.isEnabled());
if (userRep.getAttributes() != null) {
for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet()) {
user.setAttribute(new Attribute<String>(entry.getKey(), entry.getValue()));
}
}
realm.getIdm().add(user);
if (userRep.getCredentials() != null) {
for (UserRepresentation.Credential cred : userRep.getCredentials()) {
UserCredentialModel credential = new UserCredentialModel();
credential.setType(cred.getType());
credential.setValue(cred.getValue());
realm.updateCredential(user, credential);
}
}
userMap.put(user.getLoginName(), user);
}
Map<String, Role> roles = new HashMap<String, Role>();
if (rep.getRoles() != null) {
for (String roleString : rep.getRoles()) {
SimpleRole role = new SimpleRole(roleString.trim());
realm.getIdm().add(role);
roles.put(role.getName(), role);
}
}
if (rep.getRoleMappings() != null) {
for (RoleMappingRepresentation mapping : rep.getRoleMappings()) {
User user = userMap.get(mapping.getUsername());
for (String roleString : mapping.getRoles()) {
Role role = roles.get(roleString.trim());
if (role == null) {
role = new SimpleRole(roleString.trim());
realm.getIdm().add(role);
roles.put(role.getName(), role);
}
realm.getIdm().grantRole(user, role);
}
}
}
if (rep.getScopeMappings() != null) {
for (ScopeMappingRepresentation scope : rep.getScopeMappings()) {
for (String roleString : scope.getRoles()) {
Role role = roles.get(roleString.trim());
if (role == null) {
role = new SimpleRole(roleString.trim());
realm.getIdm().add(role);
roles.put(role.getName(), role);
}
User user = userMap.get(scope.getUsername());
realm.addScope(user, role.getName());
}
}
}
if (!roles.containsKey("*")) {
SimpleRole wildcard = new SimpleRole("*");
realm.getIdm().add(wildcard);
roles.put("*", wildcard);
}
if (rep.getResources() != null) {
createResources(rep, realm, userMap);
}
return realm;
}
protected void createResources(RealmRepresentation rep, RealmModel realm, Map<String, User> userMap) {
for (ResourceRepresentation resourceRep : rep.getResources()) {
ResourceModel resource = realm.addResource(resourceRep.getName());
resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
resource.updateResource();
Map<String, Role> roles = new HashMap<String, Role>();
if (resourceRep.getRoles() != null) {
for (String roleString : resourceRep.getRoles()) {
SimpleRole role = new SimpleRole(roleString.trim());
resource.getIdm().add(role);
roles.put(role.getName(), role);
}
}
if (resourceRep.getRoleMappings() != null) {
for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings()) {
User user = userMap.get(mapping.getUsername());
for (String roleString : mapping.getRoles()) {
Role role = roles.get(roleString.trim());
if (role == null) {
role = new SimpleRole(roleString.trim());
resource.getIdm().add(role);
roles.put(role.getName(), role);
}
Role role1 = resource.getIdm().getRole(role.getName());
realm.getIdm().grantRole(user, role1);
}
}
}
if (resourceRep.getScopeMappings() != null) {
for (ScopeMappingRepresentation mapping : resourceRep.getScopeMappings()) {
User user = userMap.get(mapping.getUsername());
for (String roleString : mapping.getRoles()) {
Role role = roles.get(roleString.trim());
if (role == null) {
role = new SimpleRole(roleString.trim());
resource.getIdm().add(role);
roles.put(role.getName(), role);
}
resource.addScope(user, role.getName());
}
}
}
if (!roles.containsKey("*")) {
SimpleRole wildcard = new SimpleRole("*");
resource.getIdm().add(wildcard);
roles.put("*", wildcard);
}
}
}
protected void verifyRealmRepresentation(RealmRepresentation rep) {
if (rep.getRequiredCredentials() == null) {
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity("Realm credential requirements not defined").type("text/plain").build());
}
HashMap<String, UserRepresentation> userReps = new HashMap<String, UserRepresentation>();
for (UserRepresentation userRep : rep.getUsers()) userReps.put(userRep.getUsername(), userRep);
// override enabled to false if user does not have at least all of browser or client credentials
for (UserRepresentation userRep : rep.getUsers()) {
if (userRep.getCredentials() == null) {
userRep.setEnabled(false);
} else {
boolean hasBrowserCredentials = true;
for (RequiredCredentialRepresentation credential : rep.getRequiredCredentials()) {
boolean hasCredential = false;
for (UserRepresentation.Credential cred : userRep.getCredentials()) {
if (cred.getType().equals(credential.getType())) {
hasCredential = true;
break;
}
}
if (!hasCredential) {
hasBrowserCredentials = false;
break;
}
}
if (!hasBrowserCredentials) {
userRep.setEnabled(false);
}
}
}
if (rep.getResources() != null) {
// check mappings
for (ResourceRepresentation resourceRep : rep.getResources()) {
if (resourceRep.getRoleMappings() != null) {
for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings()) {
if (!userReps.containsKey(mapping.getUsername())) {
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
.entity("No users declared for role mapping").type("text/plain").build());
}
}
}
}
}
}
}

View file

@ -1,15 +1,17 @@
package org.keycloak.services.resources; package org.keycloak.services.resources;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmManager;
import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.UserCredentialModel; import org.keycloak.services.models.UserCredentialModel;
import org.picketlink.idm.model.Realm; import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.model.Role; import org.picketlink.idm.model.Role;
import org.picketlink.idm.model.SimpleUser; import org.picketlink.idm.model.SimpleUser;
import org.picketlink.idm.model.User; import org.picketlink.idm.model.User;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
@ -23,37 +25,37 @@ import java.net.URI;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@Path("/registrations") @Path("/registrations")
public class RegistrationService public class RegistrationService {
{ protected static final Logger logger = Logger.getLogger(RegistrationService.class);
public static final String REALM_CREATOR_ROLE = "realm-creator"; public static final String REALM_CREATOR_ROLE = "realm-creator";
protected RealmManager adapter;
protected RealmModel defaultRealm;
@Context @Context
protected UriInfo uriInfo; protected UriInfo uriInfo;
public RegistrationService(RealmManager adapter) @Context
{ protected IdentitySession identitySession;
this.adapter = adapter;
defaultRealm = adapter.getRealm(Realm.DEFAULT_REALM);
}
@POST @POST
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Response register(UserRepresentation newUser) public Response register(UserRepresentation newUser) {
{ identitySession.getTransaction().begin();
try {
RealmManager realmManager = new RealmManager(identitySession);
RealmModel defaultRealm = realmManager.defaultRealm();
if (!defaultRealm.isEnabled()) {
throw new ForbiddenException();
}
if (!defaultRealm.isRegistrationAllowed()) {
throw new ForbiddenException();
}
User user = defaultRealm.getIdm().getUser(newUser.getUsername()); User user = defaultRealm.getIdm().getUser(newUser.getUsername());
if (user != null) if (user != null) {
{
return Response.status(400).type("text/plain").entity("user exists").build(); return Response.status(400).type("text/plain").entity("user exists").build();
} }
user = new SimpleUser(newUser.getUsername()); user = new SimpleUser(newUser.getUsername());
defaultRealm.getIdm().add(user); defaultRealm.getIdm().add(user);
for (UserRepresentation.Credential cred : newUser.getCredentials()) for (UserRepresentation.Credential cred : newUser.getCredentials()) {
{
UserCredentialModel credModel = new UserCredentialModel(); UserCredentialModel credModel = new UserCredentialModel();
credModel.setType(cred.getType()); credModel.setType(cred.getType());
credModel.setValue(cred.getValue()); credModel.setValue(cred.getValue());
@ -61,8 +63,14 @@ public class RegistrationService
} }
Role realmCreator = defaultRealm.getIdm().getRole(REALM_CREATOR_ROLE); Role realmCreator = defaultRealm.getIdm().getRole(REALM_CREATOR_ROLE);
defaultRealm.getIdm().grantRole(user, realmCreator); defaultRealm.getIdm().grantRole(user, realmCreator);
URI uri = uriInfo.getBaseUriBuilder().path(RealmFactory.class).path(user.getLoginName()).build(); identitySession.getTransaction().commit();
URI uri = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(user.getLoginName()).build();
return Response.created(uri).build(); return Response.created(uri).build();
} catch (RuntimeException e) {
logger.error("Failed to register", e);
identitySession.getTransaction().rollback();
throw e;
}
} }

View file

@ -1,55 +0,0 @@
package org.keycloak.services.resources;
import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.keycloak.SkeletonKeyContextResolver;
import javax.ws.rs.core.Application;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SkeletonKeyApplication extends Application
{
protected Set<Object> singletons = new HashSet<Object>();
protected Set<Class<?>> classes = new HashSet<Class<?>>();
public SkeletonKeyApplication()
{
Cache cache = getCache();
singletons.add(new TokenService(null));
singletons.add(new RealmFactory(null));
singletons.add(new RealmResource(null));
classes.add(SkeletonKeyContextResolver.class);
}
@Override
public Set<Class<?>> getClasses()
{
return classes;
}
@Override
public Set<Object> getSingletons()
{
return singletons;
}
protected Cache getCache()
{
try
{
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("skeleton-key.xml");
return new DefaultCacheManager(is).getCache("skeleton-key");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}

View file

@ -9,21 +9,21 @@ import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyScope; import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken; import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.TokenManager; import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmManager;
import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel; import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ResourceModel; import org.keycloak.services.models.ResourceModel;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.model.User; import org.picketlink.idm.model.User;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
@ -41,69 +41,17 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@Path("/realms") public class TokenService {
public class TokenService
{
public static class AccessCode
{
protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
protected long expiration;
protected SkeletonKeyToken token;
protected User client;
public boolean isExpired()
{
return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration;
}
public String getId() protected static final Logger logger = Logger.getLogger(TokenService.class);
{ protected Map<String, AccessCodeEntry> accessCodeMap;
return id;
}
public long getExpiration()
{
return expiration;
}
public void setExpiration(long expiration)
{
this.expiration = expiration;
}
public SkeletonKeyToken getToken()
{
return token;
}
public void setToken(SkeletonKeyToken token)
{
this.token = token;
}
public User getClient()
{
return client;
}
public void setClient(User client)
{
this.client = client;
}
}
protected RealmManager adapter;
protected TokenManager tokenManager;
protected AuthenticationManager authManager;
protected Logger logger = Logger.getLogger(TokenService.class);
protected Map<String, AccessCode> accessCodeMap = new HashMap<String, AccessCode>();
@Context @Context
protected UriInfo uriInfo; protected UriInfo uriInfo;
@Context @Context
@ -112,86 +60,68 @@ public class TokenService
protected SecurityContext securityContext; protected SecurityContext securityContext;
@Context @Context
protected HttpHeaders headers; protected HttpHeaders headers;
@Context
protected
IdentitySession IdentitySession;
private static AtomicLong counter = new AtomicLong(1); protected RealmModel realm;
private static String generateId() protected TokenManager tokenManager = new TokenManager();
{ protected AuthenticationManager authManager = new AuthenticationManager();
return counter.getAndIncrement() + "." + UUID.randomUUID().toString();
public TokenService(RealmModel realm, Map<String, AccessCodeEntry> accessCodeMap) {
this.realm = realm;
this.accessCodeMap = accessCodeMap;
} }
public TokenService(RealmManager adapter) @Path("grants/identity-token")
{
this.adapter = adapter;
this.tokenManager = new TokenManager(adapter);
this.authManager = new AuthenticationManager(adapter);
}
@Path("{realm}/grants/identity-token")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response identityTokenGrant(@PathParam("realm") String realmId, MultivaluedMap<String, String> form) public Response identityTokenGrant(MultivaluedMap<String, String> form) {
{
String username = form.getFirst(AuthenticationManager.FORM_USERNAME); String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
if (username == null) if (username == null) {
{
throw new NotAuthorizedException("No user"); throw new NotAuthorizedException("No user");
} }
RealmModel realm = adapter.getRealm(realmId); if (!realm.isEnabled()) {
if (realm == null)
{
throw new NotFoundException("Realm not found");
}
if (!realm.isEnabled())
{
throw new NotAuthorizedException("Disabled realm"); throw new NotAuthorizedException("Disabled realm");
} }
User user = realm.getIdm().getUser(username); User user = realm.getIdm().getUser(username);
if (user == null) if (user == null) {
{
throw new NotAuthorizedException("No user"); throw new NotAuthorizedException("No user");
} }
if (!user.isEnabled()) if (!user.isEnabled()) {
{
throw new NotAuthorizedException("Disabled user."); throw new NotAuthorizedException("Disabled user.");
} }
if (!authManager.authenticateForm(realm, user, form)) {
throw new NotAuthorizedException("FORM");
}
tokenManager = new TokenManager();
SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username); SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username);
String encoded = tokenManager.encodeToken(realm, token); String encoded = tokenManager.encodeToken(realm, token);
AccessTokenResponse res = accessTokenResponse(token, encoded); AccessTokenResponse res = accessTokenResponse(token, encoded);
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
} }
@Path("{realm}/grants/access") @Path("grants/access")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response accessTokenGrant(@PathParam("realm") String realmId, MultivaluedMap<String, String> form) public Response accessTokenGrant(MultivaluedMap<String, String> form) {
{
String username = form.getFirst(AuthenticationManager.FORM_USERNAME); String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
if (username == null) if (username == null) {
{
throw new NotAuthorizedException("No user"); throw new NotAuthorizedException("No user");
} }
RealmModel realm = adapter.getRealm(realmId); if (!realm.isEnabled()) {
if (realm == null)
{
throw new NotFoundException("Realm not found");
}
if (!realm.isEnabled())
{
throw new NotAuthorizedException("Disabled realm"); throw new NotAuthorizedException("Disabled realm");
} }
User user = realm.getIdm().getUser(username); User user = realm.getIdm().getUser(username);
if (user == null) if (user == null) {
{
throw new NotAuthorizedException("No user"); throw new NotAuthorizedException("No user");
} }
if (!user.isEnabled()) if (!user.isEnabled()) {
{
throw new NotAuthorizedException("Disabled user."); throw new NotAuthorizedException("Disabled user.");
} }
if (authManager.authenticateForm(realm, user, form)) if (authManager.authenticateForm(realm, user, form)) {
{
throw new NotAuthorizedException("Auth failed"); throw new NotAuthorizedException("Auth failed");
} }
SkeletonKeyToken token = tokenManager.createAccessToken(realm, user); SkeletonKeyToken token = tokenManager.createAccessToken(realm, user);
@ -200,69 +130,54 @@ public class TokenService
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
} }
@Path("{realm}/auth/request/login") @Path("auth/request/login")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response login(@PathParam("realm") String realmId, public Response login(MultivaluedMap<String, String> formData) {
MultivaluedMap<String, String> formData)
{
String clientId = formData.getFirst("client_id"); String clientId = formData.getFirst("client_id");
String scopeParam = formData.getFirst("scope"); String scopeParam = formData.getFirst("scope");
String state = formData.getFirst("state"); String state = formData.getFirst("state");
String redirect = formData.getFirst("redirect_uri"); String redirect = formData.getFirst("redirect_uri");
RealmModel realm = adapter.getRealm(realmId); if (!realm.isEnabled()) {
if (realm == null)
{
throw new NotFoundException("Realm not found");
}
if (!realm.isEnabled())
{
return Response.ok("Realm not enabled").type("text/html").build(); return Response.ok("Realm not enabled").type("text/html").build();
} }
User client = realm.getIdm().getUser(clientId); User client = realm.getIdm().getUser(clientId);
if (client == null) if (client == null) {
{
throw new NotAuthorizedException("No client"); throw new NotAuthorizedException("No client");
} }
if (!client.isEnabled()) if (!client.isEnabled()) {
{
return Response.ok("Requester not enabled").type("text/html").build(); return Response.ok("Requester not enabled").type("text/html").build();
} }
String username = formData.getFirst("username"); String username = formData.getFirst("username");
User user = realm.getIdm().getUser(username); User user = realm.getIdm().getUser(username);
if (user == null) if (user == null) {
{
logger.debug("user not found"); logger.debug("user not found");
return loginForm("Not valid user", redirect, clientId, scopeParam, state, realm, client); return loginForm("Not valid user", redirect, clientId, scopeParam, state, realm, client);
} }
if (!user.isEnabled()) if (!user.isEnabled()) {
{
return Response.ok("Your account is not enabled").type("text/html").build(); return Response.ok("Your account is not enabled").type("text/html").build();
} }
boolean authenticated = authManager.authenticateForm(realm, user, formData); boolean authenticated = authManager.authenticateForm(realm, user, formData);
if (!authenticated) return loginForm("Unable to authenticate, try again", redirect, clientId, scopeParam, state, realm, client); if (!authenticated)
return loginForm("Unable to authenticate, try again", redirect, clientId, scopeParam, state, realm, client);
SkeletonKeyToken token = null; SkeletonKeyToken token = null;
if (scopeParam != null) token = tokenManager.createScopedToken(scopeParam, realm, client, user); if (scopeParam != null) token = tokenManager.createScopedToken(scopeParam, realm, client, user);
else token = tokenManager.createLoginToken(realm, client, user); else token = tokenManager.createLoginToken(realm, client, user);
AccessCode code = new AccessCode(); AccessCodeEntry code = new AccessCodeEntry();
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
code.setToken(token); code.setToken(token);
code.setClient(client); code.setClient(client);
synchronized (accessCodeMap) synchronized (accessCodeMap) {
{
accessCodeMap.put(code.getId(), code); accessCodeMap.put(code.getId(), code);
} }
String accessCode = null; String accessCode = null;
try try {
{
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey()); accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
} } catch (UnsupportedEncodingException e) {
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode); UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode);
@ -270,25 +185,16 @@ public class TokenService
return Response.status(302).location(redirectUri.build()).build(); return Response.status(302).location(redirectUri.build()).build();
} }
@Path("{realm}/access/codes") @Path("access/codes")
@POST @POST
@Produces("application/json") @Produces("application/json")
public Response accessRequest(@PathParam("realm") String realmId, public Response accessRequest(MultivaluedMap<String, String> formData) {
MultivaluedMap<String, String> formData) if (!realm.isEnabled()) {
{
RealmModel realm = adapter.getRealm(realmId);
if (realm == null)
{
throw new NotFoundException("Realm not found");
}
if (!realm.isEnabled())
{
throw new NotAuthorizedException("Realm not enabled"); throw new NotAuthorizedException("Realm not enabled");
} }
String code = formData.getFirst("code"); String code = formData.getFirst("code");
if (code == null) if (code == null) {
{
logger.debug("code not specified"); logger.debug("code not specified");
Map<String, String> error = new HashMap<String, String>(); Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_request"); error.put("error", "invalid_request");
@ -297,8 +203,7 @@ public class TokenService
} }
String client_id = formData.getFirst("client_id"); String client_id = formData.getFirst("client_id");
if (client_id == null) if (client_id == null) {
{
logger.debug("client_id not specified"); logger.debug("client_id not specified");
Map<String, String> error = new HashMap<String, String>(); Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_request"); error.put("error", "invalid_request");
@ -306,8 +211,7 @@ public class TokenService
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
} }
User client = realm.getIdm().getUser(client_id); User client = realm.getIdm().getUser(client_id);
if (client == null) if (client == null) {
{
logger.debug("Could not find user"); logger.debug("Could not find user");
Map<String, String> error = new HashMap<String, String>(); Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_client"); error.put("error", "invalid_client");
@ -315,8 +219,7 @@ public class TokenService
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
} }
if (!client.isEnabled()) if (!client.isEnabled()) {
{
logger.debug("user is not enabled"); logger.debug("user is not enabled");
Map<String, String> error = new HashMap<String, String>(); Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_client"); error.put("error", "invalid_client");
@ -325,61 +228,50 @@ public class TokenService
} }
boolean authenticated = authManager.authenticateForm(realm, client, formData); boolean authenticated = authManager.authenticateForm(realm, client, formData);
if (!authenticated) if (!authenticated) {
{
Map<String, String> error = new HashMap<String, String>(); Map<String, String> error = new HashMap<String, String>();
error.put("error", "unauthorized_client"); error.put("error", "unauthorized_client");
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
} }
JWSInput input = new JWSInput(code, providers); JWSInput input = new JWSInput(code, providers);
boolean verifiedCode = false; boolean verifiedCode = false;
try try {
{
verifiedCode = RSAProvider.verify(input, realm.getPublicKey()); verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
} } catch (Exception ignored) {
catch (Exception ignored)
{
logger.debug("Failed to verify signature", ignored); logger.debug("Failed to verify signature", ignored);
} }
if (!verifiedCode) if (!verifiedCode) {
{
Map<String, String> res = new HashMap<String, String>(); Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant"); res.put("error", "invalid_grant");
res.put("error_description", "Unable to verify code signature"); res.put("error_description", "Unable to verify code signature");
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
} }
String key = input.readContent(String.class); String key = input.readContent(String.class);
AccessCode accessCode = null; AccessCodeEntry accessCode = null;
synchronized (accessCodeMap) synchronized (accessCodeMap) {
{
accessCode = accessCodeMap.remove(key); accessCode = accessCodeMap.remove(key);
} }
if (accessCode == null) if (accessCode == null) {
{
Map<String, String> res = new HashMap<String, String>(); Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant"); res.put("error", "invalid_grant");
res.put("error_description", "Code not found"); res.put("error_description", "Code not found");
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
} }
if (accessCode.isExpired()) if (accessCode.isExpired()) {
{
Map<String, String> res = new HashMap<String, String>(); Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant"); res.put("error", "invalid_grant");
res.put("error_description", "Code is expired"); res.put("error_description", "Code is expired");
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
} }
if (!accessCode.getToken().isActive()) if (!accessCode.getToken().isActive()) {
{
Map<String, String> res = new HashMap<String, String>(); Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant"); res.put("error", "invalid_grant");
res.put("error_description", "Token expired"); res.put("error_description", "Token expired");
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
} }
if (!client.getId().equals(accessCode.getClient().getId())) if (!client.getId().equals(accessCode.getClient().getId())) {
{
Map<String, String> res = new HashMap<String, String>(); Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant"); res.put("error", "invalid_grant");
res.put("error_description", "Auth error"); res.put("error_description", "Auth error");
@ -390,15 +282,11 @@ public class TokenService
} }
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) {
{
byte[] tokenBytes = null; byte[] tokenBytes = null;
try try {
{
tokenBytes = JsonSerialization.toByteArray(token, false); tokenBytes = JsonSerialization.toByteArray(token, false);
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String encodedToken = new JWSBuilder() String encodedToken = new JWSBuilder()
@ -408,35 +296,25 @@ public class TokenService
return accessTokenResponse(token, encodedToken); return accessTokenResponse(token, encodedToken);
} }
protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken) protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken) {
{
AccessTokenResponse res = new AccessTokenResponse(); AccessTokenResponse res = new AccessTokenResponse();
res.setToken(encodedToken); res.setToken(encodedToken);
res.setTokenType("bearer"); res.setTokenType("bearer");
if (token.getExpiration() != 0) if (token.getExpiration() != 0) {
{
long time = token.getExpiration() - (System.currentTimeMillis() / 1000); long time = token.getExpiration() - (System.currentTimeMillis() / 1000);
res.setExpiresIn(time); res.setExpiresIn(time);
} }
return res; return res;
} }
@Path("{realm}/auth/request") @Path("auth/request")
@GET @GET
public Response requestAccessCode(@PathParam("realm") String realmId, public Response requestAccessCode(@QueryParam("response_type") String responseType,
@QueryParam("response_type") String responseType,
@QueryParam("redirect_uri") String redirect, @QueryParam("redirect_uri") String redirect,
@QueryParam("client_id") String clientId, @QueryParam("client_id") String clientId,
@QueryParam("scope") String scopeParam, @QueryParam("scope") String scopeParam,
@QueryParam("state") String state) @QueryParam("state") String state) {
{ if (!realm.isEnabled()) {
RealmModel realm = adapter.getRealm(realmId);
if (realm == null)
{
throw new NotFoundException("Realm not found");
}
if (!realm.isEnabled())
{
throw new NotAuthorizedException("Realm not enabled"); throw new NotAuthorizedException("Realm not enabled");
} }
User client = realm.getIdm().getUser(clientId); User client = realm.getIdm().getUser(clientId);
@ -446,20 +324,14 @@ public class TokenService
return loginForm(null, redirect, clientId, scopeParam, state, realm, client); return loginForm(null, redirect, clientId, scopeParam, state, realm, client);
} }
private Response loginForm(String validationError, String redirect, String clientId, String scopeParam, String state, RealmModel realm, User client) private Response loginForm(String validationError, String redirect, String clientId, String scopeParam, String state, RealmModel realm, User client) {
{
StringBuffer html = new StringBuffer(); StringBuffer html = new StringBuffer();
if (scopeParam != null) if (scopeParam != null) {
{
html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>"); html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>");
if (validationError != null) if (validationError != null) {
{ try {
try
{
Thread.sleep(1000); // put in a delay Thread.sleep(1000); // put in a delay
} } catch (InterruptedException e) {
catch (InterruptedException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
html.append("<p/><p><b>").append(validationError).append("</b></p>"); html.append("<p/><p><b>").append(validationError).append("</b></p>");
@ -469,53 +341,37 @@ public class TokenService
SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam); SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam);
Map<String, ResourceModel> resourceMap = realm.getResourceMap(); Map<String, ResourceModel> resourceMap = realm.getResourceMap();
for (String res : scope.keySet()) for (String res : scope.keySet()) {
{
ResourceModel resource = resourceMap.get(res); ResourceModel resource = resourceMap.get(res);
html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>"); html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>");
Set<String> scopeMapping = resource.getScope(client); Set<String> scopeMapping = resource.getScope(client);
for (String role : scope.get(res)) for (String role : scope.get(res)) {
{
html.append(" ").append(role); html.append(" ").append(role);
if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) {
{
return Response.ok("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build(); return Response.ok("<h1>Security Alert</h1><p>Known client not authorized for the requested scope.</p>").type("text/html").build();
} }
} }
html.append("</td></tr>"); html.append("</td></tr>");
} }
html.append("</table><p>To Authorize, please login below</p>"); html.append("</table><p>To Authorize, please login below</p>");
} } else {
else
{
Set<String> scopeMapping = realm.getScope(client); Set<String> scopeMapping = realm.getScope(client);
if (scopeMapping.contains("*")) if (scopeMapping.contains("*")) {
{
html.append("<h1>Login For ").append(realm.getName()).append(" Realm</h1>"); html.append("<h1>Login For ").append(realm.getName()).append(" Realm</h1>");
if (validationError != null) if (validationError != null) {
{ try {
try
{
Thread.sleep(1000); // put in a delay Thread.sleep(1000); // put in a delay
} } catch (InterruptedException e) {
catch (InterruptedException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
html.append("<p/><p><b>").append(validationError).append("</b></p>"); html.append("<p/><p><b>").append(validationError).append("</b></p>");
} }
} } else {
else
{
html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>"); html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>");
if (validationError != null) if (validationError != null) {
{ try {
try
{
Thread.sleep(1000); // put in a delay Thread.sleep(1000); // put in a delay
} } catch (InterruptedException e) {
catch (InterruptedException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
html.append("<p/><p><b>").append(validationError).append("</b></p>"); html.append("<p/><p><b>").append(validationError).append("</b></p>");
@ -523,37 +379,30 @@ public class TokenService
SkeletonKeyScope scope = new SkeletonKeyScope(); SkeletonKeyScope scope = new SkeletonKeyScope();
List<ResourceModel> resources = realm.getResources(); List<ResourceModel> resources = realm.getResources();
boolean found = false; boolean found = false;
for (ResourceModel resource : resources) for (ResourceModel resource : resources) {
{
Set<String> resourceScope = resource.getScope(client); Set<String> resourceScope = resource.getScope(client);
if (resourceScope == null) continue; if (resourceScope == null) continue;
if (resourceScope.size() == 0) continue; if (resourceScope.size() == 0) continue;
if (!found) if (!found) {
{
found = true; found = true;
html.append("<p>A Third Party is requesting access to the following resources</p>"); html.append("<p>A Third Party is requesting access to the following resources</p>");
html.append("<table>"); html.append("<table>");
} }
html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>"); html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>");
// todo add description of role // todo add description of role
for (String role : resourceScope) for (String role : resourceScope) {
{
html.append(" ").append(role); html.append(" ").append(role);
scope.add(resource.getName(), role); scope.add(resource.getName(), role);
} }
} }
if (!found) if (!found) {
{
return Response.ok("<h1>Security Alert</h1><p>Known client not authorized to access this realm.</p>").type("text/html").build(); return Response.ok("<h1>Security Alert</h1><p>Known client not authorized to access this realm.</p>").type("text/html").build();
} }
html.append("</table>"); html.append("</table>");
try try {
{
String json = JsonSerialization.toString(scope, false); String json = JsonSerialization.toString(scope, false);
scopeParam = Base64Url.encode(json.getBytes("UTF-8")); scopeParam = Base64Url.encode(json.getBytes("UTF-8"));
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -565,22 +414,18 @@ public class TokenService
html.append("<form action=\"").append(action).append("\" method=\"POST\">"); html.append("<form action=\"").append(action).append("\" method=\"POST\">");
html.append("Username: <input type=\"text\" name=\"username\" size=\"20\"><br>"); html.append("Username: <input type=\"text\" name=\"username\" size=\"20\"><br>");
for (RequiredCredentialModel credential : realm.getRequiredCredentials()) for (RequiredCredentialModel credential : realm.getRequiredCredentials()) {
{
if (!credential.isInput()) continue; if (!credential.isInput()) continue;
html.append(credential.getType()).append(": "); html.append(credential.getType()).append(": ");
if (credential.isSecret()) if (credential.isSecret()) {
{
html.append("<input type=\"password\" name=\"").append(credential.getType()).append("\" size=\"20\"><br>"); html.append("<input type=\"password\" name=\"").append(credential.getType()).append("\" size=\"20\"><br>");
} else } else {
{
html.append("<input type=\"text\" name=\"").append(credential.getType()).append("\" size=\"20\"><br>"); html.append("<input type=\"text\" name=\"").append(credential.getType()).append("\" size=\"20\"><br>");
} }
} }
html.append("<input type=\"hidden\" name=\"client_id\" value=\"").append(clientId).append("\">"); html.append("<input type=\"hidden\" name=\"client_id\" value=\"").append(clientId).append("\">");
if (scopeParam != null) if (scopeParam != null) {
{
html.append("<input type=\"hidden\" name=\"scope\" value=\"").append(scopeParam).append("\">"); html.append("<input type=\"hidden\" name=\"scope\" value=\"").append(scopeParam).append("\">");
} }
if (state != null) html.append("<input type=\"hidden\" name=\"state\" value=\"").append(state).append("\">"); if (state != null) html.append("<input type=\"hidden\" name=\"state\" value=\"").append(state).append("\">");

View file

@ -7,27 +7,23 @@ import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.keycloak.representations.idm.RequiredCredentialRepresentation; import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.services.managers.InstallationManager;
import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmManager;
import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.relationships.RealmResourceRelationship;
import org.keycloak.services.models.RequiredCredentialModel; import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.relationships.RequiredCredentialRelationship;
import org.keycloak.services.models.relationships.ScopeRelationship;
import org.keycloak.services.models.UserCredentialModel; import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.resources.KeycloakApplication;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.IdentitySessionFactory;
import org.picketlink.idm.IdentityManager; import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.config.IdentityConfigurationBuilder;
import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.Credentials;
import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.UsernamePasswordCredentials; import org.picketlink.idm.credential.UsernamePasswordCredentials;
import org.picketlink.idm.file.internal.FileUtils;
import org.picketlink.idm.internal.IdentityManagerFactory;
import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Role; import org.picketlink.idm.model.Role;
import org.picketlink.idm.model.SimpleRole; import org.picketlink.idm.model.SimpleRole;
import org.picketlink.idm.model.SimpleUser; import org.picketlink.idm.model.SimpleUser;
import org.picketlink.idm.model.User; import org.picketlink.idm.model.User;
import java.io.File;
import java.util.List; import java.util.List;
/** /**
@ -35,47 +31,34 @@ import java.util.List;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class AdapterTest public class AdapterTest {
{ private IdentitySessionFactory factory;
private static IdentityManagerFactory factory; private IdentitySession IdentitySession;
public static final String WORKING_DIRECTORY = "/tmp/keycloak"; private RealmManager adapter;
public RealmManager adapter; private RealmModel realmModel;
public RealmModel realmModel;
@Before @Before
public void before() throws Exception public void before() throws Exception {
{ factory = KeycloakApplication.createFactory();
after(); IdentitySession = factory.createIdentitySession();
factory = createFactory(); adapter = new RealmManager(IdentitySession);
adapter = new RealmManager(factory);
}
private static IdentityManagerFactory createFactory() {
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
builder
.stores()
.file()
.addRealm(Realm.DEFAULT_REALM)
.workingDirectory(WORKING_DIRECTORY)
.preserveState(true)
.supportAllFeatures()
.supportRelationshipType(RealmResourceRelationship.class, RequiredCredentialRelationship.class, ScopeRelationship.class);
return new IdentityManagerFactory(builder.build());
} }
@After @After
public void after() throws Exception public void after() throws Exception {
{ IdentitySession.close();
File file = new File(WORKING_DIRECTORY); factory.close();
FileUtils.delete(file);
Thread.sleep(10); // my windows machine seems to have delays on deleting files sometimes
} }
@Test @Test
public void test1CreateRealm() throws Exception public void installTest() throws Exception {
{ new InstallationManager().install(adapter);
realmModel = adapter.create("JUGGLER");
}
@Test
public void test1CreateRealm() throws Exception {
realmModel = adapter.createRealm("JUGGLER");
realmModel.setAccessCodeLifespan(100); realmModel.setAccessCodeLifespan(100);
realmModel.setCookieLoginAllowed(true); realmModel.setCookieLoginAllowed(true);
realmModel.setEnabled(true); realmModel.setEnabled(true);
@ -97,8 +80,7 @@ public class AdapterTest
} }
@Test @Test
public void test2RequiredCredential() throws Exception public void test2RequiredCredential() throws Exception {
{
test1CreateRealm(); test1CreateRealm();
RequiredCredentialModel creds = new RequiredCredentialModel(); RequiredCredentialModel creds = new RequiredCredentialModel();
creds.setSecret(true); creds.setSecret(true);
@ -114,8 +96,7 @@ public class AdapterTest
Assert.assertEquals(2, storedCreds.size()); Assert.assertEquals(2, storedCreds.size());
boolean totp = false; boolean totp = false;
boolean password = false; boolean password = false;
for (RequiredCredentialModel cred : storedCreds) for (RequiredCredentialModel cred : storedCreds) {
{
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true; if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true;
else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true; else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true;
} }
@ -124,8 +105,7 @@ public class AdapterTest
} }
@Test @Test
public void testCredentialValidation() throws Exception public void testCredentialValidation() throws Exception {
{
test1CreateRealm(); test1CreateRealm();
User user = new SimpleUser("bburke"); User user = new SimpleUser("bburke");
realmModel.getIdm().add(user); realmModel.getIdm().add(user);
@ -140,8 +120,7 @@ public class AdapterTest
} }
@Test @Test
public void testRoles() throws Exception public void testRoles() throws Exception {
{
test1CreateRealm(); test1CreateRealm();
IdentityManager idm = realmModel.getIdm(); IdentityManager idm = realmModel.getIdm();
idm.add(new SimpleRole("admin")); idm.add(new SimpleRole("admin"));

View file

@ -0,0 +1,31 @@
package org.keycloak.test;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.test.BaseResourceTest;
import org.keycloak.representations.idm.RealmRepresentation;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakTestBase extends BaseResourceTest
{
public static RealmRepresentation loadJson(String path) throws IOException
{
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ( (c = is.read()) != -1)
{
os.write(c);
}
byte[] bytes = os.toByteArray();
System.out.println(new String(bytes));
return JsonSerialization.fromBytes(RealmRepresentation.class, bytes);
}
}

View file

@ -0,0 +1,94 @@
package org.keycloak.test;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.jboss.resteasy.test.EmbeddedContainer;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.InstallationManager;
import org.keycloak.services.models.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.picketlink.idm.IdentitySession;
import org.picketlink.idm.model.Realm;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import static org.jboss.resteasy.test.TestPortProvider.generateURL;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmCreationTest {
private static ResteasyDeployment deployment;
private static Client client;
@BeforeClass
public static void before() throws Exception {
deployment = new ResteasyDeployment();
deployment.setApplicationClass(KeycloakApplication.class.getName());
EmbeddedContainer.start(deployment);
KeycloakApplication application = (KeycloakApplication) deployment.getApplication();
IdentitySession IdentitySession = application.getFactory().createIdentitySession();
RealmManager manager = new RealmManager(IdentitySession);
new InstallationManager().install(manager);
client = new ResteasyClientBuilder().build();
client.register(SkeletonKeyContextResolver.class);
}
public static void after() throws Exception {
client.close();
EmbeddedContainer.stop();
}
@Test
public void testRegisterLoginAndCreate() throws Exception {
UserRepresentation user = new UserRepresentation();
user.setUsername("bburke");
user.credential(RequiredCredentialRepresentation.PASSWORD, "geheim", false);
WebTarget target = client.target(generateURL("/"));
Response response = target.path("registrations").request().post(Entity.json(user));
Assert.assertEquals(201, response.getStatus());
response.close();
AccessTokenResponse tokenResponse = null;
try {
Form form = new Form();
form.param(AuthenticationManager.FORM_USERNAME, "bburke");
form.param(RequiredCredentialRepresentation.PASSWORD, "badpassword");
tokenResponse = target.path("realms").path(Realm.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class);
Assert.fail();
} catch (NotAuthorizedException e) {
}
Form form = new Form();
form.param(AuthenticationManager.FORM_USERNAME, "bburke");
form.param(RequiredCredentialRepresentation.PASSWORD, "geheim");
tokenResponse = target.path("realms").path(Realm.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class);
Assert.assertNotNull(tokenResponse);
System.out.println(tokenResponse.getToken());
//
RealmRepresentation realm = KeycloakTestBase.loadJson("testrealm.json");
response = target.path("realms").request().header(HttpHeaders.AUTHORIZATION, "Bearer " + tokenResponse.getToken()).post(Entity.json(realm));
Assert.assertEquals(201, response.getStatus());
response.close();
}
}

View file

@ -2,7 +2,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0"> version="1.0">
<persistence-unit name="identitydb" transaction-type="RESOURCE_LOCAL"> <persistence-unit name="keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider> <provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.picketlink.idm.jpa.schema.IdentityObject</class> <class>org.picketlink.idm.jpa.schema.IdentityObject</class>