commit
a19453c961
59 changed files with 6260 additions and 0 deletions
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Intellij
|
||||||
|
###################
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Compiled source #
|
||||||
|
###################
|
||||||
|
*.com
|
||||||
|
*.class
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Packages #
|
||||||
|
############
|
||||||
|
# it's better to unpack these files and commit the raw source
|
||||||
|
# git has its own built in compression methods
|
||||||
|
*.7z
|
||||||
|
*.dmg
|
||||||
|
*.gz
|
||||||
|
*.iso
|
||||||
|
*.jar
|
||||||
|
*.rar
|
||||||
|
*.tar
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
# Logs and databases #
|
||||||
|
######################
|
||||||
|
*.log
|
||||||
|
|
||||||
|
|
60
core/pom.xml
Executable file
60
core/pom.xml
Executable file
|
@ -0,0 +1,60 @@
|
||||||
|
<?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-core</artifactId>
|
||||||
|
<name>Keycloak Core</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<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.resteasy</groupId>
|
||||||
|
<artifactId>jose-jwt</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>tjws</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</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>
|
162
core/src/main/java/org/keycloak/AbstractOAuthClient.java
Executable file
162
core/src/main/java/org/keycloak/AbstractOAuthClient.java
Executable file
|
@ -0,0 +1,162 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
import org.jboss.resteasy.util.BasicAuthHelper;
|
||||||
|
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
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.security.KeyStore;
|
||||||
|
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 AbstractOAuthClient
|
||||||
|
{
|
||||||
|
protected String clientId;
|
||||||
|
protected String password;
|
||||||
|
protected KeyStore truststore;
|
||||||
|
protected String authUrl;
|
||||||
|
protected String codeUrl;
|
||||||
|
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||||
|
protected Client client;
|
||||||
|
protected final AtomicLong counter = new AtomicLong();
|
||||||
|
|
||||||
|
protected String getStateCode()
|
||||||
|
{
|
||||||
|
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
client = new ResteasyClientBuilder().trustStore(truststore)
|
||||||
|
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
|
||||||
|
.connectionPoolSize(10)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientId()
|
||||||
|
{
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientId(String clientId)
|
||||||
|
{
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword()
|
||||||
|
{
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password)
|
||||||
|
{
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyStore getTruststore()
|
||||||
|
{
|
||||||
|
return truststore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTruststore(KeyStore truststore)
|
||||||
|
{
|
||||||
|
this.truststore = truststore;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 String getStateCookieName()
|
||||||
|
{
|
||||||
|
return stateCookieName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStateCookieName(String stateCookieName)
|
||||||
|
{
|
||||||
|
this.stateCookieName = stateCookieName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient()
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(Client client)
|
||||||
|
{
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String resolveBearerToken(String redirectUri, String code)
|
||||||
|
{
|
||||||
|
redirectUri = stripOauthParametersFromRedirect(redirectUri);
|
||||||
|
String authHeader = BasicAuthHelper.createHeader(clientId, password);
|
||||||
|
Form codeForm = new Form()
|
||||||
|
.param("grant_type", "authorization_code")
|
||||||
|
.param("code", code)
|
||||||
|
.param("redirect_uri", redirectUri);
|
||||||
|
Response res = client.target(codeUrl).request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(codeForm));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (res.getStatus() == 400)
|
||||||
|
{
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
else if (res.getStatus() != 200)
|
||||||
|
{
|
||||||
|
throw new InternalServerErrorException(new Exception("Unknown error when getting acess token"));
|
||||||
|
}
|
||||||
|
AccessTokenResponse tokenResponse = res.readEntity(AccessTokenResponse.class);
|
||||||
|
return tokenResponse.getToken();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
res.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String stripOauthParametersFromRedirect(String uri)
|
||||||
|
{
|
||||||
|
System.out.println("******************** redirect_uri: " + uri);
|
||||||
|
UriBuilder builder = UriBuilder.fromUri(uri)
|
||||||
|
.replaceQueryParam("code", null)
|
||||||
|
.replaceQueryParam("state", null);
|
||||||
|
return builder.build().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
core/src/main/java/org/keycloak/BouncyIntegration.java
Executable file
22
core/src/main/java/org/keycloak/BouncyIntegration.java
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class BouncyIntegration
|
||||||
|
{
|
||||||
|
static
|
||||||
|
{
|
||||||
|
if (Security.getProvider("BC") == null) Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init()
|
||||||
|
{
|
||||||
|
// empty, the static class does it
|
||||||
|
}
|
||||||
|
}
|
68
core/src/main/java/org/keycloak/DerUtils.java
Executable file
68
core/src/main/java/org/keycloak/DerUtils.java
Executable file
|
@ -0,0 +1,68 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract PrivateKey, PublicKey, and X509Certificate from a DER encoded byte array or file. Usually
|
||||||
|
* generated from openssl
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class DerUtils
|
||||||
|
{
|
||||||
|
static
|
||||||
|
{
|
||||||
|
BouncyIntegration.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey decodePrivateKey(InputStream is)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
DataInputStream dis = new DataInputStream(is);
|
||||||
|
byte[] keyBytes = new byte[dis.available()];
|
||||||
|
dis.readFully(keyBytes);
|
||||||
|
dis.close();
|
||||||
|
|
||||||
|
PKCS8EncodedKeySpec spec =
|
||||||
|
new PKCS8EncodedKeySpec(keyBytes);
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
|
||||||
|
return kf.generatePrivate(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicKey decodePublicKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
|
||||||
|
{
|
||||||
|
X509EncodedKeySpec spec =
|
||||||
|
new X509EncodedKeySpec(der);
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
|
||||||
|
return kf.generatePublic(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate decodeCertificate(InputStream is) throws Exception
|
||||||
|
{
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
|
||||||
|
X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
|
||||||
|
is.close();
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey decodePrivateKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
|
||||||
|
{
|
||||||
|
PKCS8EncodedKeySpec spec =
|
||||||
|
new PKCS8EncodedKeySpec(der);
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
|
||||||
|
return kf.generatePrivate(spec);
|
||||||
|
}
|
||||||
|
}
|
36
core/src/main/java/org/keycloak/EnvUtil.java
Executable file
36
core/src/main/java/org/keycloak/EnvUtil.java
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class EnvUtil
|
||||||
|
{
|
||||||
|
private static final Pattern p = Pattern.compile("[$][{]([^}]+)[}]");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces any ${} strings with their corresponding environent variable.
|
||||||
|
*
|
||||||
|
* @param val
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String replace(String val)
|
||||||
|
{
|
||||||
|
Matcher matcher = p.matcher(val);
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
while (matcher.find())
|
||||||
|
{
|
||||||
|
String envVar = matcher.group(1);
|
||||||
|
String envVal = System.getProperty(envVar);
|
||||||
|
if (envVal == null) envVal = "NOT-SPECIFIED";
|
||||||
|
matcher.appendReplacement(buf, envVal.replace("\\", "\\\\"));
|
||||||
|
}
|
||||||
|
matcher.appendTail(buf);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
117
core/src/main/java/org/keycloak/PemUtils.java
Executable file
117
core/src/main/java/org/keycloak/PemUtils.java
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.util.Base64;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility classes to extract PublicKey, PrivateKey, and X509Certificate from openssl generated PEM files
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class PemUtils
|
||||||
|
{
|
||||||
|
static
|
||||||
|
{
|
||||||
|
BouncyIntegration.init();
|
||||||
|
}
|
||||||
|
public static X509Certificate decodeCertificate(InputStream is) throws Exception
|
||||||
|
{
|
||||||
|
byte[] der = pemToDer(is);
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(der);
|
||||||
|
return DerUtils.decodeCertificate(bis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate decodeCertificate(String cert) throws Exception
|
||||||
|
{
|
||||||
|
byte[] der = pemToDer(cert);
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(der);
|
||||||
|
return DerUtils.decodeCertificate(bis);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a public key from a PEM string
|
||||||
|
*
|
||||||
|
* @param pem
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static PublicKey decodePublicKey(String pem) throws Exception
|
||||||
|
{
|
||||||
|
byte[] der = pemToDer(pem);
|
||||||
|
return DerUtils.decodePublicKey(der);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a private key that is a PKCS8 pem string (base64 encoded PKCS8)
|
||||||
|
*
|
||||||
|
* @param pem
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static PrivateKey decodePrivateKey(String pem) throws Exception
|
||||||
|
{
|
||||||
|
byte[] der = pemToDer(pem);
|
||||||
|
return DerUtils.decodePrivateKey(der);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey decodePrivateKey(InputStream is) throws Exception
|
||||||
|
{
|
||||||
|
String pem = pemFromStream(is);
|
||||||
|
return decodePrivateKey(pem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a PEM file to DER format
|
||||||
|
*
|
||||||
|
* @param is
|
||||||
|
* @return
|
||||||
|
* @throws java.io.IOException
|
||||||
|
*/
|
||||||
|
public static byte[] pemToDer(InputStream is) throws IOException
|
||||||
|
{
|
||||||
|
String pem = pemFromStream(is);
|
||||||
|
byte[] der = pemToDer(pem);
|
||||||
|
return der;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a PEM string to DER format
|
||||||
|
*
|
||||||
|
* @param pem
|
||||||
|
* @return
|
||||||
|
* @throws java.io.IOException
|
||||||
|
*/
|
||||||
|
public static byte[] pemToDer(String pem) throws IOException
|
||||||
|
{
|
||||||
|
pem = removeBeginEnd(pem);
|
||||||
|
return Base64.decode(pem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String removeBeginEnd(String pem)
|
||||||
|
{
|
||||||
|
pem = pem.replaceAll("-----BEGIN (.*)-----", "");
|
||||||
|
pem = pem.replaceAll("-----END (.*)----", "");
|
||||||
|
pem = pem.replaceAll("\r\n", "");
|
||||||
|
pem = pem.replaceAll("\n", "");
|
||||||
|
return pem.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String pemFromStream(InputStream is) throws IOException
|
||||||
|
{
|
||||||
|
DataInputStream dis = new DataInputStream(is);
|
||||||
|
byte[] keyBytes = new byte[dis.available()];
|
||||||
|
dis.readFully(keyBytes);
|
||||||
|
dis.close();
|
||||||
|
return new String(keyBytes, "utf-8");
|
||||||
|
}
|
||||||
|
}
|
59
core/src/main/java/org/keycloak/RSATokenVerifier.java
Executable file
59
core/src/main/java/org/keycloak/RSATokenVerifier.java
Executable file
|
@ -0,0 +1,59 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSInput;
|
||||||
|
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
|
||||||
|
import org.jboss.resteasy.jwt.JsonSerialization;
|
||||||
|
import org.keycloak.representations.SkeletonKeyToken;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RSATokenVerifier
|
||||||
|
{
|
||||||
|
public static SkeletonKeyToken verifyToken(String tokenString, ResourceMetadata metadata) throws VerificationException
|
||||||
|
{
|
||||||
|
PublicKey realmKey = metadata.getRealmKey();
|
||||||
|
String realm = metadata.getRealm();
|
||||||
|
String resource = metadata.getResourceName();
|
||||||
|
JWSInput input = new JWSInput(tokenString);
|
||||||
|
boolean verified = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
verified = RSAProvider.verify(input, realmKey);
|
||||||
|
}
|
||||||
|
catch (Exception ignore)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!verified) throw new VerificationException("Token signature not validated");
|
||||||
|
|
||||||
|
SkeletonKeyToken token = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
token = JsonSerialization.fromBytes(SkeletonKeyToken.class, input.getContent());
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new VerificationException(e);
|
||||||
|
}
|
||||||
|
if (!token.isActive())
|
||||||
|
{
|
||||||
|
throw new VerificationException("Token is not active.");
|
||||||
|
}
|
||||||
|
String user = token.getPrincipal();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new VerificationException("Token user was null");
|
||||||
|
}
|
||||||
|
if (!realm.equals(token.getAudience()))
|
||||||
|
{
|
||||||
|
throw new VerificationException("Token audience doesn't match domain");
|
||||||
|
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
98
core/src/main/java/org/keycloak/RealmConfiguration.java
Executable file
98
core/src/main/java/org/keycloak/RealmConfiguration.java
Executable file
|
@ -0,0 +1,98 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Form;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RealmConfiguration
|
||||||
|
{
|
||||||
|
protected ResourceMetadata metadata;
|
||||||
|
protected ResteasyClient client;
|
||||||
|
protected UriBuilder authUrl;
|
||||||
|
protected ResteasyWebTarget codeUrl;
|
||||||
|
protected String clientId;
|
||||||
|
protected Form credentials = new Form();
|
||||||
|
protected boolean sslRequired = true;
|
||||||
|
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||||
|
|
||||||
|
public ResourceMetadata getMetadata()
|
||||||
|
{
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMetadata(ResourceMetadata metadata)
|
||||||
|
{
|
||||||
|
this.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResteasyClient getClient()
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(ResteasyClient client)
|
||||||
|
{
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UriBuilder getAuthUrl()
|
||||||
|
{
|
||||||
|
return authUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthUrl(UriBuilder authUrl)
|
||||||
|
{
|
||||||
|
this.authUrl = authUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientId()
|
||||||
|
{
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientId(String clientId)
|
||||||
|
{
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Form getCredentials()
|
||||||
|
{
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResteasyWebTarget getCodeUrl()
|
||||||
|
{
|
||||||
|
return codeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeUrl(ResteasyWebTarget codeUrl)
|
||||||
|
{
|
||||||
|
this.codeUrl = codeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslRequired()
|
||||||
|
{
|
||||||
|
return sslRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslRequired(boolean sslRequired)
|
||||||
|
{
|
||||||
|
this.sslRequired = sslRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStateCookieName()
|
||||||
|
{
|
||||||
|
return stateCookieName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStateCookieName(String stateCookieName)
|
||||||
|
{
|
||||||
|
this.stateCookieName = stateCookieName;
|
||||||
|
}
|
||||||
|
}
|
94
core/src/main/java/org/keycloak/ResourceMetadata.java
Executable file
94
core/src/main/java/org/keycloak/ResourceMetadata.java
Executable file
|
@ -0,0 +1,94 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ResourceMetadata
|
||||||
|
{
|
||||||
|
protected String resourceName;
|
||||||
|
protected String realm;
|
||||||
|
protected KeyStore clientKeystore;
|
||||||
|
protected String clientKeyPassword;
|
||||||
|
protected KeyStore truststore;
|
||||||
|
protected PublicKey realmKey;
|
||||||
|
|
||||||
|
public String getResourceName()
|
||||||
|
{
|
||||||
|
return resourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealm()
|
||||||
|
{
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keystore that contains service's private key and certificate.
|
||||||
|
* Used when making invocations on remote HTTPS endpoints that require client-cert authentication
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public KeyStore getClientKeystore()
|
||||||
|
{
|
||||||
|
return clientKeystore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientKeyPassword()
|
||||||
|
{
|
||||||
|
return clientKeyPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientKeyPassword(String clientKeyPassword)
|
||||||
|
{
|
||||||
|
this.clientKeyPassword = clientKeyPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truststore to use if this service makes client invocations on remote HTTPS endpoints.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public KeyStore getTruststore()
|
||||||
|
{
|
||||||
|
return truststore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key of the realm. Used to verify access tokens
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public PublicKey getRealmKey()
|
||||||
|
{
|
||||||
|
return realmKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceName(String resourceName)
|
||||||
|
{
|
||||||
|
this.resourceName = resourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealm(String realm)
|
||||||
|
{
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientKeystore(KeyStore clientKeystore)
|
||||||
|
{
|
||||||
|
this.clientKeystore = clientKeystore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTruststore(KeyStore truststore)
|
||||||
|
{
|
||||||
|
this.truststore = truststore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmKey(PublicKey realmKey)
|
||||||
|
{
|
||||||
|
this.realmKey = realmKey;
|
||||||
|
}
|
||||||
|
}
|
42
core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
Executable file
42
core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
Executable file
|
@ -0,0 +1,42 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.codehaus.jackson.map.SerializationConfig;
|
||||||
|
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||||
|
|
||||||
|
import javax.ws.rs.ext.ContextResolver;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any class with package org.jboss.resteasy.skeleton.key will use NON_DEFAULT inclusion
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
|
||||||
|
{
|
||||||
|
protected ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
public SkeletonKeyContextResolver()
|
||||||
|
{
|
||||||
|
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyContextResolver(boolean indent)
|
||||||
|
{
|
||||||
|
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
|
||||||
|
if (indent)
|
||||||
|
{
|
||||||
|
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectMapper getContext(Class<?> type)
|
||||||
|
{
|
||||||
|
if (type.getPackage().getName().startsWith("org.jboss.resteasy.skeleton.key")) return mapper;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
58
core/src/main/java/org/keycloak/SkeletonKeyPrincipal.java
Executable file
58
core/src/main/java/org/keycloak/SkeletonKeyPrincipal.java
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SkeletonKeyPrincipal implements Principal
|
||||||
|
{
|
||||||
|
protected String name;
|
||||||
|
protected String surrogate;
|
||||||
|
|
||||||
|
public SkeletonKeyPrincipal(String name, String surrogate)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.surrogate = surrogate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSurrogate()
|
||||||
|
{
|
||||||
|
return surrogate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
SkeletonKeyPrincipal that = (SkeletonKeyPrincipal) o;
|
||||||
|
|
||||||
|
if (!name.equals(that.name)) return false;
|
||||||
|
if (surrogate != null ? !surrogate.equals(that.surrogate) : that.surrogate != null) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
int result = name.hashCode();
|
||||||
|
result = 31 * result + (surrogate != null ? surrogate.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
34
core/src/main/java/org/keycloak/SkeletonKeySession.java
Executable file
34
core/src/main/java/org/keycloak/SkeletonKeySession.java
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SkeletonKeySession implements Serializable
|
||||||
|
{
|
||||||
|
protected String token;
|
||||||
|
protected transient ResourceMetadata metadata;
|
||||||
|
|
||||||
|
public SkeletonKeySession()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeySession(String token, ResourceMetadata metadata)
|
||||||
|
{
|
||||||
|
this.token = token;
|
||||||
|
this.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken()
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceMetadata getMetadata()
|
||||||
|
{
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
core/src/main/java/org/keycloak/VerificationException.java
Executable file
27
core/src/main/java/org/keycloak/VerificationException.java
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class VerificationException extends Exception
|
||||||
|
{
|
||||||
|
public VerificationException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerificationException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerificationException(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerificationException(Throwable cause)
|
||||||
|
{
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
129
core/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
Executable file
129
core/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
Executable file
|
@ -0,0 +1,129 @@
|
||||||
|
package org.keycloak.jaxrs;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.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.annotation.Priority;
|
||||||
|
import javax.ws.rs.Priorities;
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.SecurityContext;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Priority(Priorities.AUTHENTICATION)
|
||||||
|
public class JaxrsBearerTokenFilter implements ContainerRequestFilter
|
||||||
|
{
|
||||||
|
protected ResourceMetadata resourceMetadata;
|
||||||
|
private static Logger log = Logger.getLogger(JaxrsBearerTokenFilter.class);
|
||||||
|
|
||||||
|
public JaxrsBearerTokenFilter(ResourceMetadata resourceMetadata)
|
||||||
|
{
|
||||||
|
this.resourceMetadata = resourceMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void challengeResponse(ContainerRequestContext request, String error, String description)
|
||||||
|
{
|
||||||
|
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("\"");
|
||||||
|
}
|
||||||
|
request.abortWith(Response.status(401).header("WWW-Authenticate", header.toString()).build());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected SecurityContext securityContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext request) throws IOException
|
||||||
|
{
|
||||||
|
String authHeader = request.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||||
|
if (authHeader == null)
|
||||||
|
{
|
||||||
|
challengeResponse(request, null, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] split = authHeader.trim().split("\\s+");
|
||||||
|
if (split == null || split.length != 2) challengeResponse(request, null, null);
|
||||||
|
if (!split[0].equalsIgnoreCase("Bearer")) challengeResponse(request, null, null);
|
||||||
|
|
||||||
|
|
||||||
|
String tokenString = split[1];
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
|
||||||
|
SkeletonKeySession skSession = new SkeletonKeySession(tokenString, resourceMetadata);
|
||||||
|
ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
|
||||||
|
String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
|
||||||
|
|
||||||
|
final SkeletonKeyPrincipal principal = new SkeletonKeyPrincipal(token.getPrincipal(), callerPrincipal);
|
||||||
|
final boolean isSecure = securityContext.isSecure();
|
||||||
|
final SkeletonKeyToken.Access access;
|
||||||
|
if (resourceMetadata.getResourceName() != null)
|
||||||
|
{
|
||||||
|
access = token.getResourceAccess(resourceMetadata.getResourceName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
access = token.getRealmAccess();
|
||||||
|
}
|
||||||
|
SecurityContext ctx = new SecurityContext()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Principal getUserPrincipal()
|
||||||
|
{
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserInRole(String role)
|
||||||
|
{
|
||||||
|
if (access.getRoles() == null) return false;
|
||||||
|
return access.getRoles().contains(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecure()
|
||||||
|
{
|
||||||
|
return isSecure;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthenticationScheme()
|
||||||
|
{
|
||||||
|
return "OAUTH_BEARER";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.setSecurityContext(ctx);
|
||||||
|
}
|
||||||
|
catch (VerificationException e)
|
||||||
|
{
|
||||||
|
log.error("Failed to verify token", e);
|
||||||
|
challengeResponse(request, "invalid_token", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java
Executable file
55
core/src/main/java/org/keycloak/jaxrs/JaxrsOAuthClient.java
Executable file
|
@ -0,0 +1,55 @@
|
||||||
|
package org.keycloak.jaxrs;
|
||||||
|
|
||||||
|
import org.keycloak.AbstractOAuthClient;
|
||||||
|
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
import javax.ws.rs.core.Cookie;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.NewCookie;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper code to obtain oauth access tokens via browser redirects
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class JaxrsOAuthClient extends AbstractOAuthClient
|
||||||
|
{
|
||||||
|
public Response redirect(UriInfo uriInfo, String redirectUri)
|
||||||
|
{
|
||||||
|
String state = getStateCode();
|
||||||
|
|
||||||
|
URI url = UriBuilder.fromUri(authUrl)
|
||||||
|
.queryParam("client_id", clientId)
|
||||||
|
.queryParam("redirect_uri", redirectUri)
|
||||||
|
.queryParam("state", state)
|
||||||
|
.build();
|
||||||
|
NewCookie cookie = new NewCookie(stateCookieName, state, uriInfo.getBaseUri().getPath(), null, null, -1, true);
|
||||||
|
return Response.status(302)
|
||||||
|
.location(url)
|
||||||
|
.cookie(cookie).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBearerToken(UriInfo uriInfo, HttpHeaders headers) throws BadRequestException, InternalServerErrorException
|
||||||
|
{
|
||||||
|
String error = uriInfo.getQueryParameters().getFirst("error");
|
||||||
|
if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
|
||||||
|
Cookie stateCookie = headers.getCookies().get(stateCookieName);
|
||||||
|
if (stateCookie == null) throw new BadRequestException(new Exception("state cookie not set"));;
|
||||||
|
|
||||||
|
String state = uriInfo.getQueryParameters().getFirst("state");
|
||||||
|
if (state == null) throw new BadRequestException(new Exception("state parameter was null"));
|
||||||
|
if (!state.equals(stateCookie.getValue()))
|
||||||
|
{
|
||||||
|
throw new BadRequestException(new Exception("state parameter invalid"));
|
||||||
|
}
|
||||||
|
String code = uriInfo.getQueryParameters().getFirst("code");
|
||||||
|
if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
|
||||||
|
return resolveBearerToken(uriInfo.getRequestUri().toString(), code);
|
||||||
|
}
|
||||||
|
}
|
64
core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
Executable file
64
core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
Executable file
|
@ -0,0 +1,64 @@
|
||||||
|
package org.keycloak.representations;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.annotate.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Access Token Response json
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class AccessTokenResponse
|
||||||
|
{
|
||||||
|
@JsonProperty("access_token")
|
||||||
|
protected String token;
|
||||||
|
|
||||||
|
@JsonProperty("expires_in")
|
||||||
|
protected long expiresIn;
|
||||||
|
|
||||||
|
@JsonProperty("refresh_token")
|
||||||
|
protected String refreshToken;
|
||||||
|
|
||||||
|
@JsonProperty("token_type")
|
||||||
|
protected String tokenType;
|
||||||
|
|
||||||
|
public String getToken()
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token)
|
||||||
|
{
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpiresIn()
|
||||||
|
{
|
||||||
|
return expiresIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiresIn(long expiresIn)
|
||||||
|
{
|
||||||
|
this.expiresIn = expiresIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRefreshToken()
|
||||||
|
{
|
||||||
|
return refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRefreshToken(String refreshToken)
|
||||||
|
{
|
||||||
|
this.refreshToken = refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenType()
|
||||||
|
{
|
||||||
|
return tokenType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenType(String tokenType)
|
||||||
|
{
|
||||||
|
this.tokenType = tokenType;
|
||||||
|
}
|
||||||
|
}
|
13
core/src/main/java/org/keycloak/representations/SkeletonKeyScope.java
Executable file
13
core/src/main/java/org/keycloak/representations/SkeletonKeyScope.java
Executable file
|
@ -0,0 +1,13 @@
|
||||||
|
package org.keycloak.representations;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key is resource desired. Values are roles desired for that resource
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SkeletonKeyScope extends MultivaluedHashMap<String, String>
|
||||||
|
{
|
||||||
|
}
|
202
core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java
Executable file
202
core/src/main/java/org/keycloak/representations/SkeletonKeyToken.java
Executable file
|
@ -0,0 +1,202 @@
|
||||||
|
package org.keycloak.representations;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
import org.codehaus.jackson.annotate.JsonProperty;
|
||||||
|
import org.jboss.resteasy.jwt.JsonWebToken;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SkeletonKeyToken extends JsonWebToken
|
||||||
|
{
|
||||||
|
public static class Access
|
||||||
|
{
|
||||||
|
@JsonProperty("roles")
|
||||||
|
protected Set<String> roles;
|
||||||
|
@JsonProperty("verify_caller")
|
||||||
|
protected Boolean verifyCaller;
|
||||||
|
|
||||||
|
public Set<String> getRoles()
|
||||||
|
{
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Access roles(Set<String> roles)
|
||||||
|
{
|
||||||
|
this.roles = roles;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isUserInRole(String role)
|
||||||
|
{
|
||||||
|
if (roles == null) return false;
|
||||||
|
return roles.contains(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Access addRole(String role)
|
||||||
|
{
|
||||||
|
if (roles == null) roles = new HashSet<String>();
|
||||||
|
roles.add(role);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getVerifyCaller()
|
||||||
|
{
|
||||||
|
return verifyCaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Access verifyCaller(Boolean required)
|
||||||
|
{
|
||||||
|
this.verifyCaller = required;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("issuedFor")
|
||||||
|
public String issuedFor;
|
||||||
|
|
||||||
|
@JsonProperty("trusted-certs")
|
||||||
|
protected Set<String> trustedCertificates;
|
||||||
|
|
||||||
|
|
||||||
|
@JsonProperty("realm_access")
|
||||||
|
protected Access realmAccess;
|
||||||
|
|
||||||
|
@JsonProperty("resource_access")
|
||||||
|
protected Map<String, Access> resourceAccess = new HashMap<String, Access>();
|
||||||
|
|
||||||
|
public Map<String, Access> getResourceAccess()
|
||||||
|
{
|
||||||
|
return resourceAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the realm require verifying the caller?
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isVerifyCaller()
|
||||||
|
{
|
||||||
|
if (getRealmAccess() != null && getRealmAccess().getVerifyCaller() != null) return getRealmAccess().getVerifyCaller().booleanValue();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the resource override the requirement of verifying the caller?
|
||||||
|
*
|
||||||
|
* @param resource
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isVerifyCaller(String resource)
|
||||||
|
{
|
||||||
|
Access access = getResourceAccess(resource);
|
||||||
|
if (access != null && access.getVerifyCaller() != null) return access.getVerifyCaller().booleanValue();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public Access getResourceAccess(String resource)
|
||||||
|
{
|
||||||
|
return resourceAccess.get(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Access addAccess(String service)
|
||||||
|
{
|
||||||
|
Access token = new Access();
|
||||||
|
resourceAccess.put(service, token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken id(String id)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.id(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken expiration(long expiration)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.expiration(expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken notBefore(long notBefore)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.notBefore(notBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken issuedAt(long issuedAt)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.issuedAt(issuedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken issuer(String issuer)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.issuer(issuer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken audience(String audience)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.audience(audience);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken principal(String principal)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.principal(principal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkeletonKeyToken type(String type)
|
||||||
|
{
|
||||||
|
return (SkeletonKeyToken)super.type(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Access getRealmAccess()
|
||||||
|
{
|
||||||
|
return realmAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmAccess(Access realmAccess)
|
||||||
|
{
|
||||||
|
this.realmAccess = realmAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getTrustedCertificates()
|
||||||
|
{
|
||||||
|
return trustedCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrustedCertificates(Set<String> trustedCertificates)
|
||||||
|
{
|
||||||
|
this.trustedCertificates = trustedCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth client the token was issued for.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getIssuedFor()
|
||||||
|
{
|
||||||
|
return issuedFor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyToken issuedFor(String issuedFor)
|
||||||
|
{
|
||||||
|
this.issuedFor = issuedFor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
import org.codehaus.jackson.annotate.JsonProperty;
|
||||||
|
import org.keycloak.PemUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class PublishedRealmRepresentation
|
||||||
|
{
|
||||||
|
protected String realm;
|
||||||
|
protected String self;
|
||||||
|
|
||||||
|
@JsonProperty("public_key")
|
||||||
|
protected String publicKeyPem;
|
||||||
|
|
||||||
|
@JsonProperty("authorization")
|
||||||
|
protected String authorizationUrl;
|
||||||
|
|
||||||
|
@JsonProperty("codes")
|
||||||
|
protected String codeUrl;
|
||||||
|
|
||||||
|
@JsonProperty("grants")
|
||||||
|
protected String grantUrl;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
protected volatile transient PublicKey publicKey;
|
||||||
|
|
||||||
|
|
||||||
|
public String getRealm()
|
||||||
|
{
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealm(String realm)
|
||||||
|
{
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSelf()
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelf(String self)
|
||||||
|
{
|
||||||
|
this.self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKeyPem()
|
||||||
|
{
|
||||||
|
return publicKeyPem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKeyPem(String publicKeyPem)
|
||||||
|
{
|
||||||
|
this.publicKeyPem = publicKeyPem;
|
||||||
|
this.publicKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public PublicKey getPublicKey()
|
||||||
|
{
|
||||||
|
if (publicKey != null) return publicKey;
|
||||||
|
if (publicKeyPem != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
publicKey = PemUtils.decodePublicKey(publicKeyPem);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public void setPublicKey(PublicKey publicKey)
|
||||||
|
{
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
PEMWriter pemWriter = new PEMWriter(writer);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pemWriter.writeObject(publicKey);
|
||||||
|
pemWriter.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String s = writer.toString();
|
||||||
|
this.publicKeyPem = PemUtils.removeBeginEnd(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getAuthorizationUrl()
|
||||||
|
{
|
||||||
|
return authorizationUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorizationUrl(String authorizationUrl)
|
||||||
|
{
|
||||||
|
this.authorizationUrl = authorizationUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodeUrl()
|
||||||
|
{
|
||||||
|
return codeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeUrl(String codeUrl)
|
||||||
|
{
|
||||||
|
this.codeUrl = codeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGrantUrl()
|
||||||
|
{
|
||||||
|
return grantUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrantUrl(String grantUrl)
|
||||||
|
{
|
||||||
|
this.grantUrl = grantUrl;
|
||||||
|
}
|
||||||
|
}
|
171
core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
Executable file
171
core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
Executable file
|
@ -0,0 +1,171 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RealmRepresentation
|
||||||
|
{
|
||||||
|
protected String self; // link
|
||||||
|
protected String realm;
|
||||||
|
protected long tokenLifespan;
|
||||||
|
protected long accessCodeLifespan;
|
||||||
|
protected boolean enabled;
|
||||||
|
protected boolean sslNotRequired;
|
||||||
|
protected boolean cookieLoginAllowed;
|
||||||
|
protected List<RequiredCredentialRepresentation> requiredCredentials;
|
||||||
|
protected List<UserRepresentation> users;
|
||||||
|
protected List<RoleMappingRepresentation> roleMappings;
|
||||||
|
protected List<ScopeMappingRepresentation> scopeMappings;
|
||||||
|
protected List<ResourceRepresentation> resources;
|
||||||
|
|
||||||
|
|
||||||
|
public String getSelf()
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelf(String self)
|
||||||
|
{
|
||||||
|
this.self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealm()
|
||||||
|
{
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealm(String realm)
|
||||||
|
{
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserRepresentation> getUsers()
|
||||||
|
{
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ResourceRepresentation> getResources()
|
||||||
|
{
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceRepresentation resource(String name)
|
||||||
|
{
|
||||||
|
ResourceRepresentation resource = new ResourceRepresentation();
|
||||||
|
if (resources == null) resources = new ArrayList<ResourceRepresentation>();
|
||||||
|
resources.add(resource);
|
||||||
|
resource.setName(name);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsers(List<UserRepresentation> users)
|
||||||
|
{
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRepresentation user(String username)
|
||||||
|
{
|
||||||
|
UserRepresentation user = new UserRepresentation();
|
||||||
|
user.setUsername(username);
|
||||||
|
if (users == null) users = new ArrayList<UserRepresentation>();
|
||||||
|
users.add(user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResources(List<ResourceRepresentation> resources)
|
||||||
|
{
|
||||||
|
this.resources = resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslNotRequired()
|
||||||
|
{
|
||||||
|
return sslNotRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslNotRequired(boolean sslNotRequired)
|
||||||
|
{
|
||||||
|
this.sslNotRequired = sslNotRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCookieLoginAllowed()
|
||||||
|
{
|
||||||
|
return cookieLoginAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookieLoginAllowed(boolean cookieLoginAllowed)
|
||||||
|
{
|
||||||
|
this.cookieLoginAllowed = cookieLoginAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTokenLifespan()
|
||||||
|
{
|
||||||
|
return tokenLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenLifespan(long tokenLifespan)
|
||||||
|
{
|
||||||
|
this.tokenLifespan = tokenLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoleMappingRepresentation> getRoleMappings()
|
||||||
|
{
|
||||||
|
return roleMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleMappingRepresentation roleMapping(String username)
|
||||||
|
{
|
||||||
|
RoleMappingRepresentation mapping = new RoleMappingRepresentation();
|
||||||
|
mapping.setUsername(username);
|
||||||
|
if (roleMappings == null) roleMappings = new ArrayList<RoleMappingRepresentation>();
|
||||||
|
roleMappings.add(mapping);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ScopeMappingRepresentation> getScopeMappings()
|
||||||
|
{
|
||||||
|
return scopeMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScopeMappingRepresentation scopeMapping(String username)
|
||||||
|
{
|
||||||
|
ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
|
||||||
|
mapping.setUsername(username);
|
||||||
|
if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
|
||||||
|
scopeMappings.add(mapping);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RequiredCredentialRepresentation> getRequiredCredentials()
|
||||||
|
{
|
||||||
|
return requiredCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiredCredentials(List<RequiredCredentialRepresentation> requiredCredentials)
|
||||||
|
{
|
||||||
|
this.requiredCredentials = requiredCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAccessCodeLifespan()
|
||||||
|
{
|
||||||
|
return accessCodeLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessCodeLifespan(long accessCodeLifespan)
|
||||||
|
{
|
||||||
|
this.accessCodeLifespan = accessCodeLifespan;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RequiredCredentialRepresentation
|
||||||
|
{
|
||||||
|
public static final String PASSWORD = "Password";
|
||||||
|
public static final String TOTP = "TOTP";
|
||||||
|
public static final String CLIENT_CERT = "CLIENT_CERT";
|
||||||
|
public static final String CALLER_PRINCIPAL = "CALLER_PRINCIPAL";
|
||||||
|
protected String type;
|
||||||
|
protected boolean input;
|
||||||
|
protected boolean secret;
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInput()
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(boolean input)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecret()
|
||||||
|
{
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecret(boolean secret)
|
||||||
|
{
|
||||||
|
this.secret = secret;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ResourceRepresentation
|
||||||
|
{
|
||||||
|
protected String self; // link
|
||||||
|
protected String name;
|
||||||
|
protected boolean surrogateAuthRequired;
|
||||||
|
protected Set<String> roles;
|
||||||
|
protected List<RoleMappingRepresentation> roleMappings;
|
||||||
|
protected List<ScopeMappingRepresentation> scopeMappings;
|
||||||
|
|
||||||
|
public String getSelf()
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelf(String self)
|
||||||
|
{
|
||||||
|
this.self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSurrogateAuthRequired()
|
||||||
|
{
|
||||||
|
return surrogateAuthRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurrogateAuthRequired(boolean surrogateAuthRequired)
|
||||||
|
{
|
||||||
|
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoles()
|
||||||
|
{
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Set<String> roles)
|
||||||
|
{
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceRepresentation role(String role)
|
||||||
|
{
|
||||||
|
if (this.roles == null) this.roles = new HashSet<String>();
|
||||||
|
this.roles.add(role);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoleMappingRepresentation> getRoleMappings()
|
||||||
|
{
|
||||||
|
return roleMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleMappingRepresentation roleMapping(String username)
|
||||||
|
{
|
||||||
|
RoleMappingRepresentation mapping = new RoleMappingRepresentation();
|
||||||
|
mapping.setUsername(username);
|
||||||
|
if (roleMappings == null) roleMappings = new ArrayList<RoleMappingRepresentation>();
|
||||||
|
roleMappings.add(mapping);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ScopeMappingRepresentation> getScopeMappings()
|
||||||
|
{
|
||||||
|
return scopeMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScopeMappingRepresentation scopeMapping(String username)
|
||||||
|
{
|
||||||
|
ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
|
||||||
|
mapping.setUsername(username);
|
||||||
|
if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
|
||||||
|
scopeMappings.add(mapping);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RoleMappingRepresentation
|
||||||
|
{
|
||||||
|
protected String self; // link
|
||||||
|
protected String username;
|
||||||
|
protected Set<String> roles;
|
||||||
|
protected Set<String> surrogates;
|
||||||
|
|
||||||
|
public String getSelf()
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelf(String self)
|
||||||
|
{
|
||||||
|
this.self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername()
|
||||||
|
{
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username)
|
||||||
|
{
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoles()
|
||||||
|
{
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getSurrogates()
|
||||||
|
{
|
||||||
|
return surrogates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurrogates(Set<String> surrogates)
|
||||||
|
{
|
||||||
|
this.surrogates = surrogates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleMappingRepresentation surrogate(String surrogate)
|
||||||
|
{
|
||||||
|
if (this.surrogates == null) this.surrogates = new HashSet<String>();
|
||||||
|
this.surrogates.add(surrogate);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setRoles(Set<String> roles)
|
||||||
|
{
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleMappingRepresentation role(String role)
|
||||||
|
{
|
||||||
|
if (this.roles == null) this.roles = new HashSet<String>();
|
||||||
|
this.roles.add(role);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ScopeMappingRepresentation
|
||||||
|
{
|
||||||
|
protected String self; // link
|
||||||
|
protected String username;
|
||||||
|
protected Set<String> roles;
|
||||||
|
|
||||||
|
public String getSelf()
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelf(String self)
|
||||||
|
{
|
||||||
|
this.self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername()
|
||||||
|
{
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username)
|
||||||
|
{
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoles()
|
||||||
|
{
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Set<String> roles)
|
||||||
|
{
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScopeMappingRepresentation role(String role)
|
||||||
|
{
|
||||||
|
if (this.roles == null) this.roles = new HashSet<String>();
|
||||||
|
this.roles.add(role);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
124
core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
Executable file
124
core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
Executable file
|
@ -0,0 +1,124 @@
|
||||||
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class UserRepresentation
|
||||||
|
{
|
||||||
|
public static class Credential
|
||||||
|
{
|
||||||
|
protected String type;
|
||||||
|
protected String value;
|
||||||
|
protected boolean hashed;
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHashed()
|
||||||
|
{
|
||||||
|
return hashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashed(boolean hashed)
|
||||||
|
{
|
||||||
|
this.hashed = hashed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String self; // link
|
||||||
|
protected String username;
|
||||||
|
protected boolean enabled;
|
||||||
|
protected Map<String, String> attributes;
|
||||||
|
protected List<Credential> credentials;
|
||||||
|
|
||||||
|
public String getSelf()
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelf(String self)
|
||||||
|
{
|
||||||
|
this.self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername()
|
||||||
|
{
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username)
|
||||||
|
{
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAttributes()
|
||||||
|
{
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributes(Map<String, String> attributes)
|
||||||
|
{
|
||||||
|
this.attributes = attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Credential> getCredentials()
|
||||||
|
{
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredentials(List<Credential> credentials)
|
||||||
|
{
|
||||||
|
this.credentials = credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRepresentation attribute(String name, String value)
|
||||||
|
{
|
||||||
|
if (this.attributes == null) attributes = new HashMap<String, String>();
|
||||||
|
attributes.put(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRepresentation credential(String type, String value, boolean hashed)
|
||||||
|
{
|
||||||
|
if (this.credentials == null) credentials = new ArrayList<Credential>();
|
||||||
|
Credential cred = new Credential();
|
||||||
|
cred.setType(type);
|
||||||
|
cred.setValue(value);
|
||||||
|
cred.setHashed(hashed);
|
||||||
|
credentials.add( cred);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
127
core/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
Executable file
127
core/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
Executable file
|
@ -0,0 +1,127 @@
|
||||||
|
package org.keycloak.servlet;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.plugins.server.servlet.ServletUtil;
|
||||||
|
import org.keycloak.AbstractOAuthClient;
|
||||||
|
import org.jboss.resteasy.spi.ResteasyUriInfo;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ServletOAuthClient extends AbstractOAuthClient
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Start the process of obtaining an access token by redirecting the browser
|
||||||
|
* to the authentication server
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param relativePath path relative to context root you want auth server to redirect back to
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void redirectRelative(String relativePath, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
|
{
|
||||||
|
ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null);
|
||||||
|
String redirect = uriInfo.getBaseUriBuilder().path(relativePath).toTemplate();
|
||||||
|
redirect(redirect, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the process of obtaining an access token by redirecting the browser
|
||||||
|
* to the authentication server
|
||||||
|
*
|
||||||
|
* @param redirectUri full URI you want auth server to redirect back to
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void redirect(String redirectUri, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
|
{
|
||||||
|
String state = getStateCode();
|
||||||
|
|
||||||
|
URI url = UriBuilder.fromUri(authUrl)
|
||||||
|
.queryParam("client_id", clientId)
|
||||||
|
.queryParam("redirect_uri", redirectUri)
|
||||||
|
.queryParam("state", state)
|
||||||
|
.build();
|
||||||
|
String cookiePath = request.getContextPath();
|
||||||
|
if (cookiePath.equals("")) cookiePath = "/";
|
||||||
|
|
||||||
|
Cookie cookie = new Cookie(stateCookieName, state);
|
||||||
|
cookie.setSecure(true);
|
||||||
|
cookie.setPath(cookiePath);
|
||||||
|
response.addCookie(cookie);
|
||||||
|
response.sendRedirect(url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getCookieValue(String name, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
if (request.getCookies() == null) return null;
|
||||||
|
|
||||||
|
for (Cookie cookie : request.getCookies())
|
||||||
|
{
|
||||||
|
if (cookie.getName().equals(name)) return cookie.getValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getCode(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
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("code")) continue;
|
||||||
|
return param.substring(eq + 1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the code parameter from the url after being redirected back from the auth-server. Then
|
||||||
|
* do an authenticated request back to the auth-server to turn the access code into an access token.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
* @throws BadRequestException
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public String getBearerToken(HttpServletRequest request) throws BadRequestException, InternalServerErrorException
|
||||||
|
{
|
||||||
|
String error = request.getParameter("error");
|
||||||
|
if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
|
||||||
|
String redirectUri = request.getRequestURL().append("?").append(request.getQueryString()).toString();
|
||||||
|
String stateCookie = getCookieValue(stateCookieName, request);
|
||||||
|
if (stateCookie == null) throw new BadRequestException(new Exception("state cookie not set"));
|
||||||
|
// we can call get parameter as this should be a redirect
|
||||||
|
String state = request.getParameter("state");
|
||||||
|
String code = request.getParameter("code");
|
||||||
|
|
||||||
|
if (state == null) throw new BadRequestException(new Exception("state parameter was null"));
|
||||||
|
if (!state.equals(stateCookie))
|
||||||
|
{
|
||||||
|
throw new BadRequestException(new Exception("state parameter invalid"));
|
||||||
|
}
|
||||||
|
if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
|
||||||
|
return resolveBearerToken(redirectUri, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
282
core/src/test/java/org/keycloak/RSAVerifierTest.java
Executable file
282
core/src/test/java/org/keycloak/RSAVerifierTest.java
Executable file
|
@ -0,0 +1,282 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
|
import org.bouncycastle.x509.X509V1CertificateGenerator;
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSBuilder;
|
||||||
|
import org.jboss.resteasy.jwt.JsonSerialization;
|
||||||
|
import org.keycloak.RSATokenVerifier;
|
||||||
|
import org.keycloak.ResourceMetadata;
|
||||||
|
import org.keycloak.VerificationException;
|
||||||
|
import org.keycloak.representations.SkeletonKeyToken;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RSAVerifierTest
|
||||||
|
{
|
||||||
|
private static X509Certificate[] idpCertificates;
|
||||||
|
private static KeyPair idpPair;
|
||||||
|
private static KeyPair badPair;
|
||||||
|
private static KeyPair clientPair;
|
||||||
|
private static X509Certificate[] clientCertificateChain;
|
||||||
|
private ResourceMetadata metadata;
|
||||||
|
private SkeletonKeyToken token;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
if (Security.getProvider("BC") == null) Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate generateTestCertificate(String subject, String issuer, KeyPair pair) throws InvalidKeyException,
|
||||||
|
NoSuchProviderException, SignatureException
|
||||||
|
{
|
||||||
|
|
||||||
|
X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
|
||||||
|
|
||||||
|
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
|
||||||
|
certGen.setIssuerDN(new X500Principal(issuer));
|
||||||
|
certGen.setNotBefore(new Date(System.currentTimeMillis() - 10000));
|
||||||
|
certGen.setNotAfter(new Date(System.currentTimeMillis() + 10000));
|
||||||
|
certGen.setSubjectDN(new X500Principal(subject));
|
||||||
|
certGen.setPublicKey(pair.getPublic());
|
||||||
|
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
|
||||||
|
|
||||||
|
return certGen.generateX509Certificate(pair.getPrivate(), "BC");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupCerts() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException
|
||||||
|
{
|
||||||
|
badPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||||
|
idpPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||||
|
idpCertificates = new X509Certificate[]{generateTestCertificate("CN=IDP", "CN=IDP", idpPair)};
|
||||||
|
clientPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||||
|
clientCertificateChain = new X509Certificate[]{generateTestCertificate("CN=Client", "CN=IDP", idpPair)};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initTest()
|
||||||
|
{
|
||||||
|
metadata = new ResourceMetadata();
|
||||||
|
metadata.setResourceName("service");
|
||||||
|
metadata.setRealm("domain");
|
||||||
|
metadata.setRealmKey(idpPair.getPublic());
|
||||||
|
|
||||||
|
token = new SkeletonKeyToken();
|
||||||
|
token.principal("CN=Client")
|
||||||
|
.audience("domain")
|
||||||
|
.addAccess("service").addRole("admin");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPemWriter() throws Exception
|
||||||
|
{
|
||||||
|
PublicKey realmPublicKey = idpPair.getPublic();
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
PEMWriter writer = new PEMWriter(sw);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
writer.writeObject(realmPublicKey);
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
System.out.println(sw.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleVerification() throws Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
SkeletonKeyToken token = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
|
||||||
|
Assert.assertEquals("CN=Client", token.getPrincipal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@Test
|
||||||
|
public void testSpeed() throws Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
int count = 10000;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
SkeletonKeyTokenVerification v = RSATokenVerifier.verify(null, encoded, metadata);
|
||||||
|
|
||||||
|
}
|
||||||
|
long end = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("rate: " + ((double)end/(double)count));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBadSignature() throws Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(badPair.getPrivate());
|
||||||
|
|
||||||
|
SkeletonKeyToken v = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch (VerificationException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotBeforeGood() throws Exception
|
||||||
|
{
|
||||||
|
token.notBefore((System.currentTimeMillis()/1000) - 100);
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
|
||||||
|
SkeletonKeyToken v = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
}
|
||||||
|
catch (VerificationException ignored)
|
||||||
|
{
|
||||||
|
throw ignored;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotBeforeBad() throws Exception
|
||||||
|
{
|
||||||
|
token.notBefore((System.currentTimeMillis()/1000) + 100);
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
|
||||||
|
SkeletonKeyToken v = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch (VerificationException ignored)
|
||||||
|
{
|
||||||
|
System.out.println(ignored.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExpirationGood() throws Exception
|
||||||
|
{
|
||||||
|
token.expiration((System.currentTimeMillis()/1000) + 100);
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
|
||||||
|
SkeletonKeyToken v = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
}
|
||||||
|
catch (VerificationException ignored)
|
||||||
|
{
|
||||||
|
throw ignored;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExpirationBad() throws Exception
|
||||||
|
{
|
||||||
|
token.expiration((System.currentTimeMillis()/1000) - 100);
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
|
||||||
|
SkeletonKeyToken v = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch (VerificationException ignored)
|
||||||
|
{
|
||||||
|
System.out.println(ignored.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTokenAuth() throws Exception
|
||||||
|
{
|
||||||
|
token = new SkeletonKeyToken();
|
||||||
|
token.principal("CN=Client")
|
||||||
|
.audience("domain")
|
||||||
|
.addAccess("service").addRole("admin").verifyCaller(true);
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(idpPair.getPrivate());
|
||||||
|
|
||||||
|
SkeletonKeyToken v = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v = RSATokenVerifier.verifyToken(encoded, metadata);
|
||||||
|
}
|
||||||
|
catch (VerificationException ignored)
|
||||||
|
{
|
||||||
|
System.out.println(ignored.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
78
core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
Executable file
78
core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSBuilder;
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSInput;
|
||||||
|
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
|
||||||
|
import org.jboss.resteasy.jwt.JsonSerialization;
|
||||||
|
import org.keycloak.representations.SkeletonKeyScope;
|
||||||
|
import org.keycloak.representations.SkeletonKeyToken;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SkeletonKeyTokenTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testScope() throws Exception
|
||||||
|
{
|
||||||
|
SkeletonKeyScope scope2 = new SkeletonKeyScope();
|
||||||
|
|
||||||
|
scope2.add("one", "admin");
|
||||||
|
scope2.add("one", "buyer");
|
||||||
|
scope2.add("two", "seller");
|
||||||
|
String json = JsonSerialization.toString(scope2, true);
|
||||||
|
System.out.println(json);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToken() throws Exception
|
||||||
|
{
|
||||||
|
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||||
|
token.id("111");
|
||||||
|
token.addAccess("foo").addRole("admin");
|
||||||
|
token.addAccess("bar").addRole("user");
|
||||||
|
|
||||||
|
String json = JsonSerialization.toString(token, true);
|
||||||
|
System.out.println(json);
|
||||||
|
|
||||||
|
token = JsonSerialization.fromString(SkeletonKeyToken.class, json);
|
||||||
|
Assert.assertEquals("111", token.getId());
|
||||||
|
SkeletonKeyToken.Access foo = token.getResourceAccess("foo");
|
||||||
|
Assert.assertNotNull(foo);
|
||||||
|
Assert.assertTrue(foo.isUserInRole("admin"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRSA() throws Exception
|
||||||
|
{
|
||||||
|
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||||
|
token.id("111");
|
||||||
|
token.addAccess("foo").addRole("admin");
|
||||||
|
token.addAccess("bar").addRole("user");
|
||||||
|
|
||||||
|
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||||
|
byte[] tokenBytes = JsonSerialization.toByteArray(token, true);
|
||||||
|
|
||||||
|
String encoded = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(keyPair.getPrivate());
|
||||||
|
|
||||||
|
System.out.println(encoded);
|
||||||
|
|
||||||
|
JWSInput input = new JWSInput(encoded);
|
||||||
|
byte[] content = input.getContent();
|
||||||
|
|
||||||
|
token = JsonSerialization.fromBytes(SkeletonKeyToken.class, content);
|
||||||
|
Assert.assertEquals("111", token.getId());
|
||||||
|
Assert.assertTrue(RSAProvider.verify(input, keyPair.getPublic()));
|
||||||
|
}
|
||||||
|
}
|
301
pom.xml
Executable file
301
pom.xml
Executable file
|
@ -0,0 +1,301 @@
|
||||||
|
<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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<name>Identity Guardener</name>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<version>1.0-alpha-1</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<resteasy.version>3.0.1.Final</resteasy.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<url>http://keycloak.org</url>
|
||||||
|
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>Apache License, Version 2.0</name>
|
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
|
||||||
|
<distribution>repo</distribution>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>jboss-releases-repository</id>
|
||||||
|
<name>JBoss Releases Repository</name>
|
||||||
|
<url>https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
|
<issueManagement>
|
||||||
|
<system>JIRA</system>
|
||||||
|
<url>http://jira.jboss.com/jira/browse/KEYCLOAK</url>
|
||||||
|
</issueManagement>
|
||||||
|
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>patriot1burke</id>
|
||||||
|
<name>Bill Burke</name>
|
||||||
|
<email>bburke@redhat.co</email>
|
||||||
|
<organization>Red Hat</organization>
|
||||||
|
<roles>
|
||||||
|
<role>project-owner</role>
|
||||||
|
</roles>
|
||||||
|
<timezone>-5</timezone>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
|
||||||
|
<contributors>
|
||||||
|
</contributors>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>core</module>
|
||||||
|
<module>services</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
|
<version>1.46</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcmail-jdk16</artifactId>
|
||||||
|
<version>1.46</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-core</artifactId>
|
||||||
|
<version>5.1.6.FINAL</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-tree</artifactId>
|
||||||
|
<version>5.1.6.FINAL</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>jaxrs-api</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-jaxrs</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-client</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>jose-jwt</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-crypto</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>jose-jwt</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-core-asl</artifactId>
|
||||||
|
<version>1.9.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-mapper-asl</artifactId>
|
||||||
|
<version>1.9.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-xc</artifactId>
|
||||||
|
<version>1.9.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-jaxrs</artifactId>
|
||||||
|
<version>1.9.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<version>2.5</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>tjws</artifactId>
|
||||||
|
<version>${resteasy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-idm-api</artifactId>
|
||||||
|
<version>2.5.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-idm-impl</artifactId>
|
||||||
|
<version>2.5.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-idm-schema</artifactId>
|
||||||
|
<version>2.5.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-config</artifactId>
|
||||||
|
<version>2.5.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.11</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>jboss</id>
|
||||||
|
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
</pluginRepositories>
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<!-- <version>2.5</version> -->
|
||||||
|
<configuration>
|
||||||
|
<forkMode>once</forkMode>
|
||||||
|
<argLine>-Xms512m -Xmx512m</argLine>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.6</source>
|
||||||
|
<target>1.6</target>
|
||||||
|
<encoding>utf-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.8</version>
|
||||||
|
<configuration>
|
||||||
|
<minmemory>128m</minmemory>
|
||||||
|
<maxmemory>1024m</maxmemory>
|
||||||
|
<quiet>false</quiet>
|
||||||
|
<aggregate>true</aggregate>
|
||||||
|
<excludePackageNames>
|
||||||
|
com.restfully.*:org.jboss.resteasy.examples.*:se.unlogic.*:org.jboss.resteasy.tests.*
|
||||||
|
</excludePackageNames>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
<configuration>
|
||||||
|
<createChecksum>true</createChecksum>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>2.1.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>2.5</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.6</source>
|
||||||
|
<target>1.6</target>
|
||||||
|
<encoding>utf-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.8</version>
|
||||||
|
<configuration>
|
||||||
|
<minmemory>128m</minmemory>
|
||||||
|
<maxmemory>1024m</maxmemory>
|
||||||
|
<quiet>false</quiet>
|
||||||
|
<aggregate>true</aggregate>
|
||||||
|
<excludePackageNames>
|
||||||
|
se.unlogic.*:com.restfully.*:org.jboss.resteasy.examples.*:org.jboss.resteasy.tests.*
|
||||||
|
</excludePackageNames>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
<configuration>
|
||||||
|
<createChecksum>true</createChecksum>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>2.1.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>2.5</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.atlassian.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-clover2-plugin</artifactId>
|
||||||
|
<version>3.1.6</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
</project>
|
111
services/pom.xml
Executable file
111
services/pom.xml
Executable file
|
@ -0,0 +1,111 @@
|
||||||
|
<?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-services</artifactId>
|
||||||
|
<name>Keycloak REST Services</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-idm-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-idm-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-idm-schema</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketlink</groupId>
|
||||||
|
<artifactId>picketlink-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-jaxrs</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>jaxrs-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-client</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-crypto</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>jose-jwt</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>tjws</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-core-asl</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-mapper-asl</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-xc</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</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>
|
84
services/src/main/java/org/keycloak/services/IdentityManagerAdapter.java
Executable file
84
services/src/main/java/org/keycloak/services/IdentityManagerAdapter.java
Executable file
|
@ -0,0 +1,84 @@
|
||||||
|
package org.keycloak.services;
|
||||||
|
|
||||||
|
import org.keycloak.services.model.data.RealmModel;
|
||||||
|
import org.keycloak.services.model.data.RequiredCredentialModel;
|
||||||
|
import org.keycloak.services.model.data.ResourceModel;
|
||||||
|
import org.keycloak.services.model.data.RoleMappingModel;
|
||||||
|
import org.keycloak.services.model.data.RoleModel;
|
||||||
|
import org.keycloak.services.model.data.ScopeMappingModel;
|
||||||
|
import org.keycloak.services.model.data.UserAttributeModel;
|
||||||
|
import org.keycloak.services.model.data.UserCredentialModel;
|
||||||
|
import org.keycloak.services.model.data.UserModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public interface IdentityManagerAdapter
|
||||||
|
{
|
||||||
|
RealmModel getRealm(String id);
|
||||||
|
RealmModel create(RealmModel realm);
|
||||||
|
void update(RealmModel realm);
|
||||||
|
void delete(RealmModel realm);
|
||||||
|
List<RequiredCredentialModel> getRequiredCredentials(RealmModel realm);
|
||||||
|
RequiredCredentialModel getRealmCredential(String id);
|
||||||
|
RequiredCredentialModel create(RealmModel realm, RequiredCredentialModel cred);
|
||||||
|
void update(RequiredCredentialModel cred);
|
||||||
|
void delete(RequiredCredentialModel cred);
|
||||||
|
|
||||||
|
UserModel getUser(RealmModel realm, String username);
|
||||||
|
UserModel create(RealmModel realm, UserModel user);
|
||||||
|
void update(RealmModel realm, UserModel user);
|
||||||
|
void delete(RealmModel realm,UserModel user);
|
||||||
|
|
||||||
|
List<UserCredentialModel> getCredentials(UserModel user);
|
||||||
|
UserCredentialModel getCredential(String id);
|
||||||
|
UserCredentialModel create(UserModel user, UserCredentialModel cred);
|
||||||
|
void update(UserCredentialModel cred);
|
||||||
|
void delete(UserCredentialModel cred);
|
||||||
|
|
||||||
|
UserAttributeModel getUserAttribute(String id);
|
||||||
|
UserAttributeModel create(UserModel user, UserAttributeModel attribute);
|
||||||
|
void update(UserAttributeModel attribute);
|
||||||
|
void delete(UserAttributeModel attribute);
|
||||||
|
|
||||||
|
ResourceModel getResource(String resourceid);
|
||||||
|
List<ResourceModel> getResources(RealmModel realm);
|
||||||
|
ResourceModel create(RealmModel realm, ResourceModel resource);
|
||||||
|
void update(ResourceModel resource);
|
||||||
|
void delete(ResourceModel resource);
|
||||||
|
|
||||||
|
|
||||||
|
RoleModel getRoleByName(RealmModel realm, String roleName);
|
||||||
|
RoleModel getRoleByName(ResourceModel resource, String roleName);
|
||||||
|
List<RoleModel> getRoles(RealmModel realm, ResourceModel resource);
|
||||||
|
List<RoleModel> getRoles(RealmModel realm);
|
||||||
|
RoleModel getRole(String id);
|
||||||
|
RoleModel create(RealmModel realm, ResourceModel resource, String role);
|
||||||
|
RoleModel create(RealmModel realm, String role);
|
||||||
|
void delete(RoleModel role);
|
||||||
|
|
||||||
|
List<RoleMappingModel> getRoleMappings(RealmModel realm);
|
||||||
|
List<RoleMappingModel> getRoleMappings(RealmModel realm, ResourceModel resource);
|
||||||
|
RoleMappingModel getRoleMapping(RealmModel realm, UserModel user);
|
||||||
|
RoleMappingModel getRoleMapping(RealmModel realm, ResourceModel resource, UserModel user);
|
||||||
|
RoleMappingModel getRoleMapping(String id);
|
||||||
|
RoleMappingModel create(RealmModel realm, UserModel user, RoleMappingModel mapping);
|
||||||
|
RoleMappingModel create(RealmModel realm, ResourceModel resource, UserModel user, RoleMappingModel mapping);
|
||||||
|
void delete(RoleMappingModel role);
|
||||||
|
|
||||||
|
List<ScopeMappingModel> getScopeMappings(RealmModel realm);
|
||||||
|
List<ScopeMappingModel> getScopeMappings(RealmModel realm, ResourceModel resource);
|
||||||
|
ScopeMappingModel getScopeMapping(RealmModel realm, UserModel user);
|
||||||
|
ScopeMappingModel getScopeMapping(RealmModel realm, ResourceModel resource, UserModel user);
|
||||||
|
ScopeMappingModel getScopeMapping(String id);
|
||||||
|
ScopeMappingModel create(RealmModel realm, UserModel user, ScopeMappingModel mapping);
|
||||||
|
ScopeMappingModel create(RealmModel realm, ResourceModel resource, UserModel user, ScopeMappingModel mapping);
|
||||||
|
void delete(ScopeMappingModel scope);
|
||||||
|
|
||||||
|
|
||||||
|
List<RealmModel> getRealmsByName(String name);
|
||||||
|
}
|
49
services/src/main/java/org/keycloak/services/model/RealmManager.java
Executable file
49
services/src/main/java/org/keycloak/services/model/RealmManager.java
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.internal.IdentityManagerFactory;
|
||||||
|
import org.picketlink.idm.model.Realm;
|
||||||
|
import org.picketlink.idm.model.SimpleAgent;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RealmManager
|
||||||
|
{
|
||||||
|
private static AtomicLong counter = new AtomicLong(1);
|
||||||
|
|
||||||
|
public static String generateId()
|
||||||
|
{
|
||||||
|
return counter.getAndIncrement() + "-" + System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IdentityManagerFactory factory;
|
||||||
|
|
||||||
|
public RealmManager(IdentityManagerFactory factory)
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmModel getRealm(String id)
|
||||||
|
{
|
||||||
|
Realm existing = factory.findRealm(id);
|
||||||
|
if (existing == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new RealmModel(existing, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmModel create(String name)
|
||||||
|
{
|
||||||
|
Realm newRealm = factory.createRealm(generateId());
|
||||||
|
IdentityManager idm = factory.createIdentityManager(newRealm);
|
||||||
|
SimpleAgent agent = new SimpleAgent(RealmModel.REALM_AGENT_ID);
|
||||||
|
idm.add(agent);
|
||||||
|
return new RealmModel(newRealm, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
389
services/src/main/java/org/keycloak/services/model/RealmModel.java
Executable file
389
services/src/main/java/org/keycloak/services/model/RealmModel.java
Executable file
|
@ -0,0 +1,389 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
|
import org.jboss.resteasy.security.PemUtils;
|
||||||
|
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.credential.Password;
|
||||||
|
import org.picketlink.idm.credential.TOTPCredential;
|
||||||
|
import org.picketlink.idm.credential.X509CertificateCredentials;
|
||||||
|
import org.picketlink.idm.internal.IdentityManagerFactory;
|
||||||
|
import org.picketlink.idm.model.Agent;
|
||||||
|
import org.picketlink.idm.model.Attribute;
|
||||||
|
import org.picketlink.idm.model.Grant;
|
||||||
|
import org.picketlink.idm.model.Realm;
|
||||||
|
import org.picketlink.idm.model.Role;
|
||||||
|
import org.picketlink.idm.model.SimpleAgent;
|
||||||
|
import org.picketlink.idm.model.Tier;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
import org.picketlink.idm.query.IdentityQuery;
|
||||||
|
import org.picketlink.idm.query.RelationshipQuery;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RealmModel
|
||||||
|
{
|
||||||
|
public static final String REALM_AGENT_ID = "_realm_";
|
||||||
|
public static final String REALM_NAME = "name";
|
||||||
|
public static final String REALM_ACCESS_CODE_LIFESPAN = "accessCodeLifespan";
|
||||||
|
public static final String REALM_TOKEN_LIFESPAN = "tokenLifespan";
|
||||||
|
public static final String REALM_PRIVATE_KEY = "privateKey";
|
||||||
|
public static final String REALM_PUBLIC_KEY = "publicKey";
|
||||||
|
public static final String REALM_IS_SSL_NOT_REQUIRED = "isSSLNotRequired";
|
||||||
|
public static final String REALM_IS_COOKIE_LOGIN_ALLOWED = "isCookieLoginAllowed";
|
||||||
|
|
||||||
|
protected Realm realm;
|
||||||
|
protected Agent realmAgent;
|
||||||
|
protected IdentityManagerFactory factory;
|
||||||
|
protected volatile transient PublicKey publicKey;
|
||||||
|
protected volatile transient PrivateKey privateKey;
|
||||||
|
|
||||||
|
public RealmModel(Realm realm, IdentityManagerFactory factory)
|
||||||
|
{
|
||||||
|
this.realm = realm;
|
||||||
|
this.factory = factory;
|
||||||
|
realmAgent = getIdm().getAgent(REALM_AGENT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdentityManager getIdm()
|
||||||
|
{
|
||||||
|
return factory.createIdentityManager(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateRealm()
|
||||||
|
{
|
||||||
|
getIdm().update(realmAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return realm.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return (String)realmAgent.getAttribute(REALM_NAME).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<String>(REALM_NAME, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return realmAgent.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
realmAgent.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslNotRequired()
|
||||||
|
{
|
||||||
|
return (Boolean)realmAgent.getAttribute(REALM_IS_SSL_NOT_REQUIRED).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslNotRequired(boolean sslNotRequired)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_SSL_NOT_REQUIRED, sslNotRequired));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCookieLoginAllowed()
|
||||||
|
{
|
||||||
|
return (Boolean)realmAgent.getAttribute(REALM_IS_COOKIE_LOGIN_ALLOWED).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookieLoginAllowed(boolean cookieLoginAllowed)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTokenLifespan()
|
||||||
|
{
|
||||||
|
return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenLifespan(long tokenLifespan)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<Long>(REALM_TOKEN_LIFESPAN,tokenLifespan));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAccessCodeLifespan()
|
||||||
|
{
|
||||||
|
return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessCodeLifespan(long accessCodeLifespan)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<Long>(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKeyPem()
|
||||||
|
{
|
||||||
|
return (String) realmAgent.getAttribute(REALM_PUBLIC_KEY).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKeyPem(String publicKeyPem)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<String>(REALM_PUBLIC_KEY, publicKeyPem));
|
||||||
|
this.publicKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKeyPem()
|
||||||
|
{
|
||||||
|
return (String) realmAgent.getAttribute(REALM_PRIVATE_KEY).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKeyPem(String privateKeyPem)
|
||||||
|
{
|
||||||
|
realmAgent.setAttribute(new Attribute<String>(REALM_PRIVATE_KEY, privateKeyPem));
|
||||||
|
this.privateKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublicKey getPublicKey()
|
||||||
|
{
|
||||||
|
if (publicKey != null) return publicKey;
|
||||||
|
String pem = getPublicKeyPem();
|
||||||
|
if (pem != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
publicKey = PemUtils.decodePublicKey(pem);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKey(PublicKey publicKey)
|
||||||
|
{
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
PEMWriter pemWriter = new PEMWriter(writer);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pemWriter.writeObject(publicKey);
|
||||||
|
pemWriter.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String s = writer.toString();
|
||||||
|
setPublicKeyPem(PemUtils.removeBeginEnd(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrivateKey getPrivateKey()
|
||||||
|
{
|
||||||
|
if (privateKey != null) return privateKey;
|
||||||
|
String pem = getPrivateKeyPem();
|
||||||
|
if (pem != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
privateKey = PemUtils.decodePrivateKey(pem);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(PrivateKey privateKey)
|
||||||
|
{
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
PEMWriter pemWriter = new PEMWriter(writer);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pemWriter.writeObject(privateKey);
|
||||||
|
pemWriter.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String s = writer.toString();
|
||||||
|
setPrivateKeyPem(PemUtils.removeBeginEnd(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RequiredCredentialModel> getRequiredCredentials()
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
Agent realmAgent = idm.getAgent(REALM_AGENT_ID);
|
||||||
|
RelationshipQuery<RequiredCredentialRelationship> query = idm.createRelationshipQuery(RequiredCredentialRelationship.class);
|
||||||
|
query.setParameter(RequiredCredentialRelationship.REALM_AGENT, realmAgent);
|
||||||
|
List<RequiredCredentialRelationship> results = query.getResultList();
|
||||||
|
List<RequiredCredentialModel> rtn = new ArrayList<RequiredCredentialModel>();
|
||||||
|
for (RequiredCredentialRelationship relationship : results)
|
||||||
|
{
|
||||||
|
RequiredCredentialModel model = new RequiredCredentialModel();
|
||||||
|
model.setInput(relationship.isInput());
|
||||||
|
model.setSecret(relationship.isSecret());
|
||||||
|
model.setType(relationship.getCredentialType());
|
||||||
|
rtn.add(model);
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRequiredCredential(RequiredCredentialModel cred)
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
Agent realmAgent = idm.getAgent(REALM_AGENT_ID);
|
||||||
|
RequiredCredentialRelationship relationship = new RequiredCredentialRelationship();
|
||||||
|
relationship.setCredentialType(cred.getType());
|
||||||
|
relationship.setInput(cred.isInput());
|
||||||
|
relationship.setSecret(cred.isSecret());
|
||||||
|
relationship.setRealmAgent(realmAgent);
|
||||||
|
idm.add(relationship);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateCredential(User user, UserCredentialModel cred)
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD))
|
||||||
|
{
|
||||||
|
Password password = new Password(cred.getValue());
|
||||||
|
idm.updateCredential(user, password);
|
||||||
|
}
|
||||||
|
else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP))
|
||||||
|
{
|
||||||
|
TOTPCredential totp = new TOTPCredential(cred.getValue());
|
||||||
|
idm.updateCredential(user, totp);
|
||||||
|
}
|
||||||
|
else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT))
|
||||||
|
{
|
||||||
|
X509Certificate cert = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
X509CertificateCredentials creds = new X509CertificateCredentials(cert);
|
||||||
|
idm.updateCredential(user, creds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Role> getRoles()
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
IdentityQuery<Role> query = idm.createIdentityQuery(Role.class);
|
||||||
|
query.setParameter(Role.PARTITION, realm);
|
||||||
|
return query.getResultList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected ResourceModel loadResource(Agent resource)
|
||||||
|
{
|
||||||
|
Tier tier = factory.findTier(resource.getPartition().getId());
|
||||||
|
return new ResourceModel(tier, resource, this, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key name, value resource
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Map<String, ResourceModel> getResourceMap()
|
||||||
|
{
|
||||||
|
Map<String, ResourceModel> resourceMap = new HashMap<String, ResourceModel>();
|
||||||
|
for (ResourceModel resource : getResources())
|
||||||
|
{
|
||||||
|
resourceMap.put(resource.getName(), resource);
|
||||||
|
}
|
||||||
|
return resourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ResourceModel> getResources()
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
RelationshipQuery<RealmResourceRelationship> query = idm.createRelationshipQuery(RealmResourceRelationship.class);
|
||||||
|
query.setParameter(RealmResourceRelationship.REALM_AGENT, realmAgent);
|
||||||
|
List<RealmResourceRelationship> results = query.getResultList();
|
||||||
|
List<ResourceModel> resources = new ArrayList<ResourceModel>();
|
||||||
|
for (RealmResourceRelationship relationship : results)
|
||||||
|
{
|
||||||
|
ResourceModel model = loadResource(relationship.getResourceAgent());
|
||||||
|
resources.add(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceModel addResource(String name)
|
||||||
|
{
|
||||||
|
Tier newTier = factory.createTier(RealmManager.generateId());
|
||||||
|
IdentityManager idm = factory.createIdentityManager(newTier);
|
||||||
|
SimpleAgent resourceAgent = new SimpleAgent(ResourceModel.RESOURCE_AGENT_ID);
|
||||||
|
resourceAgent.setAttribute(new Attribute<String>(ResourceModel.RESOURCE_NAME, name));
|
||||||
|
idm.add(resourceAgent);
|
||||||
|
idm = getIdm();
|
||||||
|
RealmResourceRelationship relationship = new RealmResourceRelationship();
|
||||||
|
relationship.setRealmAgent(realmAgent);
|
||||||
|
relationship.setResourceAgent(resourceAgent);
|
||||||
|
idm.add(relationship);
|
||||||
|
return new ResourceModel(newTier, resourceAgent, this, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoleMappings(User user)
|
||||||
|
{
|
||||||
|
RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class);
|
||||||
|
query.setParameter(Grant.ASSIGNEE, user);
|
||||||
|
List<Grant> grants = query.getResultList();
|
||||||
|
HashSet<String> set = new HashSet<String>();
|
||||||
|
for (Grant grant : grants)
|
||||||
|
{
|
||||||
|
if (grant.getRole().getPartition().getId().equals(realm.getId()))set.add(grant.getRole().getName());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addScope(Agent agent, String roleName)
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
Role role = idm.getRole(roleName);
|
||||||
|
if (role == null) throw new RuntimeException("role not found");
|
||||||
|
ScopeRelationship scope = new ScopeRelationship();
|
||||||
|
scope.setClient(agent);
|
||||||
|
scope.setScope(role);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Set<String> getScope(Agent agent)
|
||||||
|
{
|
||||||
|
RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class);
|
||||||
|
query.setParameter(ScopeRelationship.CLIENT, agent);
|
||||||
|
List<ScopeRelationship> scope = query.getResultList();
|
||||||
|
HashSet<String> set = new HashSet<String>();
|
||||||
|
for (ScopeRelationship rel : scope)
|
||||||
|
{
|
||||||
|
if (rel.getScope().getPartition().getId().equals(realm.getId())) set.add(rel.getScope().getName());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class RequiredCredentialModel
|
||||||
|
{
|
||||||
|
protected String type;
|
||||||
|
protected boolean input;
|
||||||
|
protected boolean secret;
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInput()
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(boolean input)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecret()
|
||||||
|
{
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecret(boolean secret)
|
||||||
|
{
|
||||||
|
this.secret = secret;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
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 RequiredCredentialRelationship 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 credentialType;
|
||||||
|
protected boolean input;
|
||||||
|
protected boolean secret;
|
||||||
|
|
||||||
|
public RequiredCredentialRelationship()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@IdentityProperty
|
||||||
|
public Agent getRealmAgent()
|
||||||
|
{
|
||||||
|
return realmAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmAgent(Agent realmAgent)
|
||||||
|
{
|
||||||
|
this.realmAgent = realmAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AttributeProperty
|
||||||
|
public String getCredentialType()
|
||||||
|
{
|
||||||
|
return credentialType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredentialType(String credentialType)
|
||||||
|
{
|
||||||
|
this.credentialType = credentialType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AttributeProperty
|
||||||
|
public boolean isInput()
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(boolean input)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AttributeProperty
|
||||||
|
public boolean isSecret()
|
||||||
|
{
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecret(boolean secret)
|
||||||
|
{
|
||||||
|
this.secret = secret;
|
||||||
|
}
|
||||||
|
}
|
134
services/src/main/java/org/keycloak/services/model/ResourceModel.java
Executable file
134
services/src/main/java/org/keycloak/services/model/ResourceModel.java
Executable file
|
@ -0,0 +1,134 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.internal.IdentityManagerFactory;
|
||||||
|
import org.picketlink.idm.model.Agent;
|
||||||
|
import org.picketlink.idm.model.Attribute;
|
||||||
|
import org.picketlink.idm.model.Grant;
|
||||||
|
import org.picketlink.idm.model.Role;
|
||||||
|
import org.picketlink.idm.model.Tier;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
import org.picketlink.idm.query.IdentityQuery;
|
||||||
|
import org.picketlink.idm.query.RelationshipQuery;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ResourceModel
|
||||||
|
{
|
||||||
|
public static final String RESOURCE_AGENT_ID = "_resource_";
|
||||||
|
public static final String RESOURCE_NAME = "name";
|
||||||
|
public static final String RESOURCE_SURROGATE_AUTH = "surrogate_auth";
|
||||||
|
|
||||||
|
protected Tier tier;
|
||||||
|
protected Agent agent;
|
||||||
|
protected RealmModel realm;
|
||||||
|
protected IdentityManagerFactory factory;
|
||||||
|
|
||||||
|
public ResourceModel(Tier tier, Agent agent, RealmModel realm, IdentityManagerFactory factory)
|
||||||
|
{
|
||||||
|
this.tier = tier;
|
||||||
|
this.agent = agent;
|
||||||
|
this.realm = realm;
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdentityManager getIdm()
|
||||||
|
{
|
||||||
|
return factory.createIdentityManager(tier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateResource()
|
||||||
|
{
|
||||||
|
getIdm().update(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return tier.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return (String)agent.getAttribute(RESOURCE_NAME).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
agent.setAttribute(new Attribute<String>(RESOURCE_NAME, name));
|
||||||
|
getIdm().update(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return agent.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
agent.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSurrogateAuthRequired()
|
||||||
|
{
|
||||||
|
return (Boolean)agent.getAttribute(RESOURCE_SURROGATE_AUTH).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurrogateAuthRequired(boolean surrogateAuthRequired)
|
||||||
|
{
|
||||||
|
agent.setAttribute(new Attribute<Boolean>(RESOURCE_SURROGATE_AUTH, surrogateAuthRequired));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Role> getRoles()
|
||||||
|
{
|
||||||
|
IdentityQuery<Role> query = getIdm().createIdentityQuery(Role.class);
|
||||||
|
query.setParameter(Role.PARTITION, tier);
|
||||||
|
return query.getResultList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoleMappings(User user)
|
||||||
|
{
|
||||||
|
RelationshipQuery<Grant> query = getIdm().createRelationshipQuery(Grant.class);
|
||||||
|
query.setParameter(Grant.ASSIGNEE, user);
|
||||||
|
List<Grant> grants = query.getResultList();
|
||||||
|
HashSet<String> set = new HashSet<String>();
|
||||||
|
for (Grant grant : grants)
|
||||||
|
{
|
||||||
|
if (grant.getRole().getPartition().getId().equals(tier.getId()))set.add(grant.getRole().getName());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addScope(Agent agent, String roleName)
|
||||||
|
{
|
||||||
|
IdentityManager idm = getIdm();
|
||||||
|
Role role = idm.getRole(roleName);
|
||||||
|
if (role == null) throw new RuntimeException("role not found");
|
||||||
|
ScopeRelationship scope = new ScopeRelationship();
|
||||||
|
scope.setClient(agent);
|
||||||
|
scope.setScope(role);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Set<String> getScope(Agent agent)
|
||||||
|
{
|
||||||
|
RelationshipQuery<ScopeRelationship> query = getIdm().createRelationshipQuery(ScopeRelationship.class);
|
||||||
|
query.setParameter(ScopeRelationship.CLIENT, agent);
|
||||||
|
List<ScopeRelationship> scope = query.getResultList();
|
||||||
|
HashSet<String> set = new HashSet<String>();
|
||||||
|
for (ScopeRelationship rel : scope)
|
||||||
|
{
|
||||||
|
if (rel.getScope().getPartition().getId().equals(tier.getId())) set.add(rel.getScope().getName());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
50
services/src/main/java/org/keycloak/services/model/ScopeRelationship.java
Executable file
50
services/src/main/java/org/keycloak/services/model/ScopeRelationship.java
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
import org.picketlink.idm.model.AbstractAttributedType;
|
||||||
|
import org.picketlink.idm.model.Agent;
|
||||||
|
import org.picketlink.idm.model.Relationship;
|
||||||
|
import org.picketlink.idm.model.Role;
|
||||||
|
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 ScopeRelationship extends AbstractAttributedType implements Relationship
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public static final RelationshipQueryParameter CLIENT = new RelationshipQueryParameter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "client";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected Agent client;
|
||||||
|
protected Role scope;
|
||||||
|
|
||||||
|
@IdentityProperty
|
||||||
|
public Agent getClient()
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(Agent client)
|
||||||
|
{
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@IdentityProperty
|
||||||
|
public Role getScope()
|
||||||
|
{
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScope(Role scope)
|
||||||
|
{
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
}
|
32
services/src/main/java/org/keycloak/services/model/UserCredentialModel.java
Executable file
32
services/src/main/java/org/keycloak/services/model/UserCredentialModel.java
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
package org.keycloak.services.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class UserCredentialModel
|
||||||
|
{
|
||||||
|
|
||||||
|
protected String type;
|
||||||
|
protected String value;
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
194
services/src/main/java/org/keycloak/services/model/data/RealmModel.java
Executable file
194
services/src/main/java/org/keycloak/services/model/data/RealmModel.java
Executable file
|
@ -0,0 +1,194 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
|
import org.jboss.resteasy.security.PemUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class RealmModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
protected String id;
|
||||||
|
protected String name;
|
||||||
|
protected long tokenLifespan = 3600 * 24; // one day
|
||||||
|
protected long accessCodeLifespan = 300; // 5 minutes
|
||||||
|
protected boolean enabled;
|
||||||
|
protected boolean sslNotRequired;
|
||||||
|
protected boolean cookieLoginAllowed;
|
||||||
|
protected String publicKeyPem;
|
||||||
|
protected String privateKeyPem;
|
||||||
|
protected volatile transient PublicKey publicKey;
|
||||||
|
protected volatile transient PrivateKey privateKey;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslNotRequired()
|
||||||
|
{
|
||||||
|
return sslNotRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslNotRequired(boolean sslNotRequired)
|
||||||
|
{
|
||||||
|
this.sslNotRequired = sslNotRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCookieLoginAllowed()
|
||||||
|
{
|
||||||
|
return cookieLoginAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookieLoginAllowed(boolean cookieLoginAllowed)
|
||||||
|
{
|
||||||
|
this.cookieLoginAllowed = cookieLoginAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTokenLifespan()
|
||||||
|
{
|
||||||
|
return tokenLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenLifespan(long tokenLifespan)
|
||||||
|
{
|
||||||
|
this.tokenLifespan = tokenLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAccessCodeLifespan()
|
||||||
|
{
|
||||||
|
return accessCodeLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessCodeLifespan(long accessCodeLifespan)
|
||||||
|
{
|
||||||
|
this.accessCodeLifespan = accessCodeLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKeyPem()
|
||||||
|
{
|
||||||
|
return publicKeyPem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKeyPem(String publicKeyPem)
|
||||||
|
{
|
||||||
|
this.publicKeyPem = publicKeyPem;
|
||||||
|
this.publicKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKeyPem()
|
||||||
|
{
|
||||||
|
return privateKeyPem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKeyPem(String privateKeyPem)
|
||||||
|
{
|
||||||
|
this.privateKeyPem = privateKeyPem;
|
||||||
|
this.privateKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublicKey getPublicKey()
|
||||||
|
{
|
||||||
|
if (publicKey != null) return publicKey;
|
||||||
|
if (publicKeyPem != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
publicKey = PemUtils.decodePublicKey(publicKeyPem);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKey(PublicKey publicKey)
|
||||||
|
{
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
PEMWriter pemWriter = new PEMWriter(writer);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pemWriter.writeObject(publicKey);
|
||||||
|
pemWriter.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String s = writer.toString();
|
||||||
|
this.publicKeyPem = PemUtils.removeBeginEnd(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrivateKey getPrivateKey()
|
||||||
|
{
|
||||||
|
if (privateKey != null) return privateKey;
|
||||||
|
if (privateKeyPem != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
privateKey = PemUtils.decodePrivateKey(privateKeyPem);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(PrivateKey privateKey)
|
||||||
|
{
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
PEMWriter pemWriter = new PEMWriter(writer);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pemWriter.writeObject(privateKey);
|
||||||
|
pemWriter.flush();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String s = writer.toString();
|
||||||
|
this.privateKeyPem = PemUtils.removeBeginEnd(s);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class RequiredCredentialModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
protected String id;
|
||||||
|
protected String type;
|
||||||
|
protected boolean input;
|
||||||
|
protected boolean secret;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInput()
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(boolean input)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecret()
|
||||||
|
{
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecret(boolean secret)
|
||||||
|
{
|
||||||
|
this.secret = secret;
|
||||||
|
}
|
||||||
|
}
|
46
services/src/main/java/org/keycloak/services/model/data/ResourceModel.java
Executable file
46
services/src/main/java/org/keycloak/services/model/data/ResourceModel.java
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class ResourceModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
protected String id;
|
||||||
|
protected String name;
|
||||||
|
protected boolean surrogateAuthRequired;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSurrogateAuthRequired()
|
||||||
|
{
|
||||||
|
return surrogateAuthRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurrogateAuthRequired(boolean surrogateAuthRequired)
|
||||||
|
{
|
||||||
|
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class RoleMappingModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
protected String id;
|
||||||
|
protected String userid;
|
||||||
|
protected Set<String> roles = new HashSet<String>();
|
||||||
|
protected Set<String> surrogateIds = new HashSet<String>();
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserid()
|
||||||
|
{
|
||||||
|
return userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserid(String userid)
|
||||||
|
{
|
||||||
|
this.userid = userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoles()
|
||||||
|
{
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Set<String> roles)
|
||||||
|
{
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getSurrogateIds()
|
||||||
|
{
|
||||||
|
return surrogateIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurrogateIds(Set<String> surrogateIds)
|
||||||
|
{
|
||||||
|
this.surrogateIds = surrogateIds;
|
||||||
|
}
|
||||||
|
}
|
35
services/src/main/java/org/keycloak/services/model/data/RoleModel.java
Executable file
35
services/src/main/java/org/keycloak/services/model/data/RoleModel.java
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class RoleModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
protected String id;
|
||||||
|
protected String name;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class ScopeMappingModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
protected String id;
|
||||||
|
protected String userid;
|
||||||
|
protected Set<String> roles = new HashSet<String>();
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserid()
|
||||||
|
{
|
||||||
|
return userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserid(String userid)
|
||||||
|
{
|
||||||
|
this.userid = userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoles()
|
||||||
|
{
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Set<String> roles)
|
||||||
|
{
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class UserAttributeModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
protected String id;
|
||||||
|
protected String name;
|
||||||
|
protected String value;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class UserCredentialModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
protected String id;
|
||||||
|
protected String type;
|
||||||
|
protected String value;
|
||||||
|
protected boolean hashed;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHashed()
|
||||||
|
{
|
||||||
|
return hashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashed(boolean hashed)
|
||||||
|
{
|
||||||
|
this.hashed = hashed;
|
||||||
|
}
|
||||||
|
}
|
46
services/src/main/java/org/keycloak/services/model/data/UserModel.java
Executable file
46
services/src/main/java/org/keycloak/services/model/data/UserModel.java
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
package org.keycloak.services.model.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class UserModel implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
protected String id;
|
||||||
|
protected String username;
|
||||||
|
protected boolean enabled;
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername()
|
||||||
|
{
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username)
|
||||||
|
{
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.logging.Logger;
|
||||||
|
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
|
||||||
|
import org.keycloak.services.model.RealmManager;
|
||||||
|
import org.keycloak.services.model.RealmModel;
|
||||||
|
import org.keycloak.services.model.RequiredCredentialModel;
|
||||||
|
import org.picketlink.idm.credential.Credentials;
|
||||||
|
import org.picketlink.idm.credential.Password;
|
||||||
|
import org.picketlink.idm.credential.TOTPCredentials;
|
||||||
|
import org.picketlink.idm.credential.UsernamePasswordCredentials;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class AuthenticationManager
|
||||||
|
{
|
||||||
|
protected Logger logger = Logger.getLogger(AuthenticationManager.class);
|
||||||
|
public static final String FORM_USERNAME = "username";
|
||||||
|
protected RealmManager adapter;
|
||||||
|
|
||||||
|
public AuthenticationManager(RealmManager adapter)
|
||||||
|
{
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean authenticate(RealmModel realm, User user, MultivaluedMap<String, String> formData)
|
||||||
|
{
|
||||||
|
String username = user.getLoginName();
|
||||||
|
Set<String> types = new HashSet<String>();
|
||||||
|
|
||||||
|
for (RequiredCredentialModel credential : realm.getRequiredCredentials())
|
||||||
|
{
|
||||||
|
types.add(credential.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.contains(RequiredCredentialRepresentation.PASSWORD))
|
||||||
|
{
|
||||||
|
String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD);
|
||||||
|
if (password == null)
|
||||||
|
{
|
||||||
|
logger.warn("Password not provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.contains(RequiredCredentialRepresentation.TOTP))
|
||||||
|
{
|
||||||
|
String token = formData.getFirst(RequiredCredentialRepresentation.TOTP);
|
||||||
|
if (token == null)
|
||||||
|
{
|
||||||
|
logger.warn("TOTP token not provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TOTPCredentials creds = new TOTPCredentials();
|
||||||
|
creds.setToken(token);
|
||||||
|
creds.setUsername(username);
|
||||||
|
creds.setPassword(new Password(password));
|
||||||
|
realm.getIdm().validateCredentials(creds);
|
||||||
|
if (creds.getStatus() != Credentials.Status.VALID)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, new Password(password));
|
||||||
|
realm.getIdm().validateCredentials(creds);
|
||||||
|
if (creds.getStatus() != Credentials.Status.VALID)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.warn("Do not know how to authenticate user");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
354
services/src/main/java/org/keycloak/services/service/RealmFactory.java
Executable file
354
services/src/main/java/org/keycloak/services/service/RealmFactory.java
Executable file
|
@ -0,0 +1,354 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.keycloak.services.model.RealmManager;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.services.model.RealmModel;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
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 RealmManager adapter;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
public RealmFactory(RealmManager adapter)
|
||||||
|
{
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
{
|
||||||
|
//verifyRealmRepresentation(rep);
|
||||||
|
|
||||||
|
RealmModel realm = adapter.create(rep.getRealm());
|
||||||
|
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.updateRealm();
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, User> userMap = new HashMap<String, User>();
|
||||||
|
return null;
|
||||||
|
}/*
|
||||||
|
RoleModel adminRole = identityManager.create(realm, "admin");
|
||||||
|
|
||||||
|
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials())
|
||||||
|
{
|
||||||
|
RequiredCredentialModel credential = new RequiredCredentialModel();
|
||||||
|
credential.setType(requiredCred.getType());
|
||||||
|
credential.setInput(requiredCred.isInput());
|
||||||
|
credential.setSecret(requiredCred.isSecret());
|
||||||
|
identityManager.create(realm, credential);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UserRepresentation userRep : rep.getUsers())
|
||||||
|
{
|
||||||
|
UserModel user = new UserModel();
|
||||||
|
user.setUsername(userRep.getUsername());
|
||||||
|
user.setEnabled(userRep.isEnabled());
|
||||||
|
user = identityManager.create(realm, user);
|
||||||
|
userMap.put(user.getUsername(), user);
|
||||||
|
if (userRep.getCredentials() != null)
|
||||||
|
{
|
||||||
|
for (UserRepresentation.Credential cred : userRep.getCredentials())
|
||||||
|
{
|
||||||
|
UserCredentialModel credential = new UserCredentialModel();
|
||||||
|
credential.setType(cred.getType());
|
||||||
|
credential.setValue(cred.getValue());
|
||||||
|
credential.setHashed(cred.isHashed());
|
||||||
|
identityManager.create(user, credential);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userRep.getAttributes() != null)
|
||||||
|
{
|
||||||
|
for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet())
|
||||||
|
{
|
||||||
|
UserAttributeModel attribute = new UserAttributeModel();
|
||||||
|
attribute.setName(entry.getKey());
|
||||||
|
attribute.setValue(entry.getValue());
|
||||||
|
identityManager.create(user, attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RoleMappingRepresentation mapping : rep.getRoleMappings())
|
||||||
|
{
|
||||||
|
RoleMappingModel roleMapping = createRoleMapping(userMap, mapping);
|
||||||
|
UserModel user = userMap.get(mapping.getUsername());
|
||||||
|
identityManager.create(realm, user, roleMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rep.getScopeMappings() != null)
|
||||||
|
{
|
||||||
|
for (ScopeMappingRepresentation scope : rep.getScopeMappings())
|
||||||
|
{
|
||||||
|
ScopeMappingModel scopeMapping = createScopeMapping(userMap, scope);
|
||||||
|
UserModel user = userMap.get(scope.getUsername());
|
||||||
|
identityManager.create(realm, user, scopeMapping);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rep.getResources() != null)
|
||||||
|
{
|
||||||
|
for (ResourceRepresentation resourceRep : rep.getResources())
|
||||||
|
{
|
||||||
|
ResourceModel resource = new ResourceModel();
|
||||||
|
resource.setName(resourceRep.getName());
|
||||||
|
resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
|
||||||
|
resource = identityManager.create(realm, resource);
|
||||||
|
if (resourceRep.getRoles() != null)
|
||||||
|
{
|
||||||
|
for (String role : resourceRep.getRoles())
|
||||||
|
{
|
||||||
|
RoleModel r = identityManager.create(realm, resource, role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resourceRep.getRoleMappings() != null)
|
||||||
|
{
|
||||||
|
for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings())
|
||||||
|
{
|
||||||
|
RoleMappingModel roleMapping = createRoleMapping(userMap, mapping);
|
||||||
|
UserModel user = userMap.get(mapping.getUsername());
|
||||||
|
identityManager.create(realm, resource, user, roleMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resourceRep.getScopeMappings() != null)
|
||||||
|
{
|
||||||
|
for (ScopeMappingRepresentation mapping : resourceRep.getScopeMappings())
|
||||||
|
{
|
||||||
|
ScopeMappingModel scopeMapping = createScopeMapping(userMap, mapping);
|
||||||
|
UserModel user = userMap.get(mapping.getUsername());
|
||||||
|
identityManager.create(realm, resource, user, scopeMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RoleMappingModel createRoleMapping(Map<String, UserModel> userMap, RoleMappingRepresentation mapping)
|
||||||
|
{
|
||||||
|
RoleMappingModel roleMapping = new RoleMappingModel();
|
||||||
|
UserModel user = userMap.get(mapping.getUsername());
|
||||||
|
roleMapping.setUserid(user.getId());
|
||||||
|
if (mapping.getSurrogates() != null)
|
||||||
|
{
|
||||||
|
for (String s : mapping.getSurrogates())
|
||||||
|
{
|
||||||
|
UserModel surrogate = userMap.get(s);
|
||||||
|
roleMapping.getSurrogateIds().add(surrogate.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String role : mapping.getRoles())
|
||||||
|
{
|
||||||
|
roleMapping.getRoles().add(role);
|
||||||
|
}
|
||||||
|
return roleMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ScopeMappingModel createScopeMapping(Map<String, UserModel> userMap, ScopeMappingRepresentation mapping)
|
||||||
|
{
|
||||||
|
ScopeMappingModel scopeMapping = new ScopeMappingModel();
|
||||||
|
UserModel user = userMap.get(mapping.getUsername());
|
||||||
|
scopeMapping.setUserid(user.getId());
|
||||||
|
for (String role : mapping.getRoles())
|
||||||
|
{
|
||||||
|
scopeMapping.getRoles().add(role);
|
||||||
|
}
|
||||||
|
return scopeMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void verifyRealmRepresentation(RealmRepresentation rep)
|
||||||
|
{
|
||||||
|
if (rep.getUsers() == null)
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("No realm admin users defined for realm").type("text/plain").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rep.getRequiredCredentials() == null)
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("Realm credential requirements not defined").type("text/plain").build());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rep.getRoleMappings() == null)
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("No realm admin users defined for realm").type("text/plain").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, UserRepresentation> userReps = new HashMap<String, UserRepresentation>();
|
||||||
|
for (UserRepresentation userRep : rep.getUsers()) userReps.put(userRep.getUsername(), userRep);
|
||||||
|
|
||||||
|
// make sure there is a user that has admin privileges for the realm
|
||||||
|
Set<UserRepresentation> admins = new HashSet<UserRepresentation>();
|
||||||
|
for (RoleMappingRepresentation mapping : rep.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());
|
||||||
|
|
||||||
|
}
|
||||||
|
for (String role : mapping.getRoles())
|
||||||
|
{
|
||||||
|
if (!role.equals("admin"))
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("There is only an 'admin' role for realms").type("text/plain").build());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
admins.add(userReps.get(mapping.getUsername()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (admins.size() == 0)
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("No realm admin users defined for realm").type("text/plain").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// override enabled to false if user does not have at least all of browser or client credentials
|
||||||
|
for (UserRepresentation userRep : rep.getUsers())
|
||||||
|
{
|
||||||
|
if (!userRep.isEnabled())
|
||||||
|
{
|
||||||
|
admins.remove(userRep);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (userRep.getCredentials() == null)
|
||||||
|
{
|
||||||
|
admins.remove(userRep);
|
||||||
|
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);
|
||||||
|
admins.remove(userRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admins.size() == 0)
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("No realm admin users are enabled or have appropriate credentials").type("text/plain").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
if (mapping.getSurrogates() != null)
|
||||||
|
{
|
||||||
|
for (String surrogate : mapping.getSurrogates())
|
||||||
|
{
|
||||||
|
if (!userReps.containsKey(surrogate))
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("No users declared for role mapping surrogate").type("text/plain").build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String role : mapping.getRoles())
|
||||||
|
{
|
||||||
|
if (!resourceRep.getRoles().contains(role))
|
||||||
|
{
|
||||||
|
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
|
||||||
|
.entity("No resource role for role mapping").type("text/plain").build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
158
services/src/main/java/org/keycloak/services/service/RealmResource.java
Executable file
158
services/src/main/java/org/keycloak/services/service/RealmResource.java
Executable file
|
@ -0,0 +1,158 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.keycloak.services.IdentityManagerAdapter;
|
||||||
|
import org.keycloak.services.model.data.RealmModel;
|
||||||
|
import org.jboss.resteasy.logging.Logger;
|
||||||
|
import org.keycloak.representations.idm.PublishedRealmRepresentation;
|
||||||
|
|
||||||
|
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.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.GenericEntity;
|
||||||
|
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.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Path("/")
|
||||||
|
public class RealmResource
|
||||||
|
{
|
||||||
|
protected Logger logger = Logger.getLogger(RealmResource.class);
|
||||||
|
protected IdentityManagerAdapter identityManager;
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
public RealmResource(IdentityManagerAdapter identityManager)
|
||||||
|
{
|
||||||
|
this.identityManager = identityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("realms/{realm}")
|
||||||
|
@Produces("application/json")
|
||||||
|
public PublishedRealmRepresentation getRealm(@PathParam("realm") String id)
|
||||||
|
{
|
||||||
|
RealmModel realm = identityManager.getRealm(id);
|
||||||
|
if (realm == null)
|
||||||
|
{
|
||||||
|
logger.debug("realm not found");
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
return realmRep(realm, uriInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("realms/{realm}.html")
|
||||||
|
@Produces("text/html")
|
||||||
|
public String getRealmHtml(@PathParam("realm") String id)
|
||||||
|
{
|
||||||
|
RealmModel realm = identityManager.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();
|
||||||
|
|
||||||
|
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>public key: ").append(realm.getPublicKeyPem()).append("</p>");
|
||||||
|
html.append("</body></html>");
|
||||||
|
|
||||||
|
return html.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("realms")
|
||||||
|
@Produces("application/json")
|
||||||
|
public Response getRealmsByName(@QueryParam("name") String name)
|
||||||
|
{
|
||||||
|
if (name == null) return Response.noContent().build();
|
||||||
|
List<RealmModel> realms = identityManager.getRealmsByName(name);
|
||||||
|
if (realms.size() == 0) return Response.noContent().build();
|
||||||
|
|
||||||
|
List<PublishedRealmRepresentation> list = new ArrayList<PublishedRealmRepresentation>();
|
||||||
|
for (RealmModel realm : realms)
|
||||||
|
{
|
||||||
|
list.add(realmRep(realm, uriInfo));
|
||||||
|
}
|
||||||
|
GenericEntity<List<PublishedRealmRepresentation>> entity = new GenericEntity<List<PublishedRealmRepresentation>>(list){};
|
||||||
|
return Response.ok(entity).type(MediaType.APPLICATION_JSON_TYPE).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("realms.html")
|
||||||
|
@Produces("text/html")
|
||||||
|
public String getRealmsByNameHtml(@QueryParam("name") String name)
|
||||||
|
{
|
||||||
|
if (name == null) return "<html><body><h1>No realms with that name</h1></body></html>";
|
||||||
|
List<RealmModel> realms = identityManager.getRealmsByName(name);
|
||||||
|
if (realms.size() == 0) return "<html><body><h1>No realms with that name</h1></body></html>";
|
||||||
|
if (realms.size() == 1) return realmHtml(realms.get(0));
|
||||||
|
|
||||||
|
StringBuffer html = new StringBuffer();
|
||||||
|
html.append("<html><body><h1>Realms</h1>");
|
||||||
|
for (RealmModel realm : realms)
|
||||||
|
{
|
||||||
|
html.append("<p><a href=\"").append(uriInfo.getBaseUriBuilder().path("realms").path(realm.getId() + ".html"))
|
||||||
|
.append("\">").append(realm.getId()).append("</a></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);
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.services.model.RealmManager;
|
||||||
|
import org.keycloak.services.model.RealmModel;
|
||||||
|
import org.keycloak.services.model.UserCredentialModel;
|
||||||
|
import org.picketlink.idm.model.Realm;
|
||||||
|
import org.picketlink.idm.model.SimpleUser;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Path("/registrations")
|
||||||
|
public class RegistrationService
|
||||||
|
{
|
||||||
|
protected RealmManager adapter;
|
||||||
|
protected RealmModel defaultRealm;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
public RegistrationService(RealmManager adapter)
|
||||||
|
{
|
||||||
|
this.adapter = adapter;
|
||||||
|
defaultRealm = adapter.getRealm(Realm.DEFAULT_REALM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response register(UserRepresentation newUser)
|
||||||
|
{
|
||||||
|
User user = defaultRealm.getIdm().getUser(newUser.getUsername());
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
return Response.status(400).type("text/plain").entity("user exists").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
user = new SimpleUser(newUser.getUsername());
|
||||||
|
defaultRealm.getIdm().add(user);
|
||||||
|
for (UserRepresentation.Credential cred : newUser.getCredentials())
|
||||||
|
{
|
||||||
|
UserCredentialModel credModel = new UserCredentialModel();
|
||||||
|
credModel.setType(cred.getType());
|
||||||
|
credModel.setValue(cred.getValue());
|
||||||
|
defaultRealm.updateCredential(user, credModel);
|
||||||
|
}
|
||||||
|
URI uri = uriInfo.getBaseUriBuilder().path(RealmFactory.class).path(user.getLoginName()).build();
|
||||||
|
return Response.created(uri).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.infinispan.Cache;
|
||||||
|
import org.infinispan.manager.DefaultCacheManager;
|
||||||
|
import org.keycloak.SkeletonKeyContextResolver;
|
||||||
|
|
||||||
|
import javax.ws.rs.ApplicationPath;
|
||||||
|
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 $
|
||||||
|
*/
|
||||||
|
@ApplicationPath("/")
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
176
services/src/main/java/org/keycloak/services/service/TokenManager.java
Executable file
176
services/src/main/java/org/keycloak/services/service/TokenManager.java
Executable file
|
@ -0,0 +1,176 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.jose.Base64Url;
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSBuilder;
|
||||||
|
import org.jboss.resteasy.jwt.JsonSerialization;
|
||||||
|
import org.keycloak.representations.SkeletonKeyScope;
|
||||||
|
import org.keycloak.representations.SkeletonKeyToken;
|
||||||
|
import org.keycloak.services.model.RealmManager;
|
||||||
|
import org.keycloak.services.model.RealmModel;
|
||||||
|
import org.keycloak.services.model.ResourceModel;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
|
||||||
|
import javax.ws.rs.ForbiddenException;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class TokenManager
|
||||||
|
{
|
||||||
|
protected RealmManager adapter;
|
||||||
|
|
||||||
|
public TokenManager(RealmManager adapter)
|
||||||
|
{
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user)
|
||||||
|
{
|
||||||
|
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||||
|
token.id(adapter.generateId());
|
||||||
|
token.principal(user.getLoginName());
|
||||||
|
token.audience(realm.getName());
|
||||||
|
token.issuedNow();
|
||||||
|
token.issuedFor(client.getLoginName());
|
||||||
|
if (realm.getTokenLifespan() > 0)
|
||||||
|
{
|
||||||
|
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||||
|
}
|
||||||
|
Map<String, ResourceModel> resourceMap = realm.getResourceMap();
|
||||||
|
|
||||||
|
for (String res : scope.keySet())
|
||||||
|
{
|
||||||
|
ResourceModel resource = resourceMap.get(res);
|
||||||
|
Set<String> scopeMapping = resource.getScope(client);
|
||||||
|
Set<String> roleMapping = resource.getRoleMappings(user);
|
||||||
|
SkeletonKeyToken.Access access = token.addAccess(resource.getName());
|
||||||
|
for (String role : scope.get(res))
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
access.addRole(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyToken createScopedToken(String scopeParam, RealmModel realm, User client, User user)
|
||||||
|
{
|
||||||
|
SkeletonKeyScope scope = decodeScope(scopeParam);
|
||||||
|
return createScopedToken(scope, realm, client, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyToken createLoginToken(RealmModel realm, User client, User user)
|
||||||
|
{
|
||||||
|
Set<String> mapping = realm.getScope(client);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
SkeletonKeyToken token = createAccessToken(realm, user);
|
||||||
|
token.issuedFor(client.getLoginName());
|
||||||
|
return token;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyScope decodeScope(String scopeParam)
|
||||||
|
{
|
||||||
|
SkeletonKeyScope scope = null;
|
||||||
|
byte[] bytes = Base64Url.decode(scopeParam);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SkeletonKeyToken createAccessToken(RealmModel realm, User user)
|
||||||
|
{
|
||||||
|
List<ResourceModel> resources = realm.getResources();
|
||||||
|
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||||
|
token.id(adapter.generateId());
|
||||||
|
token.issuedNow();
|
||||||
|
token.principal(user.getLoginName());
|
||||||
|
token.audience(realm.getId());
|
||||||
|
if (realm.getTokenLifespan() > 0)
|
||||||
|
{
|
||||||
|
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> realmMapping = realm.getRoleMappings(user);
|
||||||
|
|
||||||
|
if (realmMapping != null && realmMapping.size() > 0)
|
||||||
|
{
|
||||||
|
SkeletonKeyToken.Access access = new SkeletonKeyToken.Access();
|
||||||
|
for (String role : realmMapping)
|
||||||
|
{
|
||||||
|
access.addRole(role);
|
||||||
|
}
|
||||||
|
token.setRealmAccess(access);
|
||||||
|
}
|
||||||
|
if (resources != null)
|
||||||
|
{
|
||||||
|
for (ResourceModel resource : resources)
|
||||||
|
{
|
||||||
|
Set<String> mapping = resource.getRoleMappings(user);
|
||||||
|
if (mapping == null) continue;
|
||||||
|
SkeletonKeyToken.Access access = token.addAccess(resource.getName())
|
||||||
|
.verifyCaller(resource.isSurrogateAuthRequired());
|
||||||
|
for (String role : mapping)
|
||||||
|
{
|
||||||
|
access.addRole(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonKeyToken createIdentityToken(RealmModel realm, String username)
|
||||||
|
{
|
||||||
|
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||||
|
token.id(adapter.generateId());
|
||||||
|
token.issuedNow();
|
||||||
|
token.principal(username);
|
||||||
|
token.audience(realm.getId());
|
||||||
|
if (realm.getTokenLifespan() > 0)
|
||||||
|
{
|
||||||
|
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeToken(RealmModel realm, SkeletonKeyToken token)
|
||||||
|
{
|
||||||
|
byte[] tokenBytes = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String encodedToken = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(realm.getPrivateKey());
|
||||||
|
return encodedToken;
|
||||||
|
}
|
||||||
|
}
|
593
services/src/main/java/org/keycloak/services/service/TokenService.java
Executable file
593
services/src/main/java/org/keycloak/services/service/TokenService.java
Executable file
|
@ -0,0 +1,593 @@
|
||||||
|
package org.keycloak.services.service;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.jose.Base64Url;
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSBuilder;
|
||||||
|
import org.jboss.resteasy.jose.jws.JWSInput;
|
||||||
|
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
|
||||||
|
import org.jboss.resteasy.jwt.JsonSerialization;
|
||||||
|
import org.jboss.resteasy.logging.Logger;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
import org.keycloak.representations.SkeletonKeyScope;
|
||||||
|
import org.keycloak.representations.SkeletonKeyToken;
|
||||||
|
import org.keycloak.services.model.RealmManager;
|
||||||
|
import org.keycloak.services.model.RealmModel;
|
||||||
|
import org.keycloak.services.model.RequiredCredentialModel;
|
||||||
|
import org.keycloak.services.model.ResourceModel;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
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.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.SecurityContext;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import javax.ws.rs.ext.Providers;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Path("/realms")
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
@Context
|
||||||
|
protected Providers providers;
|
||||||
|
@Context
|
||||||
|
protected SecurityContext securityContext;
|
||||||
|
@Context
|
||||||
|
protected HttpHeaders headers;
|
||||||
|
|
||||||
|
private static AtomicLong counter = new AtomicLong(1);
|
||||||
|
private static String generateId()
|
||||||
|
{
|
||||||
|
return counter.getAndIncrement() + "." + UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenService(RealmManager adapter)
|
||||||
|
{
|
||||||
|
this.adapter = adapter;
|
||||||
|
this.tokenManager = new TokenManager(adapter);
|
||||||
|
this.authManager = new AuthenticationManager(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{realm}/grants/identity-token")
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Response identityTokenGrant(@PathParam("realm") String realmId, MultivaluedMap<String, String> form)
|
||||||
|
{
|
||||||
|
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
|
if (username == null)
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("No user");
|
||||||
|
}
|
||||||
|
RealmModel realm = adapter.getRealm(realmId);
|
||||||
|
if (realm == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Realm not found");
|
||||||
|
}
|
||||||
|
if (!realm.isEnabled())
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Disabled realm");
|
||||||
|
}
|
||||||
|
User user = realm.getIdm().getUser(username);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("No user");
|
||||||
|
}
|
||||||
|
if (!user.isEnabled())
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Disabled user.");
|
||||||
|
}
|
||||||
|
SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username);
|
||||||
|
String encoded = tokenManager.encodeToken(realm, token);
|
||||||
|
AccessTokenResponse res = accessTokenResponse(token, encoded);
|
||||||
|
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{realm}/grants/access")
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Response accessTokenGrant(@PathParam("realm") String realmId, MultivaluedMap<String, String> form)
|
||||||
|
{
|
||||||
|
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
|
if (username == null)
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("No user");
|
||||||
|
}
|
||||||
|
RealmModel realm = adapter.getRealm(realmId);
|
||||||
|
if (realm == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Realm not found");
|
||||||
|
}
|
||||||
|
if (!realm.isEnabled())
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Disabled realm");
|
||||||
|
}
|
||||||
|
User user = realm.getIdm().getUser(username);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("No user");
|
||||||
|
}
|
||||||
|
if (!user.isEnabled())
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Disabled user.");
|
||||||
|
}
|
||||||
|
if (authManager.authenticate(realm, user, form))
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Auth failed");
|
||||||
|
}
|
||||||
|
SkeletonKeyToken token = tokenManager.createAccessToken(realm, user);
|
||||||
|
String encoded = tokenManager.encodeToken(realm, token);
|
||||||
|
AccessTokenResponse res = accessTokenResponse(token, encoded);
|
||||||
|
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{realm}/auth/request/login")
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
public Response login(@PathParam("realm") String realmId,
|
||||||
|
MultivaluedMap<String, String> formData)
|
||||||
|
{
|
||||||
|
String clientId = formData.getFirst("client_id");
|
||||||
|
String scopeParam = formData.getFirst("scope");
|
||||||
|
String state = formData.getFirst("state");
|
||||||
|
String redirect = formData.getFirst("redirect_uri");
|
||||||
|
|
||||||
|
RealmModel realm = adapter.getRealm(realmId);
|
||||||
|
if (realm == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Realm not found");
|
||||||
|
}
|
||||||
|
if (!realm.isEnabled())
|
||||||
|
{
|
||||||
|
return Response.ok("Realm not enabled").type("text/html").build();
|
||||||
|
}
|
||||||
|
User client = realm.getIdm().getUser(clientId);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("No client");
|
||||||
|
}
|
||||||
|
if (!client.isEnabled())
|
||||||
|
{
|
||||||
|
return Response.ok("Requester not enabled").type("text/html").build();
|
||||||
|
}
|
||||||
|
String username = formData.getFirst("username");
|
||||||
|
User user = realm.getIdm().getUser(username);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
logger.debug("user not found");
|
||||||
|
return loginForm("Not valid user", redirect, clientId, scopeParam, state, realm, client);
|
||||||
|
}
|
||||||
|
if (!user.isEnabled())
|
||||||
|
{
|
||||||
|
return Response.ok("Your account is not enabled").type("text/html").build();
|
||||||
|
|
||||||
|
}
|
||||||
|
boolean authenticated = authManager.authenticate(realm, user, formData);
|
||||||
|
if (!authenticated) return loginForm("Unable to authenticate, try again", redirect, clientId, scopeParam, state, realm, client);
|
||||||
|
|
||||||
|
SkeletonKeyToken token = null;
|
||||||
|
if (scopeParam != null) token = tokenManager.createScopedToken(scopeParam, realm, client, user);
|
||||||
|
else token = tokenManager.createLoginToken(realm, client, user);
|
||||||
|
|
||||||
|
AccessCode code = new AccessCode();
|
||||||
|
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
|
||||||
|
code.setToken(token);
|
||||||
|
code.setClient(client);
|
||||||
|
synchronized (accessCodeMap)
|
||||||
|
{
|
||||||
|
accessCodeMap.put(code.getId(), code);
|
||||||
|
}
|
||||||
|
String accessCode = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode);
|
||||||
|
if (state != null) redirectUri.queryParam("state", state);
|
||||||
|
return Response.status(302).location(redirectUri.build()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{realm}/access/codes")
|
||||||
|
@POST
|
||||||
|
@Produces("application/json")
|
||||||
|
public Response accessRequest(@PathParam("realm") String realmId,
|
||||||
|
MultivaluedMap<String, String> formData)
|
||||||
|
{
|
||||||
|
RealmModel realm = adapter.getRealm(realmId);
|
||||||
|
if (realm == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Realm not found");
|
||||||
|
}
|
||||||
|
if (!realm.isEnabled())
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Realm not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
String code = formData.getFirst("code");
|
||||||
|
if (code == null)
|
||||||
|
{
|
||||||
|
logger.debug("code not specified");
|
||||||
|
Map<String, String> error = new HashMap<String, String>();
|
||||||
|
error.put("error", "invalid_request");
|
||||||
|
error.put("error_description", "code not specified");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||||
|
|
||||||
|
}
|
||||||
|
String client_id = formData.getFirst("client_id");
|
||||||
|
if (client_id == null)
|
||||||
|
{
|
||||||
|
logger.debug("client_id not specified");
|
||||||
|
Map<String, String> error = new HashMap<String, String>();
|
||||||
|
error.put("error", "invalid_request");
|
||||||
|
error.put("error_description", "client_id not specified");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||||
|
}
|
||||||
|
User client = realm.getIdm().getUser(client_id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
logger.debug("Could not find user");
|
||||||
|
Map<String, String> error = new HashMap<String, String>();
|
||||||
|
error.put("error", "invalid_client");
|
||||||
|
error.put("error_description", "Could not find user");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.isEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("user is not enabled");
|
||||||
|
Map<String, String> error = new HashMap<String, String>();
|
||||||
|
error.put("error", "invalid_client");
|
||||||
|
error.put("error_description", "User is not enabled");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean authenticated = authManager.authenticate(realm, client, formData);
|
||||||
|
if (!authenticated)
|
||||||
|
{
|
||||||
|
Map<String, String> error = new HashMap<String, String>();
|
||||||
|
error.put("error", "unauthorized_client");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
JWSInput input = new JWSInput(code, providers);
|
||||||
|
boolean verifiedCode = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
logger.debug("Failed to verify signature", ignored);
|
||||||
|
}
|
||||||
|
if (!verifiedCode)
|
||||||
|
{
|
||||||
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
|
res.put("error", "invalid_grant");
|
||||||
|
res.put("error_description", "Unable to verify code signature");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
|
||||||
|
}
|
||||||
|
String key = input.readContent(String.class);
|
||||||
|
AccessCode accessCode = null;
|
||||||
|
synchronized (accessCodeMap)
|
||||||
|
{
|
||||||
|
accessCode = accessCodeMap.remove(key);
|
||||||
|
}
|
||||||
|
if (accessCode == null)
|
||||||
|
{
|
||||||
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
|
res.put("error", "invalid_grant");
|
||||||
|
res.put("error_description", "Code not found");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
|
||||||
|
}
|
||||||
|
if (accessCode.isExpired())
|
||||||
|
{
|
||||||
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
|
res.put("error", "invalid_grant");
|
||||||
|
res.put("error_description", "Code is expired");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
|
||||||
|
}
|
||||||
|
if (!accessCode.getToken().isActive())
|
||||||
|
{
|
||||||
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
|
res.put("error", "invalid_grant");
|
||||||
|
res.put("error_description", "Token expired");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
|
||||||
|
}
|
||||||
|
if (!client.getId().equals(accessCode.getClient().getId()))
|
||||||
|
{
|
||||||
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
|
res.put("error", "invalid_grant");
|
||||||
|
res.put("error_description", "Auth error");
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
|
||||||
|
}
|
||||||
|
AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
|
||||||
|
return Response.ok(res).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token)
|
||||||
|
{
|
||||||
|
byte[] tokenBytes = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String encodedToken = new JWSBuilder()
|
||||||
|
.content(tokenBytes)
|
||||||
|
.rsa256(privateKey);
|
||||||
|
|
||||||
|
return accessTokenResponse(token, encodedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken)
|
||||||
|
{
|
||||||
|
AccessTokenResponse res = new AccessTokenResponse();
|
||||||
|
res.setToken(encodedToken);
|
||||||
|
res.setTokenType("bearer");
|
||||||
|
if (token.getExpiration() != 0)
|
||||||
|
{
|
||||||
|
long time = token.getExpiration() - (System.currentTimeMillis() / 1000);
|
||||||
|
res.setExpiresIn(time);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{realm}/auth/request")
|
||||||
|
@GET
|
||||||
|
public Response requestAccessCode(@PathParam("realm") String realmId,
|
||||||
|
@QueryParam("response_type") String responseType,
|
||||||
|
@QueryParam("redirect_uri") String redirect,
|
||||||
|
@QueryParam("client_id") String clientId,
|
||||||
|
@QueryParam("scope") String scopeParam,
|
||||||
|
@QueryParam("state") String state)
|
||||||
|
{
|
||||||
|
RealmModel realm = adapter.getRealm(realmId);
|
||||||
|
if (realm == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Realm not found");
|
||||||
|
}
|
||||||
|
if (!realm.isEnabled())
|
||||||
|
{
|
||||||
|
throw new NotAuthorizedException("Realm not enabled");
|
||||||
|
}
|
||||||
|
User client = realm.getIdm().getUser(clientId);
|
||||||
|
if (client == null)
|
||||||
|
return Response.ok("<h1>Security Alert</h1><p>Unknown client trying to get access to your account.</p>").type("text/html").build();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
StringBuffer html = new StringBuffer();
|
||||||
|
if (scopeParam != null)
|
||||||
|
{
|
||||||
|
html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>");
|
||||||
|
if (validationError != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(1000); // put in a delay
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
html.append("<p/><p><b>").append(validationError).append("</b></p>");
|
||||||
|
}
|
||||||
|
html.append("<p>A Third Party is requesting access to the following resources</p>");
|
||||||
|
html.append("<table>");
|
||||||
|
SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam);
|
||||||
|
Map<String, ResourceModel> resourceMap = realm.getResourceMap();
|
||||||
|
|
||||||
|
for (String res : scope.keySet())
|
||||||
|
{
|
||||||
|
ResourceModel resource = resourceMap.get(res);
|
||||||
|
html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>");
|
||||||
|
Set<String> scopeMapping = resource.getScope(client);
|
||||||
|
for (String role : scope.get(res))
|
||||||
|
{
|
||||||
|
html.append(" ").append(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html.append("</td></tr>");
|
||||||
|
}
|
||||||
|
html.append("</table><p>To Authorize, please login below</p>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Set<String> scopeMapping = realm.getScope(client);
|
||||||
|
if (scopeMapping.contains("*"))
|
||||||
|
{
|
||||||
|
html.append("<h1>Login For ").append(realm.getName()).append(" Realm</h1>");
|
||||||
|
if (validationError != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(1000); // put in a delay
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
html.append("<p/><p><b>").append(validationError).append("</b></p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
html.append("<h1>Grant Request For ").append(realm.getName()).append(" Realm</h1>");
|
||||||
|
if (validationError != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(1000); // put in a delay
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
html.append("<p/><p><b>").append(validationError).append("</b></p>");
|
||||||
|
}
|
||||||
|
SkeletonKeyScope scope = new SkeletonKeyScope();
|
||||||
|
List<ResourceModel> resources = realm.getResources();
|
||||||
|
boolean found = false;
|
||||||
|
for (ResourceModel resource : resources)
|
||||||
|
{
|
||||||
|
Set<String> resourceScope = resource.getScope(client);
|
||||||
|
if (resourceScope == null) continue;
|
||||||
|
if (resourceScope.size() == 0) continue;
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
html.append("<p>A Third Party is requesting access to the following resources</p>");
|
||||||
|
html.append("<table>");
|
||||||
|
}
|
||||||
|
html.append("<tr><td><b>Resource: </b>").append(resource.getName()).append("</td><td><b>Roles:</b>");
|
||||||
|
// todo add description of role
|
||||||
|
for (String role : resourceScope)
|
||||||
|
{
|
||||||
|
html.append(" ").append(role);
|
||||||
|
scope.add(resource.getName(), role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
return Response.ok("<h1>Security Alert</h1><p>Known client not authorized to access this realm.</p>").type("text/html").build();
|
||||||
|
}
|
||||||
|
html.append("</table>");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String json = JsonSerialization.toString(scope, false);
|
||||||
|
scopeParam = Base64Url.encode(json.getBytes("UTF-8"));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UriBuilder formActionUri = uriInfo.getBaseUriBuilder().path(TokenService.class).path(TokenService.class, "login");
|
||||||
|
String action = formActionUri.build(realm.getId()).toString();
|
||||||
|
html.append("<form action=\"").append(action).append("\" method=\"POST\">");
|
||||||
|
html.append("Username: <input type=\"text\" name=\"username\" size=\"20\"><br>");
|
||||||
|
|
||||||
|
for (RequiredCredentialModel credential : realm.getRequiredCredentials())
|
||||||
|
{
|
||||||
|
if (!credential.isInput()) continue;
|
||||||
|
html.append(credential.getType()).append(": ");
|
||||||
|
if (credential.isSecret())
|
||||||
|
{
|
||||||
|
html.append("<input type=\"password\" name=\"").append(credential.getType()).append("\" size=\"20\"><br>");
|
||||||
|
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
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("\">");
|
||||||
|
if (scopeParam != null)
|
||||||
|
{
|
||||||
|
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("\">");
|
||||||
|
html.append("<input type=\"hidden\" name=\"redirect_uri\" value=\"").append(redirect).append("\">");
|
||||||
|
html.append("<input type=\"submit\" value=\"");
|
||||||
|
if (scopeParam == null) html.append("Login");
|
||||||
|
else html.append("Grant Access");
|
||||||
|
html.append("\">");
|
||||||
|
html.append("</form>");
|
||||||
|
return Response.ok(html.toString()).type("text/html").build();
|
||||||
|
}
|
||||||
|
}
|
159
services/src/test/java/org/keycloak/test/AdapterTest.java
Executable file
159
services/src/test/java/org/keycloak/test/AdapterTest.java
Executable file
|
@ -0,0 +1,159 @@
|
||||||
|
package org.keycloak.test;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
|
||||||
|
import org.keycloak.services.model.RealmManager;
|
||||||
|
import org.keycloak.services.model.RealmModel;
|
||||||
|
import org.keycloak.services.model.RealmResourceRelationship;
|
||||||
|
import org.keycloak.services.model.RequiredCredentialModel;
|
||||||
|
import org.keycloak.services.model.RequiredCredentialRelationship;
|
||||||
|
import org.keycloak.services.model.ScopeRelationship;
|
||||||
|
import org.keycloak.services.model.UserCredentialModel;
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.config.IdentityConfigurationBuilder;
|
||||||
|
import org.picketlink.idm.credential.Credentials;
|
||||||
|
import org.picketlink.idm.credential.Password;
|
||||||
|
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.SimpleRole;
|
||||||
|
import org.picketlink.idm.model.SimpleUser;
|
||||||
|
import org.picketlink.idm.model.User;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class AdapterTest
|
||||||
|
{
|
||||||
|
private static IdentityManagerFactory factory;
|
||||||
|
public static final String WORKING_DIRECTORY = "/tmp/keycloak";
|
||||||
|
public RealmManager adapter;
|
||||||
|
public RealmModel realmModel;
|
||||||
|
@Before
|
||||||
|
public void before() throws Exception
|
||||||
|
{
|
||||||
|
after();
|
||||||
|
factory = createFactory();
|
||||||
|
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
|
||||||
|
public void after() throws Exception
|
||||||
|
{
|
||||||
|
File file = new File(WORKING_DIRECTORY);
|
||||||
|
FileUtils.delete(file);
|
||||||
|
Thread.sleep(10); // my windows machine seems to have delays on deleting files sometimes
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test1CreateRealm() throws Exception
|
||||||
|
{
|
||||||
|
realmModel = adapter.create("JUGGLER");
|
||||||
|
realmModel.setAccessCodeLifespan(100);
|
||||||
|
realmModel.setCookieLoginAllowed(true);
|
||||||
|
realmModel.setEnabled(true);
|
||||||
|
realmModel.setName("JUGGLER");
|
||||||
|
realmModel.setPrivateKeyPem("0234234");
|
||||||
|
realmModel.setPublicKeyPem("0234234");
|
||||||
|
realmModel.setTokenLifespan(1000);
|
||||||
|
realmModel.updateRealm();
|
||||||
|
|
||||||
|
System.out.println(realmModel.getId());
|
||||||
|
realmModel = adapter.getRealm(realmModel.getId());
|
||||||
|
Assert.assertNotNull(realmModel);
|
||||||
|
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
|
||||||
|
Assert.assertEquals(realmModel.getTokenLifespan(), 1000);
|
||||||
|
Assert.assertEquals(realmModel.isEnabled(), true);
|
||||||
|
Assert.assertEquals(realmModel.getName(), "JUGGLER");
|
||||||
|
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
|
||||||
|
Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test2RequiredCredential() throws Exception
|
||||||
|
{
|
||||||
|
test1CreateRealm();
|
||||||
|
RequiredCredentialModel creds = new RequiredCredentialModel();
|
||||||
|
creds.setSecret(true);
|
||||||
|
creds.setType(RequiredCredentialRepresentation.PASSWORD);
|
||||||
|
creds.setInput(true);
|
||||||
|
realmModel.addRequiredCredential(creds);
|
||||||
|
creds = new RequiredCredentialModel();
|
||||||
|
creds.setSecret(true);
|
||||||
|
creds.setType(RequiredCredentialRepresentation.TOTP);
|
||||||
|
creds.setInput(true);
|
||||||
|
realmModel.addRequiredCredential(creds);
|
||||||
|
List<RequiredCredentialModel> storedCreds = realmModel.getRequiredCredentials();
|
||||||
|
Assert.assertEquals(2, storedCreds.size());
|
||||||
|
boolean totp = false;
|
||||||
|
boolean password = false;
|
||||||
|
for (RequiredCredentialModel cred : storedCreds)
|
||||||
|
{
|
||||||
|
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true;
|
||||||
|
else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true;
|
||||||
|
}
|
||||||
|
Assert.assertTrue(totp);
|
||||||
|
Assert.assertTrue(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCredentialValidation() throws Exception
|
||||||
|
{
|
||||||
|
test1CreateRealm();
|
||||||
|
User user = new SimpleUser("bburke");
|
||||||
|
realmModel.getIdm().add(user);
|
||||||
|
UserCredentialModel cred = new UserCredentialModel();
|
||||||
|
cred.setType(RequiredCredentialRepresentation.PASSWORD);
|
||||||
|
cred.setValue("geheim");
|
||||||
|
realmModel.updateCredential(user, cred);
|
||||||
|
IdentityManager idm = realmModel.getIdm();
|
||||||
|
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user.getLoginName(), new Password("geheim"));
|
||||||
|
idm.validateCredentials(creds);
|
||||||
|
Assert.assertEquals(creds.getStatus(), Credentials.Status.VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRoles() throws Exception
|
||||||
|
{
|
||||||
|
test1CreateRealm();
|
||||||
|
IdentityManager idm = realmModel.getIdm();
|
||||||
|
idm.add(new SimpleRole("admin"));
|
||||||
|
idm.add(new SimpleRole("user"));
|
||||||
|
List<Role> roles = realmModel.getRoles();
|
||||||
|
Assert.assertEquals(2, roles.size());
|
||||||
|
SimpleUser user = new SimpleUser("bburke");
|
||||||
|
idm.add(user);
|
||||||
|
Role role = idm.getRole("user");
|
||||||
|
idm.grantRole(user, role);
|
||||||
|
Assert.assertTrue(idm.hasRole(user, role));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
29
services/src/test/resources/META-INF/persistence.xml
Executable file
29
services/src/test/resources/META-INF/persistence.xml
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
|
||||||
|
version="1.0">
|
||||||
|
<persistence-unit name="identitydb" transaction-type="RESOURCE_LOCAL">
|
||||||
|
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||||
|
|
||||||
|
<class>org.picketlink.idm.jpa.schema.IdentityObject</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.PartitionObject</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.RelationshipObject</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.RelationshipIdentityObject</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.RelationshipIdentityWeakObject</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.RelationshipObjectAttribute</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.IdentityObjectAttribute</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.CredentialObject</class>
|
||||||
|
<class>org.picketlink.idm.jpa.schema.CredentialObjectAttribute</class>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
|
||||||
|
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
|
||||||
|
<property name="hibernate.connection.username" value="sa"/>
|
||||||
|
<property name="hibernate.connection.password" value=""/>
|
||||||
|
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
|
||||||
|
<property name="hibernate.show_sql" value="true" />
|
||||||
|
<property name="hibernate.format_sql" value="true" />
|
||||||
|
</properties>
|
||||||
|
</persistence-unit>
|
||||||
|
|
||||||
|
</persistence>
|
101
services/src/test/resources/testrealm.json
Normal file
101
services/src/test/resources/testrealm.json
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"realm" : "test-realm",
|
||||||
|
"enabled" : true,
|
||||||
|
"tokenLifespan" : 6000,
|
||||||
|
"accessCodeLifespan" : 30,
|
||||||
|
"requiredCredentials" : [
|
||||||
|
{
|
||||||
|
"type" : "Password",
|
||||||
|
"input" : true,
|
||||||
|
"secret" : true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"users" : [
|
||||||
|
{
|
||||||
|
"username" : "wburke",
|
||||||
|
"enabled" : true,
|
||||||
|
"attributes" : {
|
||||||
|
"email" : "bburke@redhat.com"
|
||||||
|
},
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "Password",
|
||||||
|
"value" : "userpassword" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "loginclient",
|
||||||
|
"enabled" : true,
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "Password",
|
||||||
|
"value" : "clientpassword" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "admin",
|
||||||
|
"enabled" : true,
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "Password",
|
||||||
|
"value" : "adminpassword" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "oauthclient",
|
||||||
|
"enabled" : true,
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "Password",
|
||||||
|
"value" : "clientpassword" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"roleMappings" : [
|
||||||
|
{
|
||||||
|
"username" : "admin",
|
||||||
|
"roles" : ["admin"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scopeMappings" : [
|
||||||
|
{
|
||||||
|
"username" : "loginclient",
|
||||||
|
"roles" : ["login"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resources" : [
|
||||||
|
{
|
||||||
|
"name" : "Application",
|
||||||
|
"roles" : ["admin", "user"],
|
||||||
|
"roleMappings" : [
|
||||||
|
{
|
||||||
|
"username" : "wburke",
|
||||||
|
"roles" : ["user"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "admin",
|
||||||
|
"roles" : ["admin"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scopeMappings" : [
|
||||||
|
{
|
||||||
|
"username" : "oauthclient",
|
||||||
|
"roles" : ["user"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "OtherApp",
|
||||||
|
"roles" : ["admin", "user"],
|
||||||
|
"roleMappings" : [
|
||||||
|
{
|
||||||
|
"username" : "wburke",
|
||||||
|
"roles" : ["user"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "admin",
|
||||||
|
"roles" : ["admin"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue