sync and import

This commit is contained in:
Bill Burke 2016-10-13 20:49:02 -04:00
commit 8c8a39c833
197 changed files with 10323 additions and 3196 deletions

View file

@ -214,7 +214,7 @@ public class PreAuthActionsHandler {
try {
JWSInput input = new JWSInput(token);
PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input, deployment);
PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input.getHeader().getKeyId(), deployment);
if (RSAProvider.verify(input, publicKey)) {
return input;
}

View file

@ -38,12 +38,12 @@ public class AdapterRSATokenVerifier {
}
public static PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) throws VerificationException {
public static PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
PublicKey publicKey = pkLocator.getPublicKey(input, deployment);
PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
if (publicKey == null) {
log.errorf("Didn't find publicKey for kid: %s", input.getHeader().getKeyId());
log.errorf("Didn't find publicKey for kid: %s", kid);
throw new VerificationException("Didn't find publicKey for specified kid");
}
@ -51,14 +51,8 @@ public class AdapterRSATokenVerifier {
}
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException {
JWSInput input;
try {
input = new JWSInput(tokenString);
} catch (Exception e) {
throw new VerificationException("Couldn't parse token", e);
}
PublicKey publicKey = getPublicKey(input, deployment);
return RSATokenVerifier.verifyToken(input, publicKey, deployment.getRealmInfoUrl(), checkActive, checkTokenType);
RSATokenVerifier verifier = RSATokenVerifier.create(tokenString).realmUrl(deployment.getRealmInfoUrl()).checkActive(checkActive).checkTokenType(checkTokenType);
PublicKey publicKey = getPublicKey(verifier.getHeader().getKeyId(), deployment);
return verifier.publicKey(publicKey).verify().getToken();
}
}

View file

@ -34,7 +34,7 @@ public class HardcodedPublicKeyLocator implements PublicKeyLocator {
}
@Override
public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) {
public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
return publicKey;
}
}

View file

@ -46,13 +46,7 @@ public class JWKPublicKeyLocator implements PublicKeyLocator {
private volatile int lastRequestTime = 0;
@Override
public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) {
String kid = input.getHeader().getKeyId();
return getPublicKey(kid, deployment);
}
private PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
int minTimeBetweenRequests = deployment.getMinTimeBetweenJwksRequests();
// Check if key is in cache.

View file

@ -28,10 +28,10 @@ import java.security.PublicKey;
public interface PublicKeyLocator {
/**
* @param input
* @param kid
* @param deployment
* @return publicKey, which should be used for verify signature on given "input"
*/
PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment);
PublicKey getPublicKey(String kid, KeycloakDeployment deployment);
}

View file

@ -140,11 +140,13 @@ public class CertificateUtils {
*
* @throws Exception the exception
*/
public static X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject) throws Exception {
public static X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject) {
return generateV1SelfSignedCertificate(caKeyPair, subject, BigInteger.valueOf(System.currentTimeMillis()));
}
public static X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair, String subject, BigInteger serialNumber) {
try {
X500Name subjectDN = new X500Name("CN=" + subject);
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
Date validityStartDate = new Date(System.currentTimeMillis() - 100000);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 10);

View file

@ -0,0 +1,76 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.common.util;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.RSAPublicKeySpec;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class KeyUtils {
private static final String DEFAULT_MESSAGE_DIGEST = "SHA-256";
private KeyUtils() {
}
public static KeyPair generateRsaKeyPair(int keysize) {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(keysize);
KeyPair keyPair = generator.generateKeyPair();
return keyPair;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static PublicKey extractPublicKey(PrivateKey key) {
if (key == null) {
return null;
}
try {
RSAPrivateCrtKey rsaPrivateCrtKey = (RSAPrivateCrtKey) key;
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPrivateCrtKey.getModulus(), rsaPrivateCrtKey.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(publicKeySpec);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String createKeyId(Key key) {
try {
return Base64Url.encode(MessageDigest.getInstance(DEFAULT_MESSAGE_DIGEST).digest(key.getEncoded()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.common.util;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PemException extends RuntimeException {
public PemException(Throwable cause) {
super(cause);
}
}

View file

@ -18,12 +18,15 @@
package org.keycloak.common.util;
import org.bouncycastle.openssl.PEMWriter;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
/**
@ -33,6 +36,7 @@ import java.security.cert.X509Certificate;
* @version $Revision: 1 $
*/
public final class PemUtils {
static {
BouncyIntegration.init();
}
@ -40,73 +44,112 @@ public final class PemUtils {
private PemUtils() {
}
public static X509Certificate decodeCertificate(InputStream is) throws Exception {
byte[] der = pemToDer(is);
ByteArrayInputStream bis = new ByteArrayInputStream(der);
return DerUtils.decodeCertificate(bis);
/**
* Decode a X509 Certificate from a PEM string
*
* @param cert
* @return
* @throws Exception
*/
public static X509Certificate decodeCertificate(String cert) {
if (cert == null) {
return null;
}
public static X509Certificate decodeCertificate(String cert) throws Exception {
try {
byte[] der = pemToDer(cert);
ByteArrayInputStream bis = new ByteArrayInputStream(der);
return DerUtils.decodeCertificate(bis);
} catch (Exception e) {
throw new PemException(e);
}
}
/**
* Extract a public key from a PEM string
* Decode a Public Key from a PEM string
*
* @param pem
* @return
* @throws Exception
*/
public static PublicKey decodePublicKey(String pem) throws Exception {
public static PublicKey decodePublicKey(String pem) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePublicKey(der);
} catch (Exception e) {
throw new PemException(e);
}
}
/**
* Extract a private key that is a PKCS8 pem string (base64 encoded PKCS8)
* Decode a Private Key from a PEM string
*
* @param pem
* @return
* @throws Exception
*/
public static PrivateKey decodePrivateKey(String pem) throws Exception {
public static PrivateKey decodePrivateKey(String pem) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePrivateKey(der);
} catch (Exception e) {
throw new PemException(e);
}
public static PrivateKey decodePrivateKey(InputStream is) throws Exception {
String pem = pemFromStream(is);
return decodePrivateKey(pem);
}
/**
* Decode a PEM file to DER format
* Encode a Key to a PEM string
*
* @param is
* @param key
* @return
* @throws java.io.IOException
* @throws Exception
*/
public static byte[] pemToDer(InputStream is) throws IOException {
String pem = pemFromStream(is);
return pemToDer(pem);
public static String encodeKey(Key key) {
return encode(key);
}
/**
* Decode a PEM string to DER format
* Encode a X509 Certificate to a PEM string
*
* @param pem
* @param certificate
* @return
* @throws java.io.IOException
*/
public static byte[] pemToDer(String pem) throws IOException {
public static String encodeCertificate(Certificate certificate) {
return encode(certificate);
}
private static String encode(Object obj) {
if (obj == null) {
return null;
}
try {
StringWriter writer = new StringWriter();
PEMWriter pemWriter = new PEMWriter(writer);
pemWriter.writeObject(obj);
pemWriter.flush();
pemWriter.close();
String s = writer.toString();
return PemUtils.removeBeginEnd(s);
} catch (Exception e) {
throw new PemException(e);
}
}
private static byte[] pemToDer(String pem) throws IOException {
pem = removeBeginEnd(pem);
return Base64.decode(pem);
}
public static String removeBeginEnd(String pem) {
private static String removeBeginEnd(String pem) {
pem = pem.replaceAll("-----BEGIN (.*)-----", "");
pem = pem.replaceAll("-----END (.*)----", "");
pem = pem.replaceAll("\r\n", "");
@ -114,12 +157,4 @@ public final class PemUtils {
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");
}
}

View file

@ -18,6 +18,7 @@
package org.keycloak;
import org.keycloak.common.VerificationException;
import org.keycloak.jose.jws.JWSHeader;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.RSAProvider;
@ -31,84 +32,123 @@ import java.security.PublicKey;
* @version $Revision: 1 $
*/
public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl) throws VerificationException {
return verifyToken(tokenString, realmKey, realmUrl, true, true);
private final String tokenString;
private PublicKey publicKey;
private String realmUrl;
private boolean checkTokenType = true;
private boolean checkActive = true;
private boolean checkRealmUrl = true;
private JWSInput jws;
private AccessToken token;
private RSATokenVerifier(String tokenString) {
this.tokenString = tokenString;
}
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
AccessToken token = toAccessToken(tokenString, realmKey);
public static RSATokenVerifier create(String tokenString) {
return new RSATokenVerifier(tokenString);
}
tokenVerifications(token, realmUrl, checkActive, checkTokenType);
public static AccessToken verifyToken(String tokenString, PublicKey publicKey, String realmUrl) throws VerificationException {
return RSATokenVerifier.create(tokenString).publicKey(publicKey).realmUrl(realmUrl).verify().getToken();
}
public static AccessToken verifyToken(String tokenString, PublicKey publicKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
return RSATokenVerifier.create(tokenString).publicKey(publicKey).realmUrl(realmUrl).checkActive(checkActive).checkTokenType(checkTokenType).verify().getToken();
}
public RSATokenVerifier publicKey(PublicKey publicKey) {
this.publicKey = publicKey;
return this;
}
public RSATokenVerifier realmUrl(String realmUrl) {
this.realmUrl = realmUrl;
return this;
}
public RSATokenVerifier checkTokenType(boolean checkTokenType) {
this.checkTokenType = checkTokenType;
return this;
}
public RSATokenVerifier checkActive(boolean checkActive) {
this.checkActive = checkActive;
return this;
}
public RSATokenVerifier checkRealmUrl(boolean checkRealmUrl) {
this.checkRealmUrl = checkRealmUrl;
return this;
}
public RSATokenVerifier parse() throws VerificationException {
if (jws == null) {
if (tokenString == null) {
throw new VerificationException("Token not set");
}
try {
jws = new JWSInput(tokenString);
} catch (JWSInputException e) {
throw new VerificationException("Failed to parse JWT", e);
}
try {
token = jws.readJsonContent(AccessToken.class);
} catch (JWSInputException e) {
throw new VerificationException("Failed to read access token from JWT", e);
}
}
return this;
}
public AccessToken getToken() throws VerificationException {
parse();
return token;
}
private static void tokenVerifications(AccessToken token, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
public JWSHeader getHeader() throws VerificationException {
parse();
return jws.getHeader();
}
public RSATokenVerifier verify() throws VerificationException {
parse();
if (publicKey == null) {
throw new VerificationException("Public key not set");
}
if (checkRealmUrl && realmUrl == null) {
throw new VerificationException("Realm URL not set");
}
if (!RSAProvider.verify(jws, publicKey)) {
throw new VerificationException("Invalid token signature");
}
String user = token.getSubject();
if (user == null) {
throw new VerificationException("Token user was null.");
}
if (realmUrl == null) {
throw new VerificationException("Realm URL is null. Make sure to add auth-server-url to the configuration of your adapter!");
}
if (!realmUrl.equals(token.getIssuer())) {
throw new VerificationException("Token audience doesn't match domain. Token issuer is " + token.getIssuer() + ", but URL from configuration is " + realmUrl);
throw new VerificationException("Subject missing in token");
}
if (checkTokenType) {
String type = token.getType();
if (type == null || !type.equalsIgnoreCase(TokenUtil.TOKEN_TYPE_BEARER)) {
throw new VerificationException("Token type is incorrect. Expected '" + TokenUtil.TOKEN_TYPE_BEARER + "' but was '" + type + "'");
if (checkRealmUrl && !realmUrl.equals(token.getIssuer())) {
throw new VerificationException("Invalid token issuer. Expected '" + realmUrl + "', but was '" + token.getIssuer() + "'");
}
if (checkTokenType && !TokenUtil.TOKEN_TYPE_BEARER.equalsIgnoreCase(token.getType())) {
throw new VerificationException("Token type is incorrect. Expected '" + TokenUtil.TOKEN_TYPE_BEARER + "' but was '" + token.getType() + "'");
}
if (checkActive && !token.isActive()) {
throw new VerificationException("Token is not active.");
throw new VerificationException("Token is not active");
}
return this;
}
public static AccessToken toAccessToken(String tokenString, PublicKey realmKey) throws VerificationException {
JWSInput input;
try {
input = new JWSInput(tokenString);
} catch (JWSInputException e) {
throw new VerificationException("Couldn't parse token", e);
}
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
AccessToken token;
try {
token = input.readJsonContent(AccessToken.class);
} catch (JWSInputException e) {
throw new VerificationException("Couldn't parse token signature", e);
}
return token;
}
public static AccessToken verifyToken(JWSInput input, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
AccessToken token;
try {
token = input.readJsonContent(AccessToken.class);
} catch (JWSInputException e) {
throw new VerificationException("Couldn't parse token signature", e);
}
tokenVerifications(token, realmUrl, checkActive, checkTokenType);
return token;
}
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
try {
return RSAProvider.verify(input, realmKey);
} catch (Exception e) {
throw new VerificationException("Token signature not validated.", e);
}
}
}

View file

@ -18,6 +18,7 @@
package org.keycloak.jose.jwk;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.KeyUtils;
import java.math.BigInteger;
import java.security.Key;
@ -53,7 +54,7 @@ public class JWKBuilder {
RSAPublicJWK k = new RSAPublicJWK();
String kid = this.kid != null ? this.kid : createKeyId(key);
String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
k.setKeyId(kid);
k.setKeyType(RSAPublicJWK.RSA);
k.setAlgorithm(RSAPublicJWK.RS256);
@ -64,14 +65,6 @@ public class JWKBuilder {
return k;
}
public static String createKeyId(Key key) {
try {
return Base64Url.encode(MessageDigest.getInstance(DEFAULT_MESSAGE_DIGEST).digest(key.getEncoded()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/**
* Copied from org.apache.commons.codec.binary.Base64
*/

View file

@ -24,6 +24,8 @@ import org.keycloak.common.util.MultivaluedHashMap;
*/
public class ComponentRepresentation {
public static final String SECRET_VALUE = "**********";
private String id;
private String name;
private String providerId;

View file

@ -27,6 +27,7 @@ public class ConfigPropertyRepresentation {
protected String helpText;
protected String type;
protected Object defaultValue;
protected boolean secret;
public String getName() {
return name;
@ -67,4 +68,12 @@ public class ConfigPropertyRepresentation {
public void setHelpText(String helpText) {
this.helpText = helpText;
}
public boolean isSecret() {
return secret;
}
public void setSecret(boolean secret) {
this.secret = secret;
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class KeysMetadataRepresentation {
private Map<String, String> active;
private List<KeyMetadataRepresentation> keys;
public Map<String, String> getActive() {
return active;
}
public void setActive(Map<String, String> active) {
this.active = active;
}
public List<KeyMetadataRepresentation> getKeys() {
return keys;
}
public void setKeys(List<KeyMetadataRepresentation> keys) {
this.keys = keys;
}
public static class KeyMetadataRepresentation {
private String providerId;
private long providerPriority;
private String kid;
private String status;
private String type;
private String publicKey;
private String certificate;
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public long getProviderPriority() {
return providerPriority;
}
public void setProviderPriority(long providerPriority) {
this.providerPriority = providerPriority;
}
public String getKid() {
return kid;
}
public void setKid(String kid) {
this.kid = kid;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getCertificate() {
return certificate;
}
public void setCertificate(String certificate) {
this.certificate = certificate;
}
}
}

View file

@ -19,11 +19,8 @@ package org.keycloak.representations.idm;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.bouncycastle.openssl.PEMWriter;
import org.keycloak.common.util.PemUtils;
import java.io.IOException;
import java.io.StringWriter;
import java.security.PublicKey;
/**
@ -85,17 +82,7 @@ public class PublishedRealmRepresentation {
@JsonIgnore
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
StringWriter writer = new StringWriter();
PEMWriter pemWriter = new PEMWriter(writer);
try {
pemWriter.writeObject(publicKey);
pemWriter.flush();
pemWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
String s = writer.toString();
this.publicKeyPem = PemUtils.removeBeginEnd(s);
this.publicKeyPem = PemUtils.encodeKey(publicKey);
}
public String getTokenServiceUrl() {

View file

@ -72,9 +72,13 @@ public class RealmRepresentation {
protected Integer failureFactor;
//--- end brute force settings
@Deprecated
protected String privateKey;
@Deprecated
protected String publicKey;
@Deprecated
protected String certificate;
@Deprecated
protected String codeSecret;
protected RolesRepresentation roles;
protected List<GroupRepresentation> groups;

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="aopalliance">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${aopalliance:aopalliance}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.inject.api"/>
<module name="javax.enterprise.api"/>
<module name="org.slf4j"/>
<module name="org.apache.commons.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
</dependencies>
</module>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="com.thoughtworks.xstream">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${com.thoughtworks.xstream:xstream}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.inject.api"/>
<module name="javax.enterprise.api"/>
<module name="org.slf4j"/>
<module name="org.apache.commons.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
</dependencies>
</module>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.antlr" slot="3.5">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.antlr:antlr-runtime}"/>
</resources>
<dependencies>
<module name="javax.api"/>
</dependencies>
</module>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.apache.ant" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.apache.ant:ant}"/>
<artifact name="${org.apache.ant:ant-launcher}"/>
</resources>
<dependencies>
<module name="javax.api"/>
</dependencies>
</module>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.apache.maven" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.apache.maven:maven-aether-provider}"/>
<artifact name="${org.apache.maven:maven-artifact}"/>
<artifact name="${org.apache.maven:maven-compat}"/>
<artifact name="${org.apache.maven:maven-core}"/>
<artifact name="${org.apache.maven:maven-model}"/>
<artifact name="${org.apache.maven:maven-model-builder}"/>
<artifact name="${org.apache.maven:maven-plugin-api}"/>
<artifact name="${org.apache.maven:maven-repository-metadata}"/>
<artifact name="${org.apache.maven:maven-settings}"/>
<artifact name="${org.apache.maven:maven-settings-builder}"/>
</resources>
<dependencies>
<module name="com.google.guava"/>
<module name="javax.api"/>
<module name="javax.inject.api"/>
<module name="org.apache.maven.wagon"/>
<module name="org.codehouse.plexus"/>
<module name="org.eclipse.aether" slot="kie"/>
<module name="org.eclipse.sisu"/>
<module name="org.sonatype.plexus"/>
<module name="org.sonatype.sisu"/>
</dependencies>
</module>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ * Copyright 2016 Red Hat, Inc. and/or its affiliates
~ * and other contributors as indicated by the @author tags.
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.apache.maven.wagon" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.apache.maven.wagon:wagon-http}"/>
<artifact name="${org.apache.maven.wagon:wagon-http-shared}"/>
<artifact name="${org.apache.maven.wagon:wagon-provider-api}"/>
</resources>
<dependencies>
<module name="javax.api" export="false" slot="main" services="import" optional="false"/>
<module name="org.apache.commons.io" export="false" slot="main" services="import" optional="false"/>
<module name="org.apache.commons.lang" export="false" slot="main" services="import" optional="false"/>
<module name="org.apache.commons.logging" export="false" slot="main" services="import" optional="false"/>
<module name="org.apache.httpcomponents" export="false" slot="main" services="import" optional="false"/>
<module name="org.codehouse.plexus" export="false" slot="main" services="import" optional="false"/>
<module name="sun.jdk" export="false" slot="main" services="import" optional="false"/>
</dependencies>
</module>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.codehouse.plexus" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.codehaus.plexus:plexus-classworlds}"/>
<artifact name="${org.codehaus.plexus:plexus-component-annotations}"/>
<artifact name="${org.codehaus.plexus:plexus-interpolation}"/>
<artifact name="${org.codehaus.plexus:plexus-utils}"/>
</resources>
<dependencies>
<module name="javax.api"/>
</dependencies>
</module>

View file

@ -1,10 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2016 Red Hat, Inc., and individual contributors
~ as indicated by the @author tags.
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -18,69 +15,27 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.drools">
<module xmlns="urn:jboss:module:1.3" name="org.drools" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.kie:kie-api}"/>
<artifact name="${org.kie:kie-ci}"/>
<artifact name="${org.kie:kie-internal}"/>
<artifact name="${org.drools:drools-core}"/>
<artifact name="${org.drools:drools-compiler}"/>
<artifact name="${org.eclipse.aether:aether-api}"/>
<artifact name="${org.eclipse.aether:aether-connector-basic}"/>
<artifact name="${org.eclipse.aether:aether-spi}"/>
<artifact name="${org.eclipse.aether:aether-impl}"/>
<artifact name="${org.eclipse.aether:aether-transport-file}"/>
<artifact name="${org.eclipse.aether:aether-transport-http}"/>
<artifact name="${org.eclipse.aether:aether-transport-wagon}"/>
<artifact name="${org.eclipse.aether:aether-util}"/>
<artifact name="${org.apache.ant:ant}"/>
<artifact name="${org.apache.ant:ant-launcher}"/>
<artifact name="${org.antlr:antlr-runtime}"/>
<artifact name="${aopalliance:aopalliance}"/>
<artifact name="${org.apache.maven:maven-aether-provider}"/>
<artifact name="${org.apache.maven:maven-artifact}"/>
<artifact name="${org.apache.maven:maven-compat}"/>
<artifact name="${org.apache.maven:maven-core}"/>
<artifact name="${org.apache.maven:maven-model}"/>
<artifact name="${org.apache.maven:maven-model-builder}"/>
<artifact name="${org.apache.maven:maven-plugin-api}"/>
<artifact name="${org.apache.maven:maven-repository-metadata}"/>
<artifact name="${org.apache.maven:maven-settings}"/>
<artifact name="${org.apache.maven:maven-settings-builder}"/>
<artifact name="${org.mvel:mvel2}"/>
<artifact name="${org.eclipse.sisu:org.eclipse.sisu.inject}"/>
<artifact name="${org.eclipse.sisu:org.eclipse.sisu.plexus}"/>
<artifact name="${org.sonatype.plexus:plexus-cipher}"/>
<artifact name="${org.codehaus.plexus:plexus-classworlds}"/>
<artifact name="${org.codehaus.plexus:plexus-component-annotations}"/>
<artifact name="${org.codehaus.plexus:plexus-interpolation}"/>
<artifact name="${org.sonatype.plexus:plexus-sec-dispatcher}"/>
<artifact name="${org.codehaus.plexus:plexus-utils}"/>
<artifact name="${org.apache.maven.wagon:wagon-http}"/>
<artifact name="${org.apache.maven.wagon:wagon-http-shared}"/>
<artifact name="${org.apache.maven.wagon:wagon-provider-api}"/>
<artifact name="${com.thoughtworks.xstream:xstream}"/>
<artifact name="${com.google.guava:guava}"/>
<artifact name="${org.eclipse.jdt.core.compiler:ecj}"/>
<artifact name="${org.apache.httpcomponents:httpclient}"/>
<artifact name="${org.apache.httpcomponents:httpcore}"/>
<artifact name="${com.lowagie:itext}"/>
<artifact name="${org.sonatype.sisu:sisu-guice::no_aop}"/>
<artifact name="${com.google.inject.extensions:guice-servlet}"/>
</resources>
<dependencies>
<module name="javax.activation.api"/>
<module name="javax.api"/>
<module name="javax.inject.api"/>
<module name="javax.enterprise.api"/>
<module name="org.slf4j"/>
<module name="org.apache.commons.logging"/>
<module name="javax.inject.api"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
<module name="com.thoughtworks.xstream"/>
<module name="org.antlr" slot="3.5"/>
<module name="org.kie"/>
<module name="org.mvel"/>
<module name="org.slf4j"/>
<module name="org.eclipse.jdt.core.compiler.ecj"/>
</dependencies>
</module>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.eclipse.aether" slot="kie">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.eclipse.aether:aether-api}"/>
<artifact name="${org.eclipse.aether:aether-connector-basic}"/>
<artifact name="${org.eclipse.aether:aether-spi}"/>
<artifact name="${org.eclipse.aether:aether-impl}"/>
<artifact name="${org.eclipse.aether:aether-transport-file}"/>
<artifact name="${org.eclipse.aether:aether-transport-http}"/>
<artifact name="${org.eclipse.aether:aether-transport-wagon}"/>
<artifact name="${org.eclipse.aether:aether-util}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.inject.api"/>
<module name="org.apache.httpcomponents"/>
<module name="org.apache.maven.wagon"/>
<module name="org.eclipse.sisu"/>
</dependencies>
</module>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.eclipse.jdt.core.compiler.ecj">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.eclipse.jdt.core.compiler:ecj}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.inject.api"/>
<module name="javax.enterprise.api"/>
<module name="org.slf4j"/>
<module name="org.apache.commons.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
</dependencies>
</module>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.eclipse.sisu" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.eclipse.sisu:org.eclipse.sisu.inject}"/>
<artifact name="${org.eclipse.sisu:org.eclipse.sisu.plexus}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.enterprise.api"/>
<module name="javax.inject.api"/>
<module name="org.codehouse.plexus"/>
<module name="org.sonatype.sisu"/>
</dependencies>
</module>

View file

@ -1,10 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2016 Red Hat, Inc., and individual contributors
~ as indicated by the @author tags.
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@ -18,7 +15,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-authz-policy-drools">
<properties>
<property name="jboss.api" value="private"/>
@ -33,6 +29,6 @@
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.drools"/>
<module name="org.kie"/>
</dependencies>
</module>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.kie">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.kie:kie-api}"/>
<artifact name="${org.kie:kie-ci}"/>
<artifact name="${org.kie:kie-internal}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="javax.inject.api"/>
<module name="javax.enterprise.api"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
<module name="org.slf4j"/>
<module name="org.apache.commons.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-server-spi"/>
<module name="com.sun.xml.bind"/>
<module name="com.thoughtworks.xstream"/>
<module name="org.apache.ant"/>
<module name="org.apache.httpcomponents"/>
<module name="org.apache.maven"/>
<module name="org.apache.maven.wagon"/>
<module name="org.codehouse.plexus"/>
<module name="org.drools"/>
<module name="org.eclipse.aether" slot="kie"/>
<module name="org.eclipse.sisu"/>
<module name="org.sonatype.plexus"/>
<module name="org.sonatype.sisu"/>
</dependencies>
</module>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ * Copyright 2016 Red Hat, Inc. and/or its affiliates
~ * and other contributors as indicated by the @author tags.
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.mvel" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.mvel:mvel2}"/>
</resources>
<dependencies>
<module name="javax.api" export="false" slot="main" services="import" optional="false"/>
</dependencies>
</module>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.sonatype.plexus" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.sonatype.plexus:plexus-cipher}"/>
<artifact name="${org.sonatype.plexus:plexus-sec-dispatcher}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.codehouse.plexus"/>
<module name="org.eclipse.sisu"/>
<module name="org.sonatype.sisu"/>
</dependencies>
</module>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<module xmlns="urn:jboss:module:1.3" name="org.sonatype.sisu" slot="main">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${com.google.inject.extensions:guice-servlet}"/>
<artifact name="${org.sonatype.sisu:sisu-guice::no_aop}"/>
</resources>
<dependencies>
<module name="aopalliance"/>
<module name="com.google.guava"/>
<module name="javax.api"/>
<module name="javax.inject.api"/>
</dependencies>
</module>

View file

@ -37,7 +37,16 @@
<include>org/liquibase/**</include>
<include>org/mongodb/**</include>
<include>org/twitter4j/**</include>
<include>aopalliance/**</include>
<include>com/thoughtworks/xstream/**</include>
<include>org/antlr/**</include>
<include>org/apache/**</include>
<include>org/codehouse/**</include>
<include>org/drools/**</include>
<include>org/eclipse/**</include>
<include>org/kie/**</include>
<include>org/mvel/**</include>
<include>org/sonatype/**</include>
<include>sun/jdk/jgss/**</include>
</includes>
</fileSet>

View file

@ -0,0 +1,36 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.admin.client.resource;
import org.keycloak.representations.idm.KeysMetadataRepresentation;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface KeyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
KeysMetadataRepresentation getKeyMetadata();
}

View file

@ -207,4 +207,7 @@ public interface RealmResource {
@Path("components")
ComponentsResource components();
@Path("keys")
KeyResource keys();
}

View file

@ -17,17 +17,15 @@ Creating a JPA change-set
-------------------------
We use Liquibase to support updating the database. The change-sets are located in
`connections/jpa-liquibase/src/main/resources/META-INF`. There's a separate file for each release that requires database
changes.
[`model/jpa/src/main/resources/META-INF`](../model/jpa/src/main/resources/META-INF).
There's a separate file for each release that requires database changes.
To manually create a change-set add a new file in the above location with the name `jpa-changelog-<version>.xml`. This file
should contain a single `change-set` with `id` equal to the next version to be released and `author` set to your email
address. Then look at Liquibase documentation on how to write this file. Add a reference to this file in `jpa-changelog-master.xml`.
address. Then look at Liquibase documentation on how to write this file. Add a reference to this file in
[`jpa-changelog-master.xml`](../model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml).
The file should have a single change-set and the id of the change-set should be the next version to be released.
You also need to update `org.keycloak.connections.jpa.updater.JpaUpdaterProvider#LAST_VERSION`. This
is used by Keycloak to quickly determine if the database is up to date or not.
You can also have Liquibase and Hibernate create one for you. To do this follow these steps:
1. Delete existing databases

View file

@ -60,10 +60,6 @@ public class RealmAdapter implements RealmModel {
protected RealmCacheSession cacheSession;
protected RealmModel updated;
protected RealmCache cache;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
protected volatile transient Key codeSecretKey;
protected volatile transient X509Certificate certificate;
public RealmAdapter(CachedRealm cached, RealmCacheSession cacheSession) {
this.cached = cached;
@ -423,123 +419,6 @@ public class RealmAdapter implements RealmModel {
updated.setAccessCodeLifespanLogin(seconds);
}
@Override
public String getKeyId() {
if (isUpdated()) return updated.getKeyId();
return cached.getKeyId();
}
@Override
public String getPublicKeyPem() {
if (isUpdated()) return updated.getPublicKeyPem();
return cached.getPublicKeyPem();
}
@Override
public void setPublicKeyPem(String publicKeyPem) {
getDelegateForUpdate();
updated.setPublicKeyPem(publicKeyPem);
}
@Override
public String getPrivateKeyPem() {
if (isUpdated()) return updated.getPrivateKeyPem();
return cached.getPrivateKeyPem();
}
@Override
public void setPrivateKeyPem(String privateKeyPem) {
getDelegateForUpdate();
updated.setPrivateKeyPem(privateKeyPem);
}
@Override
public PublicKey getPublicKey() {
if (isUpdated()) return updated.getPublicKey();
if (publicKey != null) return publicKey;
publicKey = cached.getPublicKey();
if (publicKey != null) return publicKey;
publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
return publicKey;
}
@Override
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
setPublicKeyPem(publicKeyPem);
}
@Override
public X509Certificate getCertificate() {
if (isUpdated()) return updated.getCertificate();
if (certificate != null) return certificate;
certificate = cached.getCertificate();
if (certificate != null) return certificate;
certificate = KeycloakModelUtils.getCertificate(getCertificatePem());
return certificate;
}
@Override
public void setCertificate(X509Certificate certificate) {
this.certificate = certificate;
String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
setCertificatePem(certPem);
}
@Override
public String getCertificatePem() {
if (isUpdated()) return updated.getCertificatePem();
return cached.getCertificatePem();
}
@Override
public void setCertificatePem(String certificate) {
getDelegateForUpdate();
updated.setCertificatePem(certificate);
}
@Override
public PrivateKey getPrivateKey() {
if (isUpdated()) return updated.getPrivateKey();
if (privateKey != null) {
return privateKey;
}
privateKey = cached.getPrivateKey();
if (privateKey != null) {
return privateKey;
}
privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
return privateKey;
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
setPrivateKeyPem(privateKeyPem);
}
@Override
public String getCodeSecret() {
return isUpdated() ? updated.getCodeSecret() : cached.getCodeSecret();
}
@Override
public Key getCodeSecretKey() {
if (codeSecretKey == null) {
codeSecretKey = KeycloakModelUtils.getSecretKey(getCodeSecret());
}
return codeSecretKey;
}
@Override
public void setCodeSecret(String codeSecret) {
getDelegateForUpdate();
updated.setCodeSecret(codeSecret);
}
@Override
public List<RequiredCredentialModel> getRequiredCredentials() {
if (isUpdated()) return updated.getRequiredCredentials();

View file

@ -89,15 +89,6 @@ public class CachedRealm extends AbstractRevisioned {
protected PasswordPolicy passwordPolicy;
protected OTPPolicy otpPolicy;
protected transient String keyId;
protected transient PublicKey publicKey;
protected String publicKeyPem;
protected transient PrivateKey privateKey;
protected String privateKeyPem;
protected transient X509Certificate certificate;
protected String certificatePem;
protected String codeSecret;
protected String loginTheme;
protected String accountTheme;
protected String adminTheme;
@ -191,15 +182,6 @@ public class CachedRealm extends AbstractRevisioned {
passwordPolicy = model.getPasswordPolicy();
otpPolicy = model.getOTPPolicy();
keyId = model.getKeyId();
publicKeyPem = model.getPublicKeyPem();
publicKey = model.getPublicKey();
privateKeyPem = model.getPrivateKeyPem();
privateKey = model.getPrivateKey();
certificatePem = model.getCertificatePem();
certificate = model.getCertificate();
codeSecret = model.getCodeSecret();
loginTheme = model.getLoginTheme();
accountTheme = model.getAccountTheme();
adminTheme = model.getAdminTheme();
@ -415,22 +397,6 @@ public class CachedRealm extends AbstractRevisioned {
return accessCodeLifespanLogin;
}
public String getKeyId() {
return keyId;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
public String getPrivateKeyPem() {
return privateKeyPem;
}
public String getCodeSecret() {
return codeSecret;
}
public List<RequiredCredentialModel> getRequiredCredentials() {
return requiredCredentials;
}
@ -507,10 +473,6 @@ public class CachedRealm extends AbstractRevisioned {
return userFederationMappers;
}
public String getCertificatePem() {
return certificatePem;
}
public List<IdentityProviderModel> getIdentityProviders() {
return identityProviders;
}
@ -591,18 +553,6 @@ public class CachedRealm extends AbstractRevisioned {
return clientTemplates;
}
public PublicKey getPublicKey() {
return publicKey;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public X509Certificate getCertificate() {
return certificate;
}
public Set<UserFederationMapperModel> getUserFederationMapperSet() {
return userFederationMapperSet;
}

View file

@ -106,7 +106,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
tx.put(sessionCache, id, entity);
ClientSessionAdapter wrap = wrap(realm, entity, false);
wrap.setNote(ClientSessionModel.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
return wrap;
}

View file

@ -27,14 +27,45 @@ import java.sql.Connection;
*/
public interface JpaUpdaterProvider extends Provider {
/**
* Status of database up-to-dateness
*/
enum Status {
VALID, EMPTY, OUTDATED
/**
* Database is valid and up to date
*/
VALID,
/**
* No database exists.
*/
EMPTY,
/**
* Database needs to be updated
*/
OUTDATED
}
/**
* Updates the Keycloak database
* @param connection DB connection
* @param defaultSchema DB connection
*/
void update(Connection connection, String defaultSchema);
/**
* Checks whether Keycloak database is up to date with the most recent changesets
* @param connection DB connection
* @param defaultSchema DB schema to use
* @return
*/
Status validate(Connection connection, String defaultSchema);
/**
* Exports the SQL update script into the given File.
* @param connection DB connection
* @param defaultSchema DB schema to use
* @param file File to write to
*/
void export(Connection connection, String defaultSchema, File file);
}

View file

@ -37,6 +37,7 @@ import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Set;
import liquibase.LabelExpression;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -95,7 +96,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
protected void updateChangeSet(Liquibase liquibase, String changelog, File exportFile) throws LiquibaseException, IOException {
List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null, new LabelExpression());
if (!changeSets.isEmpty()) {
List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
if (ranChangeSets.isEmpty()) {
@ -159,7 +160,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
}
protected Status validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null, new LabelExpression());
if (!changeSets.isEmpty()) {
if (changeSets.size() == liquibase.getDatabaseChangeLog().getChangeSets().size()) {
return Status.EMPTY;

View file

@ -0,0 +1,88 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.connections.jpa.updater.liquibase.custom;
import liquibase.exception.CustomChangeException;
import liquibase.statement.core.InsertStatement;
import liquibase.structure.core.Table;
import org.keycloak.keys.KeyProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ExtractRealmKeysFromRealmTable extends CustomKeycloakTask {
@Override
protected void generateStatementsImpl() throws CustomChangeException {
try {
PreparedStatement statement = jdbcConnection.prepareStatement("select ID, PRIVATE_KEY, CERTIFICATE from " + getTableName("REALM"));
try {
ResultSet resultSet = statement.executeQuery();
try {
while (resultSet.next()) {
String realmId = resultSet.getString(1);
String privateKeyPem = resultSet.getString(2);
String certificatePem = resultSet.getString(3);
String componentId = KeycloakModelUtils.generateId();
InsertStatement insertComponent = new InsertStatement(null, null, database.correctObjectName("COMPONENT", Table.class))
.addColumnValue("ID", componentId)
.addColumnValue("REALM_ID", realmId)
.addColumnValue("PARENT_ID", realmId)
.addColumnValue("NAME", "rsa")
.addColumnValue("PROVIDER_ID", "rsa")
.addColumnValue("PROVIDER_TYPE", KeyProvider.class.getName());
statements.add(insertComponent);
statements.add(componentConfigStatement(componentId, "priority", "100"));
statements.add(componentConfigStatement(componentId, "privateKey", privateKeyPem));
statements.add(componentConfigStatement(componentId, "certificate", certificatePem));
}
} finally {
resultSet.close();
}
} finally {
statement.close();
}
confirmationMessage.append("Updated " + statements.size() + " records in USER_FEDERATION_PROVIDER table");
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
}
}
private InsertStatement componentConfigStatement(String componentId, String name, String value) {
return new InsertStatement(null, null, database.correctObjectName("COMPONENT_CONFIG", Table.class))
.addColumnValue("ID", KeycloakModelUtils.generateId())
.addColumnValue("COMPONENT_ID", componentId)
.addColumnValue("NAME", name)
.addColumnValue("VALUE", value);
}
@Override
protected String getTaskId() {
return "Update 2.3.0.Final";
}
}

View file

@ -20,6 +20,7 @@ package org.keycloak.models.jpa;
import org.jboss.logging.Logger;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.models.AuthenticationExecutionModel;
@ -60,6 +61,7 @@ import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.jpa.entities.UserFederationMapperEntity;
import org.keycloak.models.jpa.entities.UserFederationProviderEntity;
import org.keycloak.models.utils.ComponentUtil;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
@ -88,10 +90,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
protected static final Logger logger = Logger.getLogger(RealmAdapter.class);
protected RealmEntity realm;
protected EntityManager em;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
protected volatile transient X509Certificate certificate;
protected volatile transient Key codeSecretKey;
protected KeycloakSession session;
private PasswordPolicy passwordPolicy;
private OTPPolicy otpPolicy;
@ -488,106 +486,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
em.flush();
}
@Override
public String getKeyId() {
PublicKey publicKey = getPublicKey();
return publicKey != null ? JWKBuilder.create().rs256(publicKey).getKeyId() : null;
}
@Override
public String getPublicKeyPem() {
return realm.getPublicKeyPem();
}
@Override
public void setPublicKeyPem(String publicKeyPem) {
realm.setPublicKeyPem(publicKeyPem);
em.flush();
}
@Override
public X509Certificate getCertificate() {
if (certificate != null) return certificate;
certificate = KeycloakModelUtils.getCertificate(getCertificatePem());
return certificate;
}
@Override
public void setCertificate(X509Certificate certificate) {
this.certificate = certificate;
String certificatePem = KeycloakModelUtils.getPemFromCertificate(certificate);
setCertificatePem(certificatePem);
}
@Override
public String getCertificatePem() {
return realm.getCertificatePem();
}
@Override
public void setCertificatePem(String certificate) {
realm.setCertificatePem(certificate);
}
@Override
public String getPrivateKeyPem() {
return realm.getPrivateKeyPem();
}
@Override
public void setPrivateKeyPem(String privateKeyPem) {
realm.setPrivateKeyPem(privateKeyPem);
em.flush();
}
@Override
public PublicKey getPublicKey() {
if (publicKey != null) return publicKey;
publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
return publicKey;
}
@Override
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
setPublicKeyPem(publicKeyPem);
}
@Override
public PrivateKey getPrivateKey() {
if (privateKey != null) return privateKey;
privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
return privateKey;
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
setPrivateKeyPem(privateKeyPem);
}
@Override
public String getCodeSecret() {
return realm.getCodeSecret();
}
@Override
public Key getCodeSecretKey() {
if (codeSecretKey == null) {
codeSecretKey = KeycloakModelUtils.getSecretKey(getCodeSecret());
}
return codeSecretKey;
}
@Override
public void setCodeSecret(String codeSecret) {
realm.setCodeSecret(codeSecret);
}
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
if (model == null) {
@ -2138,6 +2036,13 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
@Override
public ComponentModel addComponentModel(ComponentModel model) {
ComponentFactory componentFactory = ComponentUtil.getComponentFactory(session, model);
if (componentFactory == null) {
throw new IllegalArgumentException("Invalid component type");
}
componentFactory.validateConfiguration(session, model);
ComponentEntity c = new ComponentEntity();
if (model.getId() == null) {
c.setId(KeycloakModelUtils.generateId());
@ -2174,6 +2079,8 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
@Override
public void updateComponent(ComponentModel component) {
ComponentUtil.getComponentFactory(session, component).validateConfiguration(session, component);
ComponentEntity c = em.find(ComponentEntity.class, component.getId());
if (c == null) return;
c.setName(component.getName());

View file

@ -117,15 +117,6 @@ public class RealmEntity {
@Column(name="NOT_BEFORE")
protected int notBefore;
@Column(name="PUBLIC_KEY", length = 4000)
protected String publicKeyPem;
@Column(name="PRIVATE_KEY", length = 4000)
protected String privateKeyPem;
@Column(name="CERTIFICATE", length = 4000)
protected String certificatePem;
@Column(name="CODE_SECRET", length = 255)
protected String codeSecret;
@Column(name="LOGIN_THEME")
protected String loginTheme;
@Column(name="ACCOUNT_THEME")
@ -384,30 +375,6 @@ public class RealmEntity {
this.accessCodeLifespanLogin = accessCodeLifespanLogin;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
public void setPublicKeyPem(String publicKeyPem) {
this.publicKeyPem = publicKeyPem;
}
public String getPrivateKeyPem() {
return privateKeyPem;
}
public void setPrivateKeyPem(String privateKeyPem) {
this.privateKeyPem = privateKeyPem;
}
public String getCodeSecret() {
return codeSecret;
}
public void setCodeSecret(String codeSecret) {
this.codeSecret = codeSecret;
}
public Collection<RequiredCredentialEntity> getRequiredCredentials() {
return requiredCredentials;
}
@ -567,14 +534,6 @@ public class RealmEntity {
this.attributes = attributes;
}
public String getCertificatePem() {
return certificatePem;
}
public void setCertificatePem(String certificatePem) {
this.certificatePem = certificatePem;
}
public List<IdentityProviderEntity> getIdentityProviders() {
return this.identityProviders;
}

View file

@ -19,7 +19,7 @@
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="mposolda@redhat.com" id="1.6.1_from15">
<preConditions onFail="MARK_RAN" onFailMessage="Upgrading from 1.6.0 version. Skipped 1.6.1_from15 changeSet and marked as ran">
<preConditions onFail="MARK_RAN" onFailMessage="Upgrading from 1.6.0 version. Skipped 1.6.1_from15 changeSet and marked as ran" onSqlOutput="TEST">
<not>
<changeSetExecuted id="1.6.0" author="mposolda@redhat.com" changeLogFile="META-INF/jpa-changelog-1.6.0.xml" />
</not>
@ -84,7 +84,7 @@
<!-- KEYCLOAK-2404 - just for the update from 1.6.0 BUT not 1.6.1 or newer -->
<changeSet author="mposolda@redhat.com" id="1.6.1_from16-pre">
<preConditions onFail="MARK_RAN" onFailMessage="Skipped 1.6.1_from16-pre changeSet and marked as ran">
<preConditions onFail="MARK_RAN" onFailMessage="Skipped 1.6.1_from16-pre changeSet and marked as ran" onSqlOutput="TEST">
<and>
<changeSetExecuted id="1.6.0" author="mposolda@redhat.com" changeLogFile="META-INF/jpa-changelog-1.6.0.xml" />
<not>
@ -101,7 +101,7 @@
<!-- Just for the update from 1.6.0 -->
<changeSet author="mposolda@redhat.com" id="1.6.1_from16">
<preConditions onFail="MARK_RAN" onFailMessage="Upgrading from 1.5.0 or older version. Skipped 1.6.1_from16 changeSet and marked as ran">
<preConditions onFail="MARK_RAN" onFailMessage="Upgrading from 1.5.0 or older version. Skipped 1.6.1_from16 changeSet and marked as ran" onSqlOutput="TEST">
<changeSetExecuted id="1.6.0" author="mposolda@redhat.com" changeLogFile="META-INF/jpa-changelog-1.6.0.xml" />
</preConditions>

View file

@ -36,6 +36,13 @@
<addColumn tableName="IDENTITY_PROVIDER">
<column name="PROVIDER_DISPLAY_NAME" type="VARCHAR(255)"></column>
</addColumn>
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.ExtractRealmKeysFromRealmTable"/>
<dropColumn tableName="REALM" columnName="CODE_SECRET" />
<dropColumn tableName="REALM" columnName="PRIVATE_KEY" />
<dropColumn tableName="REALM" columnName="PUBLIC_KEY" />
<dropColumn tableName="REALM" columnName="CERTIFICATE" />
</changeSet>

View file

@ -33,6 +33,7 @@ import org.keycloak.connections.mongo.updater.impl.updates.Update1_4_0;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_7_0;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_8_0;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_9_2;
import org.keycloak.connections.mongo.updater.impl.updates.Update2_3_0;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
@ -57,7 +58,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
Update1_4_0.class,
Update1_7_0.class,
Update1_8_0.class,
Update1_9_2.class
Update1_9_2.class,
Update2_3_0.class
};
@Override

View file

@ -0,0 +1,80 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.connections.mongo.updater.impl.updates;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.keycloak.keys.KeyProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.mongo.keycloak.entities.ComponentEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.Collections;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Update2_3_0 extends Update {
@Override
public String getId() {
return "2.3.0";
}
@Override
public void update(KeycloakSession session) {
DBCollection realms = db.getCollection("realms");
DBCursor cursor = realms.find();
while (cursor.hasNext()) {
BasicDBObject realm = (BasicDBObject) cursor.next();
String realmId = realm.getString("_id");
String privateKeyPem = realm.getString("privateKeyPem");
String certificatePem = realm.getString("certificatePem");
BasicDBList entities = (BasicDBList) realm.get("componentEntities");
BasicDBObject component = new BasicDBObject();
component.put("id", KeycloakModelUtils.generateId());
component.put("name", "rsa");
component.put("providerType", KeyProvider.class.getName());
component.put("providerId", "rsa");
component.put("parentId", realmId);
BasicDBObject config = new BasicDBObject();
config.put("priority", Collections.singletonList("100"));
config.put("privateKey", Collections.singletonList(privateKeyPem));
config.put("certificate", Collections.singletonList(certificatePem));
component.put("config", config);
entities.add(component);
realm.remove("privateKeyPem");
realm.remove("certificatePem");
realm.remove("publicKeyPem");
realm.remove("codeSecret");
realms.update(new BasicDBObject().append("_id", realmId), realm);
}
}
}

View file

@ -59,6 +59,7 @@ import org.keycloak.models.mongo.keycloak.entities.RequiredActionProviderEntity;
import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
import org.keycloak.models.mongo.keycloak.entities.UserFederationMapperEntity;
import org.keycloak.models.mongo.keycloak.entities.UserFederationProviderEntity;
import org.keycloak.models.utils.ComponentUtil;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.Key;
@ -85,11 +86,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
private final MongoRealmEntity realm;
private final RealmProvider model;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
protected volatile transient X509Certificate certificate;
protected volatile transient Key codeSecretKey;
private volatile transient OTPPolicy otpPolicy;
private volatile transient PasswordPolicy passwordPolicy;
private volatile transient KeycloakSession session;
@ -455,110 +451,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
return realm.getAccessCodeLifespanLogin();
}
@Override
public String getKeyId() {
PublicKey publicKey = getPublicKey();
return publicKey != null ? JWKBuilder.create().rs256(publicKey).getKeyId() : null;
}
@Override
public String getPublicKeyPem() {
return realm.getPublicKeyPem();
}
@Override
public void setPublicKeyPem(String publicKeyPem) {
realm.setPublicKeyPem(publicKeyPem);
this.publicKey = null;
updateRealm();
}
@Override
public X509Certificate getCertificate() {
if (certificate != null) return certificate;
certificate = KeycloakModelUtils.getCertificate(getCertificatePem());
return certificate;
}
@Override
public void setCertificate(X509Certificate certificate) {
this.certificate = certificate;
String certificatePem = KeycloakModelUtils.getPemFromCertificate(certificate);
setCertificatePem(certificatePem);
}
@Override
public String getCertificatePem() {
return realm.getCertificatePem();
}
@Override
public void setCertificatePem(String certificate) {
realm.setCertificatePem(certificate);
}
@Override
public String getPrivateKeyPem() {
return realm.getPrivateKeyPem();
}
@Override
public void setPrivateKeyPem(String privateKeyPem) {
realm.setPrivateKeyPem(privateKeyPem);
this.privateKey = null;
updateRealm();
}
@Override
public PublicKey getPublicKey() {
if (publicKey != null) return publicKey;
publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
return publicKey;
}
@Override
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
setPublicKeyPem(publicKeyPem);
}
@Override
public PrivateKey getPrivateKey() {
if (privateKey != null) return privateKey;
privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
return privateKey;
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
setPrivateKeyPem(privateKeyPem);
}
@Override
public String getCodeSecret() {
return realm.getCodeSecret();
}
@Override
public Key getCodeSecretKey() {
if (codeSecretKey == null) {
codeSecretKey = KeycloakModelUtils.getSecretKey(getCodeSecret());
}
return codeSecretKey;
}
@Override
public void setCodeSecret(String codeSecret) {
realm.setCodeSecret(codeSecret);
updateRealm();
}
@Override
public String getLoginTheme() {
return realm.getLoginTheme();
@ -2062,6 +1954,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public ComponentModel addComponentModel(ComponentModel model) {
ComponentUtil.getComponentFactory(session, model).validateConfiguration(session, model);
ComponentEntity entity = new ComponentEntity();
if (model.getId() == null) {
entity.setId(KeycloakModelUtils.generateId());
@ -2082,6 +1976,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public void updateComponent(ComponentModel model) {
ComponentUtil.getComponentFactory(session, model).validateConfiguration(session, model);
for (ComponentEntity entity : realm.getComponentEntities()) {
if (entity.getId().equals(model.getId())) {
entity.setConfig(model.getConfig());

View file

@ -70,11 +70,6 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private int accessCodeLifespanLogin;
private int notBefore;
private String publicKeyPem;
private String privateKeyPem;
private String certificatePem;
private String codeSecret;
private String loginTheme;
private String accountTheme;
private String adminTheme;
@ -351,30 +346,6 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.notBefore = notBefore;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
public void setPublicKeyPem(String publicKeyPem) {
this.publicKeyPem = publicKeyPem;
}
public String getPrivateKeyPem() {
return privateKeyPem;
}
public void setPrivateKeyPem(String privateKeyPem) {
this.privateKeyPem = privateKeyPem;
}
public String getCodeSecret() {
return codeSecret;
}
public void setCodeSecret(String codeSecret) {
this.codeSecret = codeSecret;
}
public String getLoginTheme() {
return loginTheme;
}
@ -527,14 +498,6 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.identityProviders = identityProviders;
}
public String getCertificatePem() {
return certificatePem;
}
public void setCertificatePem(String certificatePem) {
this.certificatePem = certificatePem;
}
public boolean isInternationalizationEnabled() {
return internationalizationEnabled;
}

View file

@ -33,6 +33,6 @@ public interface ComponentFactory<CreatedType, ProviderType extends Provider> ex
return null;
}
void validateConfiguration(KeycloakSession session, ComponentModel config) throws ComponentValidationException;
void validateConfiguration(KeycloakSession session, ComponentModel model) throws ComponentValidationException;
}

View file

@ -20,6 +20,7 @@ package org.keycloak.component;
import org.keycloak.common.util.MultivaluedHashMap;
import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
/**
* Stored configuration of a User Storage provider instance.
@ -35,6 +36,7 @@ public class ComponentModel implements Serializable {
private String providerType;
private String parentId;
private MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
private transient ConcurrentHashMap<String, Object> notes = new ConcurrentHashMap<>();
public ComponentModel() {}
@ -72,6 +74,57 @@ public class ComponentModel implements Serializable {
this.config = config;
}
public boolean contains(String key) {
return config.containsKey(key);
}
public String get(String key) {
return config.getFirst(key);
}
public int get(String key, int defaultValue) {
String s = config.getFirst(key);
return s != null ? Integer.parseInt(s) : defaultValue;
}
public long get(String key, long defaultValue) {
String s = config.getFirst(key);
return s != null ? Long.parseLong(s) : defaultValue;
}
public boolean get(String key, boolean defaultValue) {
String s = config.getFirst(key);
return s != null ? Boolean.parseBoolean(s) : defaultValue;
}
public void put(String key, String value) {
config.putSingle(key, value);
}
public void put(String key, int value) {
config.putSingle(key, Integer.toString(value));
}
public void put(String key, long value) {
config.putSingle(key, Long.toString(value));
}
public void put(String key, boolean value) {
config.putSingle(key, Boolean.toString(value));
}
public boolean hasNote(String key) {
return notes.containsKey(key);
}
public <T> T getNote(String key) {
return (T) notes.get(key);
}
public void setNote(String key, Object object) {
notes.put(key, object);
}
public String getProviderId() {
return providerId;
}

View file

@ -0,0 +1,104 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import java.security.PublicKey;
import java.security.cert.Certificate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class KeyMetadata {
public enum Status {
ACTIVE, PASSIVE, DISABLED
}
public enum Type {
RSA
}
private String providerId;
private long providerPriority;
private String kid;
private Status status;
private Type type;
private PublicKey publicKey;
private Certificate certificate;
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public long getProviderPriority() {
return providerPriority;
}
public void setProviderPriority(long providerPriority) {
this.providerPriority = providerPriority;
}
public String getKid() {
return kid;
}
public void setKid(String kid) {
this.kid = kid;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public PublicKey getPublicKey() {
return publicKey;
}
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
}
public Certificate getCertificate() {
return certificate;
}
public void setCertificate(Certificate certificate) {
this.certificate = certificate;
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.provider.Provider;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface KeyProvider extends Provider {
/**
* Return the KID for the active keypair, or <code>null</code> if no active key is available.
*
* @return
*/
String getKid();
/**
* Return the private key for the active keypair, or <code>null</code> if no active key is available.
*
* @return
*/
PrivateKey getPrivateKey();
/**
* Return the public key for the specified kid, or <code>null</code> if the kid is unknown.
*
* @param kid
* @return
*/
PublicKey getPublicKey(String kid);
/**
* Return the certificate for the specified kid, or <code>null</code> if the kid is unknown.
*
* @param kid
* @return
*/
X509Certificate getCertificate(String kid);
/**
* Return metadata about all keypairs held by the provider
* @return
*/
List<KeyMetadata> getKeyMetadata();
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface KeyProviderFactory<T extends KeyProvider> extends ComponentFactory<T, KeyProvider> {
T create(KeycloakSession session, ComponentModel model);
}

View file

@ -15,50 +15,33 @@
* limitations under the License.
*/
package org.keycloak.representations;
package org.keycloak.keys;
import org.keycloak.common.util.Time;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PasswordToken {
private String realm;
private String user;
private int timestamp;
public PasswordToken() {
public class KeySpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
public PasswordToken(String realm, String user) {
this.realm = realm;
this.user = user;
this.timestamp = Time.currentTime();
@Override
public String getName() {
return "keys";
}
public String getRealm() {
return realm;
@Override
public Class<? extends Provider> getProviderClass() {
return KeyProvider.class;
}
public void setRealm(String realm) {
this.realm = realm;
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return KeyProviderFactory.class;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public int getTimestamp() {
return timestamp;
}
public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}
}

View file

@ -24,7 +24,7 @@ import java.util.Set;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface ClientSessionModel {
public static final String ACTION_KEY = "action_key";
public static final String ACTION_SIGNATURE = "action_signature";
public String getId();
public RealmModel getRealm();

View file

@ -0,0 +1,71 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models;
import org.keycloak.keys.KeyMetadata;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface KeyManager {
ActiveKey getActiveKey(RealmModel realm);
PublicKey getPublicKey(RealmModel realm, String kid);
Certificate getCertificate(RealmModel realm, String kid);
List<KeyMetadata> getKeys(RealmModel realm, boolean includeDisabled);
class ActiveKey {
private final String kid;
private final PrivateKey privateKey;
private final PublicKey publicKey;
private final X509Certificate certificate;
public ActiveKey(String kid, PrivateKey privateKey, PublicKey publicKey, X509Certificate certificate) {
this.kid = kid;
this.privateKey = privateKey;
this.publicKey = publicKey;
this.certificate = certificate;
}
public String getKid() {
return kid;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public PublicKey getPublicKey() {
return publicKey;
}
public X509Certificate getCertificate() {
return certificate;
}
}
}

View file

@ -145,6 +145,12 @@ public interface KeycloakSession {
*/
UserFederatedStorageProvider userFederatedStorage();
/**
* Key manager
*
* @return
*/
KeyManager keys();
/**
* Keycloak scripting support.

View file

@ -178,35 +178,6 @@ public interface RealmModel extends RoleContainerModel {
void setAccessCodeLifespanLogin(int seconds);
String getKeyId();
String getPublicKeyPem();
void setPublicKeyPem(String publicKeyPem);
String getPrivateKeyPem();
void setPrivateKeyPem(String privateKeyPem);
PublicKey getPublicKey();
void setPublicKey(PublicKey publicKey);
String getCodeSecret();
Key getCodeSecretKey();
void setCodeSecret(String codeSecret);
X509Certificate getCertificate();
void setCertificate(X509Certificate certificate);
String getCertificatePem();
void setCertificatePem(String certificate);
PrivateKey getPrivateKey();
void setPrivateKey(PrivateKey privateKey);
List<RequiredCredentialModel> getRequiredCredentials();
void addRequiredCredential(String cred);

View file

@ -0,0 +1,66 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.utils;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ComponentUtil {
public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, ComponentModel component) {
try {
List<ProviderConfigProperty> l = getComponentFactory(session, component).getConfigProperties();
Map<String, ProviderConfigProperty> properties = new HashMap<>();
for (ProviderConfigProperty p : l) {
properties.put(p.getName(), p);
}
return properties;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentModel component) {
Class<? extends Provider> provider;
try {
provider = (Class<? extends Provider>) Class.forName(component.getProviderType());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Invalid provider type '" + component.getProviderType() + "'");
}
ProviderFactory<? extends Provider> f = session.getKeycloakSessionFactory().getProviderFactory(provider, component.getProviderId());
if (f == null) {
throw new RuntimeException("No such provider '" + component.getProviderId() + "'");
}
ComponentFactory cf = (ComponentFactory) f;
return cf;
}
}

View file

@ -17,15 +17,9 @@
package org.keycloak.models.utils;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.PasswordToken;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -33,29 +27,6 @@ import org.keycloak.representations.PasswordToken;
*/
public class CredentialValidation {
public static boolean validPasswordToken(RealmModel realm, UserModel user, String encodedPasswordToken) {
try {
JWSInput jws = new JWSInput(encodedPasswordToken);
if (!RSAProvider.verify(jws, realm.getPublicKey())) {
return false;
}
PasswordToken passwordToken = jws.readJsonContent(PasswordToken.class);
if (!passwordToken.getRealm().equals(realm.getName())) {
return false;
}
if (!passwordToken.getUser().equals(user.getId())) {
return false;
}
if (Time.currentTime() - passwordToken.getTimestamp() > realm.getAccessCodeLifespanUserAction()) {
return false;
}
return true;
} catch (JWSInputException e) {
return false;
}
}
public static boolean validOTP(RealmModel realm, String token, String secret) {
OTPPolicy policy = realm.getOTPPolicy();
if (policy.getType().equals(UserCredentialModel.TOTP)) {

View file

@ -0,0 +1,62 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.utils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.keys.KeyProvider;
import org.keycloak.models.RealmModel;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultKeyProviders {
public static void createProviders(RealmModel realm) {
ComponentModel generated = new ComponentModel();
generated.setName("rsa-generated");
generated.setParentId(realm.getId());
generated.setProviderId("rsa-generated");
generated.setProviderType(KeyProvider.class.getName());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
config.putSingle("priority", "100");
generated.setConfig(config);
realm.addComponentModel(generated);
}
public static void createProviders(RealmModel realm, String privateKeyPem, String certificatePem) {
ComponentModel rsa = new ComponentModel();
rsa.setName("rsa");
rsa.setParentId(realm.getId());
rsa.setProviderId("rsa");
rsa.setProviderType(KeyProvider.class.getName());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
config.putSingle("priority", "100");
config.putSingle("privateKey", privateKeyPem);
if (certificatePem != null) {
config.putSingle("certificate", certificatePem);
}
rsa.setConfig(config);
realm.addComponentModel(rsa);
}
}

View file

@ -17,12 +17,15 @@
package org.keycloak.models.utils;
import org.bouncycastle.openssl.PEMWriter;
import org.keycloak.broker.social.SocialIdentityProvider;
import org.keycloak.broker.social.SocialIdentityProviderFactory;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.PemUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.keys.KeyProvider;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
@ -52,8 +55,6 @@ import javax.crypto.spec.SecretKeySpec;
import javax.transaction.InvalidTransactionException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.io.IOException;
import java.io.StringWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@ -135,82 +136,19 @@ public final class KeycloakModelUtils {
}
public static String getPemFromKey(Key key) {
StringWriter writer = new StringWriter();
PEMWriter pemWriter = new PEMWriter(writer);
try {
pemWriter.writeObject(key);
pemWriter.flush();
pemWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
String s = writer.toString();
return PemUtils.removeBeginEnd(s);
return PemUtils.encodeKey(key);
}
public static String getPemFromCertificate(X509Certificate certificate) {
StringWriter writer = new StringWriter();
PEMWriter pemWriter = new PEMWriter(writer);
try {
pemWriter.writeObject(certificate);
pemWriter.flush();
pemWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
String s = writer.toString();
return PemUtils.removeBeginEnd(s);
}
public static void generateRealmKeys(RealmModel realm) {
KeyPair keyPair = null;
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
keyPair = generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
realm.setPrivateKey(keyPair.getPrivate());
realm.setPublicKey(keyPair.getPublic());
X509Certificate certificate = null;
try {
certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, realm.getName());
} catch (Exception e) {
throw new RuntimeException(e);
}
realm.setCertificate(certificate);
realm.setCodeSecret(generateCodeSecret());
}
public static void generateRealmCertificate(RealmModel realm) {
X509Certificate certificate = null;
try {
certificate = CertificateUtils.generateV1SelfSignedCertificate(new KeyPair(realm.getPublicKey(), realm.getPrivateKey()), realm.getName());
} catch (Exception e) {
throw new RuntimeException(e);
}
realm.setCertificate(certificate);
return PemUtils.encodeCertificate(certificate);
}
public static CertificateRepresentation generateKeyPairCertificate(String subject) {
KeyPair keyPair = null;
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
keyPair = generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
X509Certificate certificate = null;
try {
certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
} catch (Exception e) {
throw new RuntimeException(e);
}
String privateKeyPem = KeycloakModelUtils.getPemFromKey(keyPair.getPrivate());
String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
X509Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
String privateKeyPem = PemUtils.encodeKey(keyPair.getPrivate());
String certPem = PemUtils.encodeCertificate(certificate);
CertificateRepresentation rep = new CertificateRepresentation();
rep.setPrivateKey(privateKeyPem);

View file

@ -25,6 +25,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
@ -265,16 +266,6 @@ public class ModelToRepresentation {
rep.setEnabled(realm.isEnabled());
rep.setNotBefore(realm.getNotBefore());
rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
rep.setPublicKey(realm.getPublicKeyPem());
if (internal) {
rep.setPrivateKey(realm.getPrivateKeyPem());
String privateKeyPem = realm.getPrivateKeyPem();
if (realm.getCertificatePem() == null && privateKeyPem != null) {
KeycloakModelUtils.generateRealmCertificate(realm);
}
rep.setCodeSecret(realm.getCodeSecret());
}
rep.setCertificate(realm.getCertificatePem());
rep.setRegistrationAllowed(realm.isRegistrationAllowed());
rep.setRegistrationEmailAsUsername(realm.isRegistrationEmailAsUsername());
rep.setRememberMe(realm.isRememberMe());
@ -783,19 +774,38 @@ public class ModelToRepresentation {
propRep.setType(prop.getType());
propRep.setDefaultValue(prop.getDefaultValue());
propRep.setHelpText(prop.getHelpText());
propRep.setSecret(prop.isSecret());
propertiesRep.add(propRep);
}
return propertiesRep;
}
public static ComponentRepresentation toRepresentation(ComponentModel component) {
public static ComponentRepresentation toRepresentation(KeycloakSession session, ComponentModel component, boolean internal) {
ComponentRepresentation rep = new ComponentRepresentation();
rep.setId(component.getId());
rep.setName(component.getName());
rep.setProviderId(component.getProviderId());
rep.setProviderType(component.getProviderType());
rep.setParentId(component.getParentId());
if (internal) {
rep.setConfig(component.getConfig());
} else {
Map<String, ProviderConfigProperty> configProperties = ComponentUtil.getComponentConfigProperties(session, component);
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
for (Map.Entry<String, List<String>> e : component.getConfig().entrySet()) {
ProviderConfigProperty configProperty = configProperties.get(e.getKey());
if (configProperty != null) {
if (configProperty.isSecret()) {
config.putSingle(e.getKey(), ComponentRepresentation.SECRET_VALUE);
} else {
config.put(e.getKey(), e.getValue());
}
}
}
rep.setConfig(config);
}
return rep;
}

View file

@ -31,10 +31,14 @@ import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.Base64;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.PemUtils;
import org.keycloak.common.util.UriUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
import org.keycloak.keys.KeyProvider;
import org.keycloak.migration.MigrationProvider;
import org.keycloak.migration.migrators.MigrationUtils;
import org.keycloak.models.AuthenticationExecutionModel;
@ -63,6 +67,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
@ -99,12 +104,16 @@ import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.security.KeyPair;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@ -183,23 +192,6 @@ public class RepresentationToModel {
if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail());
if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
if (rep.isEditUsernameAllowed() != null) newRealm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
if (rep.getPrivateKey() == null || rep.getPublicKey() == null) {
KeycloakModelUtils.generateRealmKeys(newRealm);
} else {
newRealm.setPrivateKeyPem(rep.getPrivateKey());
newRealm.setPublicKeyPem(rep.getPublicKey());
}
if (rep.getCertificate() == null) {
KeycloakModelUtils.generateRealmCertificate(newRealm);
} else {
newRealm.setCertificatePem(rep.getCertificate());
}
if (rep.getCodeSecret() == null) {
newRealm.setCodeSecret(KeycloakModelUtils.generateCodeSecret());
} else {
newRealm.setCodeSecret(rep.getCodeSecret());
}
if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme());
if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme());
if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme());
@ -381,6 +373,13 @@ public class RepresentationToModel {
}
}
if (newRealm.getComponents(newRealm.getId(), KeyProvider.class.getName()).isEmpty()) {
if (rep.getPrivateKey() != null) {
DefaultKeyProviders.createProviders(newRealm, rep.getPrivateKey(), rep.getCertificate());
} else {
DefaultKeyProviders.createProviders(newRealm);
}
}
}
protected static void importComponents(RealmModel newRealm, MultivaluedHashMap<String, ComponentExportRepresentation> components, String parentId) {
@ -819,20 +818,6 @@ public class RepresentationToModel {
realm.setUserFederationProviders(providerModels);
}
if (Constants.GENERATE.equals(rep.getPublicKey())) {
KeycloakModelUtils.generateRealmKeys(realm);
} else {
if (rep.getPrivateKey() != null && rep.getPublicKey() != null) {
realm.setPrivateKeyPem(rep.getPrivateKey());
realm.setPublicKeyPem(rep.getPublicKey());
realm.setCodeSecret(KeycloakModelUtils.generateCodeSecret());
}
if (rep.getCertificate() != null) {
realm.setCertificatePem(rep.getCertificate());
}
}
if(rep.isInternationalizationEnabled() != null){
realm.setInternationalizationEnabled(rep.isInternationalizationEnabled());
}
@ -1692,17 +1677,82 @@ public class RepresentationToModel {
return model;
}
public static ComponentModel toModel(ComponentRepresentation rep) {
public static ComponentModel toModel(KeycloakSession session, ComponentRepresentation rep) {
ComponentModel model = new ComponentModel();
model.setParentId(rep.getParentId());
model.setProviderType(rep.getProviderType());
model.setProviderId(rep.getProviderId());
model.setConfig(rep.getConfig());
model.setConfig(new MultivaluedHashMap<>());
model.setName(rep.getName());
if (rep.getConfig() != null) {
Set<String> keys = new HashSet<>(rep.getConfig().keySet());
for (String k : keys) {
List<String> values = rep.getConfig().get(k);
if (values != null) {
ListIterator<String> itr = values.listIterator();
while (itr.hasNext()) {
String v = itr.next();
if (v == null || v.trim().isEmpty()) {
itr.remove();
}
}
if (!values.isEmpty()) {
model.getConfig().put(k, values);
}
}
}
}
return model;
}
public static void updateComponent(KeycloakSession session, ComponentRepresentation rep, ComponentModel component, boolean internal) {
if (rep.getParentId() != null) {
component.setParentId(rep.getParentId());
}
if (rep.getProviderType() != null) {
component.setProviderType(rep.getProviderType());
}
if (rep.getProviderId() != null) {
component.setProviderId(rep.getProviderId());
}
Map<String, ProviderConfigProperty> providerConfiguration = null;
if (!internal) {
providerConfiguration = ComponentUtil.getComponentConfigProperties(session, component);
}
if (rep.getConfig() != null) {
Set<String> keys = new HashSet<>(rep.getConfig().keySet());
for (String k : keys) {
if (!internal && !providerConfiguration.containsKey(k)) {
break;
}
List<String> values = rep.getConfig().get(k);
if (values == null || values.isEmpty() || values.get(0) == null || values.get(0).trim().isEmpty()) {
component.getConfig().remove(k);
} else {
ListIterator<String> itr = values.listIterator();
while (itr.hasNext()) {
String v = itr.next();
if (v == null || v.trim().isEmpty() || v.equals(ComponentRepresentation.SECRET_VALUE)) {
itr.remove();
}
}
if (!values.isEmpty()) {
component.getConfig().put(k, values);
}
}
}
}
}
public static void importAuthorizationSettings(ClientRepresentation clientRepresentation, ClientModel client, KeycloakSession session) {
if (Boolean.TRUE.equals(clientRepresentation.getAuthorizationServicesEnabled())) {
AuthorizationProviderFactory authorizationFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class);

View file

@ -0,0 +1,121 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.provider;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ConfigurationValidationHelper {
private ComponentModel model;
private ConfigurationValidationHelper(ComponentModel model) {
this.model = model;
}
public static ConfigurationValidationHelper check(ComponentModel model) {
return new ConfigurationValidationHelper(model);
}
public ConfigurationValidationHelper checkInt(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
return checkInt(property.getName(), property.getLabel(), required);
}
public ConfigurationValidationHelper checkInt(String key, String label, boolean required) throws ComponentValidationException {
checkSingle(key, label, required);
String val = model.getConfig().getFirst(key);
if (val != null) {
try {
Integer.parseInt(val);
} catch (NumberFormatException e) {
throw new ComponentValidationException(label + " should be a number");
}
}
return this;
}
public ConfigurationValidationHelper checkLong(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
return checkLong(property.getName(), property.getLabel(), required);
}
public ConfigurationValidationHelper checkLong(String key, String label, boolean required) throws ComponentValidationException {
checkSingle(key, label, required);
String val = model.getConfig().getFirst(key);
if (val != null) {
try {
Long.parseLong(val);
} catch (NumberFormatException e) {
throw new ComponentValidationException(label + " should be a number");
}
}
return this;
}
public ConfigurationValidationHelper checkSingle(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
return checkSingle(property.getName(), property.getLabel(), required);
}
public ConfigurationValidationHelper checkSingle(String key, String label, boolean required) throws ComponentValidationException {
if (model.getConfig().containsKey(key) && model.getConfig().get(key).size() > 1) {
throw new ComponentValidationException(label + " should be a single entry");
}
if (required) {
checkRequired(key, label);
}
return this;
}
public ConfigurationValidationHelper checkRequired(ProviderConfigProperty property) throws ComponentValidationException {
return checkRequired(property.getName(), property.getLabel());
}
public ConfigurationValidationHelper checkRequired(String key, String label) throws ComponentValidationException {
List<String> values = model.getConfig().get(key);
if (values == null) {
throw new ComponentValidationException(label + " is required");
}
return this;
}
public ConfigurationValidationHelper checkBoolean(ProviderConfigProperty property, boolean required) throws ComponentValidationException {
return checkBoolean(property.getName(), property.getLabel(), required);
}
public ConfigurationValidationHelper checkBoolean(String key, String label, boolean required) {
checkSingle(key, label, required);
String val = model.getConfig().getFirst(key);
if (val != null && !(val.equals("true") || val.equals("false"))) {
throw new ComponentValidationException(label + " should be 'true' or 'false'");
}
return this;
}
}

View file

@ -25,6 +25,7 @@ public class ProviderConfigProperty {
public static final String BOOLEAN_TYPE="boolean";
public static final String STRING_TYPE="String";
public static final String SCRIPT_TYPE="Script";
public static final String FILE_TYPE="File";
public static final String ROLE_TYPE="Role";
public static final String LIST_TYPE="List";
public static final String CLIENT_LIST_TYPE="ClientList";
@ -35,6 +36,7 @@ public class ProviderConfigProperty {
protected String helpText;
protected String type;
protected Object defaultValue;
protected boolean secret;
public ProviderConfigProperty() {
}
@ -47,6 +49,11 @@ public class ProviderConfigProperty {
this.defaultValue = defaultValue;
}
public ProviderConfigProperty(String name, String label, String helpText, String type, Object defaultValue, boolean secret) {
this(name, label, helpText, type, defaultValue);
this.secret = secret;
}
public String getName() {
return name;
}
@ -86,4 +93,13 @@ public class ProviderConfigProperty {
public void setHelpText(String helpText) {
this.helpText = helpText;
}
public boolean isSecret() {
return secret;
}
public void setSecret(boolean secret) {
this.secret = secret;
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.provider;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ProviderConfigurationBuilder {
private List<ProviderConfigProperty> properties = new LinkedList<>();
private ProviderConfigurationBuilder() {
}
public static ProviderConfigurationBuilder create() {
return new ProviderConfigurationBuilder();
}
public ProviderConfigPropertyBuilder property() {
return new ProviderConfigPropertyBuilder();
}
public ProviderConfigurationBuilder property(ProviderConfigProperty property) {
properties.add(property);
return this;
}
public ProviderConfigurationBuilder property(String name, String label, String helpText, String type, Object defaultValue, boolean secret) {
ProviderConfigProperty property = new ProviderConfigProperty(name, label, helpText, type, defaultValue);
property.setSecret(secret);
properties.add(property);
return this;
}
public ProviderConfigurationBuilder property(String name, String label, String helpText, String type, Object defaultValue) {
properties.add(new ProviderConfigProperty(name, label, helpText, type, defaultValue));
return this;
}
public List<ProviderConfigProperty> build() {
return properties;
}
public class ProviderConfigPropertyBuilder {
private String name;
private String label;
private String helpText;
private String type;
private Object defaultValue;
private boolean secret;
public ProviderConfigPropertyBuilder name(String name) {
this.name = name;
return this;
}
public ProviderConfigPropertyBuilder label(String label) {
this.label = label;
return this;
}
public ProviderConfigPropertyBuilder helpText(String helpText) {
this.helpText = helpText;
return this;
}
public ProviderConfigPropertyBuilder type(String type) {
this.type = type;
return this;
}
public ProviderConfigPropertyBuilder defaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
return this;
}
public ProviderConfigPropertyBuilder secret(boolean secret) {
this.secret = secret;
return this;
}
public ProviderConfigurationBuilder add() {
ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(name);
property.setLabel(label);
property.setHelpText(helpText);
property.setType(type);
property.setDefaultValue(defaultValue);
property.setSecret(secret);
ProviderConfigurationBuilder.this.properties.add(property);
return ProviderConfigurationBuilder.this;
}
}
}

View file

@ -17,18 +17,23 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.crypto.Mac;
import java.security.Key;
import java.security.PublicKey;
import java.security.Signature;
import java.util.HashSet;
import java.util.Set;
@ -38,8 +43,11 @@ import java.util.Set;
*/
public class ClientSessionCode {
private static final byte[] HASH_SEPERATOR = "//".getBytes();
private static final Logger logger = Logger.getLogger(ClientSessionCode.class);
private static final String NEXT_CODE = ClientSessionCode.class.getName() + ".nextCode";
private KeycloakSession session;
private final RealmModel realm;
private final ClientSessionModel clientSession;
@ -49,32 +57,12 @@ public class ClientSessionCode {
USER
}
public ClientSessionCode(RealmModel realm, ClientSessionModel clientSession) {
public ClientSessionCode(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
this.session = session;
this.realm = realm;
this.clientSession = clientSession;
}
public static ClientSessionCode parse(String code, KeycloakSession session) {
try {
String[] parts = code.split("\\.");
String id = parts[1];
ClientSessionModel clientSession = session.sessions().getClientSession(id);
if (clientSession == null) {
return null;
}
String hash = createHash(clientSession.getRealm(), clientSession);
if (!hash.equals(parts[0])) {
return null;
}
return new ClientSessionCode(clientSession.getRealm(), clientSession);
} catch (RuntimeException e) {
return null;
}
}
public static class ParseResult {
ClientSessionCode code;
boolean clientSessionNotFound;
@ -114,13 +102,12 @@ public class ClientSessionCode {
return result;
}
String hash = createHash(realm, result.clientSession);
if (!hash.equals(parts[0])) {
if (!verifyCode(code, session, realm, result.clientSession)) {
result.illegalHash = true;
return result;
}
result.code = new ClientSessionCode(realm, result.clientSession);
result.code = new ClientSessionCode(session, realm, result.clientSession);
return result;
} catch (RuntimeException e) {
result.illegalHash = true;
@ -128,8 +115,6 @@ public class ClientSessionCode {
}
}
public static ClientSessionCode parse(String code, KeycloakSession session, RealmModel realm) {
try {
String[] parts = code.split("\\.");
@ -140,12 +125,11 @@ public class ClientSessionCode {
return null;
}
String hash = createHash(realm, clientSession);
if (!hash.equals(parts[0])) {
if (!verifyCode(code, session, realm, clientSession)) {
return null;
}
return new ClientSessionCode(realm, clientSession);
return new ClientSessionCode(session, realm, clientSession);
} catch (RuntimeException e) {
return null;
}
@ -194,7 +178,7 @@ public class ClientSessionCode {
public Set<RoleModel> getRequestedRoles() {
Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
Set<RoleModel> requestedRoles = new HashSet<>();
for (String roleId : clientSession.getRoles()) {
RoleModel role = realm.getRoleById(roleId);
if (role != null) {
@ -205,7 +189,7 @@ public class ClientSessionCode {
}
public Set<ProtocolMapperModel> getRequestedProtocolMappers() {
Set<ProtocolMapperModel> requestedProtocolMappers = new HashSet<ProtocolMapperModel>();
Set<ProtocolMapperModel> requestedProtocolMappers = new HashSet<>();
Set<String> protocolMappers = clientSession.getProtocolMappers();
ClientModel client = clientSession.getClient();
ClientTemplateModel template = client.getClientTemplate();
@ -229,32 +213,67 @@ public class ClientSessionCode {
}
public String getCode() {
return generateCode(realm, clientSession);
String nextCode = (String) session.getAttribute(NEXT_CODE + "." + clientSession.getId());
if (nextCode == null) {
nextCode = generateCode(session, realm, clientSession);
session.setAttribute(NEXT_CODE + "." + clientSession.getId(), nextCode);
} else {
logger.debug("Code already generated for session, using code from session attributes");
}
return nextCode;
}
private static String generateCode(RealmModel realm, ClientSessionModel clientSession) {
String hash = createHash(realm, clientSession);
private static String generateCode(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
try {
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
String secret = KeycloakModelUtils.generateSecret();
StringBuilder sb = new StringBuilder();
sb.append(hash);
sb.append(".");
sb.append(secret);
sb.append('.');
sb.append(clientSession.getId());
return sb.toString();
}
String code = sb.toString();
private static String createHash(RealmModel realm, ClientSessionModel clientSession) {
try {
Key codeSecretKey = realm.getCodeSecretKey();
Mac mac = Mac.getInstance(codeSecretKey.getAlgorithm());
mac.init(codeSecretKey);
mac.update(clientSession.getId().getBytes());
mac.update(HASH_SEPERATOR);
mac.update(clientSession.getNote(ClientSessionModel.ACTION_KEY).getBytes());
return Base64Url.encode(mac.doFinal());
Signature signature = RSAProvider.getSignature(Algorithm.RS256);
signature.initSign(keys.getPrivateKey());
signature.update(code.getBytes("utf-8"));
sb = new StringBuilder();
sb.append(Base64Url.encode(signature.sign()));
sb.append('.');
sb.append(keys.getKid());
clientSession.setNote(ClientSessionModel.ACTION_SIGNATURE, sb.toString());
return code;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static boolean verifyCode(String code, KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
try {
String note = clientSession.getNote(ClientSessionModel.ACTION_SIGNATURE);
if (note == null) {
logger.debug("Action signature not found in client session");
return false;
}
clientSession.removeNote(ClientSessionModel.ACTION_SIGNATURE);
String[] signed = note.split("\\.");
PublicKey publicKey = session.keys().getPublicKey(realm, signed[1]);
Signature verifier = RSAProvider.getSignature(Algorithm.RS256);
verifier.initVerify(publicKey);
verifier.update(code.getBytes("utf-8"));
return verifier.verify(Base64Url.decode(signed[0]));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -65,3 +65,4 @@ org.keycloak.transaction.TransactionManagerLookupSpi
org.keycloak.credential.hash.PasswordHashSpi
org.keycloak.credential.CredentialSpi
org.keycloak.keys.PublicKeyStorageSpi
org.keycloak.keys.KeySpi

View file

@ -212,7 +212,7 @@ public class AuthenticationProcessor {
}
public String generateCode() {
ClientSessionCode accessCode = new ClientSessionCode(getRealm(), getClientSession());
ClientSessionCode accessCode = new ClientSessionCode(session, getRealm(), getClientSession());
clientSession.setTimestamp(Time.currentTime());
return accessCode.getCode();
}
@ -690,10 +690,10 @@ public class AuthenticationProcessor {
}
public static Response redirectToRequiredActions(RealmModel realm, ClientSessionModel clientSession, UriInfo uriInfo) {
public static Response redirectToRequiredActions(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, UriInfo uriInfo) {
// redirect to non-action url so browser refresh button works without reposting past data
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
ClientSessionCode accessCode = new ClientSessionCode(session, realm, clientSession);
accessCode.setAction(ClientSessionModel.Action.REQUIRED_ACTIONS.name());
clientSession.setTimestamp(Time.currentTime());
@ -764,7 +764,7 @@ public class AuthenticationProcessor {
}
public void checkClientSession() {
ClientSessionCode code = new ClientSessionCode(realm, clientSession);
ClientSessionCode code = new ClientSessionCode(session, realm, clientSession);
String action = ClientSessionModel.Action.AUTHENTICATE.name();
if (!code.isValidAction(action)) {
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION);
@ -862,7 +862,7 @@ public class AuthenticationProcessor {
protected Response authenticationComplete() {
attachSession();
if (isActionRequired()) {
return redirectToRequiredActions(realm, clientSession, uriInfo);
return redirectToRequiredActions(session, realm, clientSession, uriInfo);
} else {
event.detail(Details.CODE_ID, clientSession.getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
return AuthenticationManager.finishedRequiredActions(session, userSession, clientSession, connection, request, uriInfo, event);

View file

@ -148,7 +148,7 @@ public class RequiredActionContextResult implements RequiredActionContext {
@Override
public String generateCode() {
ClientSessionCode accessCode = new ClientSessionCode(getRealm(), getClientSession());
ClientSessionCode accessCode = new ClientSessionCode(session, getRealm(), getClientSession());
clientSession.setTimestamp(Time.currentTime());
return accessCode.getCode();
}

View file

@ -256,7 +256,6 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
ctx.setLastName(getLastName());
ctx.setBrokerSessionId(getBrokerSessionId());
ctx.setBrokerUserId(getBrokerUserId());
ctx.setCode(getCode());
ctx.setToken(getToken());
RealmModel realm = clientSession.getRealm();
@ -297,7 +296,6 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
ctx.setLastName(context.getLastName());
ctx.setBrokerSessionId(context.getBrokerSessionId());
ctx.setBrokerUserId(context.getBrokerUserId());
ctx.setCode(context.getCode());
ctx.setToken(context.getToken());
ctx.setIdentityProviderId(context.getIdpConfig().getAlias());

View file

@ -63,7 +63,7 @@ public class IdentityProviderAuthenticator implements Authenticator {
List<IdentityProviderModel> identityProviders = context.getRealm().getIdentityProviders();
for (IdentityProviderModel identityProvider : identityProviders) {
if (identityProvider.isEnabled() && providerId.equals(identityProvider.getAlias())) {
String accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode();
String accessCode = new ClientSessionCode(context.getSession(), context.getRealm(), context.getClientSession()).getCode();
Response response = Response.seeOther(
Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode))
.build();

View file

@ -38,6 +38,7 @@ import org.keycloak.authorization.util.Permissions;
import org.keycloak.authorization.util.Tokens;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
@ -77,6 +78,9 @@ public class AuthorizationTokenService {
@Context
private HttpRequest httpRequest;
@Context
private KeycloakSession session;
public AuthorizationTokenService(AuthorizationProvider authorization) {
this.authorization = authorization;
}
@ -180,7 +184,7 @@ public class AuthorizationTokenService {
String rpt = request.getRpt();
if (rpt != null && !"".equals(rpt)) {
if (!Tokens.verifySignature(rpt, getRealm().getPublicKey())) {
if (!Tokens.verifySignature(session, getRealm(), rpt)) {
throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
}
@ -252,13 +256,13 @@ public class AuthorizationTokenService {
authorization.setPermissions(permissions);
accessToken.setAuthorization(authorization);
return new TokenManager().encodeToken(getRealm(), accessToken);
return new TokenManager().encodeToken(session, getRealm(), accessToken);
}
private PermissionTicket verifyPermissionTicket(AuthorizationRequest request) {
String ticketString = request.getTicket();
if (ticketString == null || !Tokens.verifySignature(ticketString, getRealm().getPublicKey())) {
if (ticketString == null || !Tokens.verifySignature(session, getRealm(), ticketString)) {
throw new ErrorResponseException("invalid_ticket", "Ticket verification failed", Status.FORBIDDEN);
}

View file

@ -17,6 +17,7 @@
*/
package org.keycloak.authorization.config;
import org.keycloak.common.util.PemUtils;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@ -46,7 +47,7 @@ public class UmaWellKnownProvider implements WellKnownProvider {
return Configuration.fromDefault(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString(), realm.getName(),
URI.create(RealmsResource.protocolUrl(uriInfo).path(OIDCLoginProtocolService.class, "auth").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString()),
URI.create(RealmsResource.protocolUrl(uriInfo).path(OIDCLoginProtocolService.class, "token").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString()),
realm.getPublicKeyPem());
PemUtils.encodeKey(session.keys().getActiveKey(realm).getPublicKey()));
}
@Override

View file

@ -40,6 +40,7 @@ import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
@ -81,6 +82,9 @@ public class EntitlementService {
@Context
private HttpRequest request;
@Context
private KeycloakSession session;
public EntitlementService(AuthorizationProvider authorization) {
this.authorization = authorization;
}
@ -200,7 +204,7 @@ public class EntitlementService {
authorization.setPermissions(permissions);
accessToken.setAuthorization(authorization);
return new TokenManager().encodeToken(realm, accessToken);
return new TokenManager().encodeToken(this.authorization.getKeycloakSession(), realm, accessToken);
}
private List<ResourcePermission> createPermissions(EntitlementRequest entitlementRequest, ResourceServer resourceServer, AuthorizationProvider authorization) {
@ -252,7 +256,7 @@ public class EntitlementService {
if (rpt != null && !"".equals(rpt)) {
KeycloakContext context = authorization.getKeycloakSession().getContext();
if (!Tokens.verifySignature(rpt, context.getRealm().getPublicKey())) {
if (!Tokens.verifySignature(session, context.getRealm(), rpt)) {
throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
}

View file

@ -25,6 +25,7 @@ import org.keycloak.authorization.protection.permission.representation.Permissio
import org.keycloak.authorization.protection.permission.representation.PermissionResponse;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.KeyManager;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponseException;
@ -130,7 +131,8 @@ public class AbstractPermissionService {
}
private String createPermissionTicket(List<ResourceRepresentation> resources) {
return new JWSBuilder().jsonContent(new PermissionTicket(resources, this.resourceServer.getId(), this.identity.getAccessToken()))
.rsa256(this.authorization.getKeycloakSession().getContext().getRealm().getPrivateKey());
KeyManager.ActiveKey keys = this.authorization.getKeycloakSession().keys().getActiveKey(this.authorization.getRealm());
return new JWSBuilder().kid(keys.getKid()).jsonContent(new PermissionTicket(resources, this.resourceServer.getId(), this.identity.getAccessToken()))
.rsa256(keys.getPrivateKey());
}
}

View file

@ -22,6 +22,7 @@ import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AppAuthManager;
@ -53,10 +54,10 @@ public class Tokens {
return authManager.extractAuthorizationHeaderToken(keycloakSession.getContext().getRequestHeaders());
}
public static boolean verifySignature(String token, PublicKey publicKey) {
public static boolean verifySignature(KeycloakSession keycloakSession, RealmModel realm, String token) {
try {
JWSInput jws = new JWSInput(token);
PublicKey publicKey = keycloakSession.keys().getPublicKey(realm, jws.getHeader().getKeyId());
return RSAProvider.verify(jws, publicKey);
} catch (Exception e) {
throw new ErrorResponseException("invalid_signature", "Unexpected error while validating signature.", Status.INTERNAL_SERVER_ERROR);

View file

@ -233,9 +233,9 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
federatedIdentity.setToken(response);
}
federatedIdentity.setCode(state);
federatedIdentity.setIdpConfig(getConfig());
federatedIdentity.setIdp(AbstractOAuth2IdentityProvider.this);
federatedIdentity.setCode(state);
return callback.authenticated(federatedIdentity);
}

View file

@ -38,6 +38,7 @@ import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
@ -265,7 +266,8 @@ public class SAMLEndpoint {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
binding.signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
.signatureAlgorithm(provider.getSignatureAlgorithm())
.signDocument();
}
@ -291,13 +293,13 @@ public class SAMLEndpoint {
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
try {
AssertionType assertion = AssertionUtil.getAssertion(responseType, realm.getPrivateKey());
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
AssertionType assertion = AssertionUtil.getAssertion(responseType, keys.getPrivateKey());
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
//Map<String, String> notes = new HashMap<>();
BrokeredIdentityContext identity = new BrokeredIdentityContext(subjectNameID.getValue());
identity.setCode(relayState);
identity.getContextData().put(SAML_LOGIN_RESPONSE, responseType);
identity.getContextData().put(SAML_ASSERTION, assertion);
@ -340,6 +342,7 @@ public class SAMLEndpoint {
if (authn != null && authn.getSessionIndex() != null) {
identity.setBrokerSessionId(identity.getBrokerUserId() + "." + authn.getSessionIndex());
}
identity.setCode(relayState);
return callback.authenticated(identity);

View file

@ -23,6 +23,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.common.util.PemUtils;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
@ -31,6 +32,7 @@ import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
@ -97,18 +99,9 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.relayState(request.getState());
if (getConfig().isWantAuthnRequestsSigned()) {
PrivateKey privateKey = realm.getPrivateKey();
PublicKey publicKey = realm.getPublicKey();
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
if (privateKey == null) {
throw new IdentityBrokerException("Identity Provider [" + getConfig().getAlias() + "] wants a signed authentication request. But the Realm [" + realm.getName() + "] does not have a private key.");
}
if (publicKey == null) {
throw new IdentityBrokerException("Identity Provider [" + getConfig().getAlias() + "] wants a signed authentication request. But the Realm [" + realm.getName() + "] does not have a public key.");
}
KeyPair keypair = new KeyPair(publicKey, privateKey);
KeyPair keypair = new KeyPair(keys.getPublicKey(), keys.getPrivateKey());
binding.signWith(keypair);
binding.signatureAlgorithm(getSignatureAlgorithm());
@ -155,7 +148,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(session, userSession, realm);
try {
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
.param(GeneralConstants.SAML_REQUEST_KEY, binding.postBinding(logoutBuilder.buildDocument()).encoded())
@ -181,7 +174,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
} else {
try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(session, userSession, realm);
return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
} catch (Exception e) {
throw new RuntimeException(e);
@ -200,11 +193,12 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
return logoutBuilder;
}
private JaxrsSAML2BindingBuilder buildLogoutBinding(UserSessionModel userSession, RealmModel realm) {
private JaxrsSAML2BindingBuilder buildLogoutBinding(KeycloakSession session, UserSessionModel userSession, RealmModel realm) {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) {
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
binding.signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
.signatureAlgorithm(getSignatureAlgorithm())
.signDocument();
}
@ -231,7 +225,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
String entityId = getEntityId(uriInfo, realm);
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
String certificatePem = realm.getCertificatePem();
String certificatePem = PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate());
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, certificatePem);
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
}

View file

@ -0,0 +1,141 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.RealmModel;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public abstract class AbstractRsaKeyProvider implements KeyProvider {
private final boolean enabled;
private final boolean active;
private final ComponentModel model;
private final Keys keys;
public AbstractRsaKeyProvider(RealmModel realm, ComponentModel model) {
this.model = model;
this.enabled = model.get(Attributes.ENABLED_KEY, true);
this.active = model.get(Attributes.ACTIVE_KEY, true);
if (model.hasNote(Keys.class.getName())) {
keys = model.getNote(Keys.class.getName());
} else {
keys = loadKeys(realm, model);
model.setNote(Keys.class.getName(), keys);
}
}
protected abstract Keys loadKeys(RealmModel realm, ComponentModel model);
@Override
public final String getKid() {
return isActive() ? keys.getKid() : null;
}
@Override
public final PrivateKey getPrivateKey() {
return isActive() ? keys.getKeyPair().getPrivate() : null;
}
@Override
public final PublicKey getPublicKey(String kid) {
return isEnabled() && kid.equals(keys.getKid()) ? keys.getKeyPair().getPublic() : null;
}
@Override
public X509Certificate getCertificate(String kid) {
return isEnabled() && kid.equals(keys.getKid()) ? keys.getCertificate() : null;
}
@Override
public final List<KeyMetadata> getKeyMetadata() {
String kid = keys.getKid();
PublicKey publicKey = keys.getKeyPair().getPublic();
if (kid != null && publicKey != null) {
KeyMetadata k = new KeyMetadata();
k.setProviderId(model.getId());
k.setProviderPriority(model.get(Attributes.PRIORITY_KEY, 0l));
k.setKid(kid);
if (isActive()) {
k.setStatus(KeyMetadata.Status.ACTIVE);
} else if (isEnabled()) {
k.setStatus(KeyMetadata.Status.PASSIVE);
} else {
k.setStatus(KeyMetadata.Status.DISABLED);
}
k.setType(KeyMetadata.Type.RSA);
k.setPublicKey(publicKey);
k.setCertificate(keys.getCertificate());
return Collections.singletonList(k);
} else {
return Collections.emptyList();
}
}
@Override
public void close() {
}
private boolean isEnabled() {
return keys != null && enabled;
}
private boolean isActive() {
return isEnabled() && active;
}
public static class Keys {
private String kid;
private KeyPair keyPair;
private X509Certificate certificate;
public Keys(String kid, KeyPair keyPair, X509Certificate certificate) {
this.kid = kid;
this.keyPair = keyPair;
this.certificate = certificate;
}
public String getKid() {
return kid;
}
public KeyPair getKeyPair() {
return keyPair;
}
public X509Certificate getCertificate() {
return certificate;
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ConfigurationValidationHelper;
import org.keycloak.provider.ProviderConfigurationBuilder;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public abstract class AbstractRsaKeyProviderFactory implements KeyProviderFactory {
public final static ProviderConfigurationBuilder configurationBuilder() {
return ProviderConfigurationBuilder.create()
.property(Attributes.PRIORITY_PROPERTY)
.property(Attributes.ENABLED_PROPERTY)
.property(Attributes.ACTIVE_PROPERTY);
}
@Override
public void validateConfiguration(KeycloakSession session, ComponentModel model) throws ComponentValidationException {
ConfigurationValidationHelper.check(model)
.checkLong(Attributes.PRIORITY_PROPERTY, false)
.checkBoolean(Attributes.ENABLED_PROPERTY, false)
.checkBoolean(Attributes.ACTIVE_PROPERTY, false);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.provider.ProviderConfigProperty;
import static org.keycloak.provider.ProviderConfigProperty.BOOLEAN_TYPE;
import static org.keycloak.provider.ProviderConfigProperty.FILE_TYPE;
import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface Attributes {
String PRIORITY_KEY = "priority";
ProviderConfigProperty PRIORITY_PROPERTY = new ProviderConfigProperty(PRIORITY_KEY, "Priority", "Priority for the provider", STRING_TYPE, "0");
String ENABLED_KEY = "enabled";
ProviderConfigProperty ENABLED_PROPERTY = new ProviderConfigProperty(ENABLED_KEY, "Enabled", "Set if the keys are enabled", BOOLEAN_TYPE, "true");
String ACTIVE_KEY = "active";
ProviderConfigProperty ACTIVE_PROPERTY = new ProviderConfigProperty(ACTIVE_KEY, "Active", "Set if the keys can be used for signing", BOOLEAN_TYPE, "true");
String PRIVATE_KEY_KEY = "privateKey";
ProviderConfigProperty PRIVATE_KEY_PROPERTY = new ProviderConfigProperty(PRIVATE_KEY_KEY, "Private RSA Key", "Private RSA Key encoded in PEM format", FILE_TYPE, null, true);
String CERTIFICATE_KEY = "certificate";
ProviderConfigProperty CERTIFICATE_PROPERTY = new ProviderConfigProperty(CERTIFICATE_KEY, "X509 Certificate", "X509 Certificate encoded in PEM format", FILE_TYPE, null);
String KEY_SIZE_KEY = "keySize";
ProviderConfigProperty KEY_SIZE_PROPERTY = new ProviderConfigProperty(KEY_SIZE_KEY, "Keysize", "Size for the generated keys (1024, 2048 or 4096)", STRING_TYPE, null);
}

View file

@ -0,0 +1,162 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultKeyManager implements KeyManager {
private static final Logger logger = Logger.getLogger(DefaultKeyManager.class);
private final KeycloakSession session;
private final Map<String, List<KeyProvider>> providersMap = new HashMap<>();
public DefaultKeyManager(KeycloakSession session) {
this.session = session;
}
@Override
public ActiveKey getActiveKey(RealmModel realm) {
for (KeyProvider p : getProviders(realm)) {
if (p.getKid() != null && p.getPrivateKey() != null) {
if (logger.isTraceEnabled()) {
logger.tracev("Active key realm={0} kid={1}", realm.getName(), p.getKid());
}
String kid = p.getKid();
return new ActiveKey(kid, p.getPrivateKey(), p.getPublicKey(kid), p.getCertificate(kid));
}
}
throw new RuntimeException("Failed to get keys");
}
@Override
public PublicKey getPublicKey(RealmModel realm, String kid) {
if (kid == null) {
logger.warnv("KID is null, can't find public key", realm.getName(), kid);
return null;
}
for (KeyProvider p : getProviders(realm)) {
PublicKey publicKey = p.getPublicKey(kid);
if (publicKey != null) {
if (logger.isTraceEnabled()) {
logger.tracev("Found public key realm={0} kid={1}", realm.getName(), kid);
}
return publicKey;
}
}
if (logger.isTraceEnabled()) {
logger.tracev("Failed to find public key realm={0} kid={1}", realm.getName(), kid);
}
return null;
}
@Override
public Certificate getCertificate(RealmModel realm, String kid) {
if (kid == null) {
logger.warnv("KID is null, can't find public key", realm.getName(), kid);
return null;
}
for (KeyProvider p : getProviders(realm)) {
Certificate certificate = p.getCertificate(kid);
if (certificate != null) {
if (logger.isTraceEnabled()) {
logger.tracev("Found certificate realm={0} kid={1}", realm.getName(), kid);
}
return certificate;
}
}
if (logger.isTraceEnabled()) {
logger.tracev("Failed to find certificate realm={0} kid={1}", realm.getName(), kid);
}
return null;
}
@Override
public List<KeyMetadata> getKeys(RealmModel realm, boolean includeDisabled) {
List<KeyMetadata> keys = new LinkedList<>();
for (KeyProvider p : getProviders(realm)) {
if (includeDisabled) {
keys.addAll(p.getKeyMetadata());
} else {
p.getKeyMetadata().stream().filter(k -> k.getStatus() != KeyMetadata.Status.DISABLED).forEach(k -> keys.add(k));
}
}
return keys;
}
private List<KeyProvider> getProviders(RealmModel realm) {
boolean active = false;
List<KeyProvider> providers = providersMap.get(realm.getId());
if (providers == null) {
providers = new LinkedList<>();
List<ComponentModel> components = new LinkedList<>(realm.getComponents(realm.getId(), KeyProvider.class.getName()));
components.sort(new ProviderComparator());
for (ComponentModel c : components) {
try {
ProviderFactory<KeyProvider> f = session.getKeycloakSessionFactory().getProviderFactory(KeyProvider.class, c.getProviderId());
KeyProviderFactory factory = (KeyProviderFactory) f;
KeyProvider provider = factory.create(session, c);
session.enlistForClose(provider);
providers.add(provider);
if (!active && provider.getKid() != null && provider.getPrivateKey() != null) {
active = true;
}
} catch (Throwable t) {
logger.errorv(t, "Failed to load provider {0}", c.getId());
}
}
if (!active) {
providers.add(new FailsafeRsaKeyProvider());
}
providersMap.put(realm.getId(), providers);
}
return providers;
}
private class ProviderComparator implements Comparator<ComponentModel> {
@Override
public int compare(ComponentModel o1, ComponentModel o2) {
int i = Long.compare(o2.get("priority", 0l), o1.get("priority", 0l));
return i != 0 ? i : o1.getId().compareTo(o2.getId());
}
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.jboss.logging.Logger;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.Time;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class FailsafeRsaKeyProvider implements KeyProvider {
private static final Logger logger = Logger.getLogger(FailsafeRsaKeyProvider.class);
private static String KID;
private static KeyPair KEY_PAIR;
private static long EXPIRES;
private KeyPair keyPair;
private String kid;
public FailsafeRsaKeyProvider() {
logger.errorv("No active keys found, using failsafe provider, please login to admin console to add keys. Clustering is not supported.");
synchronized (FailsafeRsaKeyProvider.class) {
if (EXPIRES < Time.currentTime()) {
KEY_PAIR = KeyUtils.generateRsaKeyPair(2048);
KID = KeyUtils.createKeyId(KEY_PAIR.getPublic());
EXPIRES = Time.currentTime() + 60 * 10;
if (EXPIRES > 0) {
logger.warnv("Keys expired, re-generated kid={0}", KID);
}
}
kid = KID;
keyPair = KEY_PAIR;
}
}
@Override
public String getKid() {
return kid;
}
@Override
public PrivateKey getPrivateKey() {
return keyPair.getPrivate();
}
@Override
public PublicKey getPublicKey(String kid) {
return kid.equals(this.kid) ? keyPair.getPublic() : null;
}
@Override
public X509Certificate getCertificate(String kid) {
return null;
}
@Override
public List<KeyMetadata> getKeyMetadata() {
return Collections.emptyList();
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ConfigurationValidationHelper;
import org.keycloak.provider.ProviderConfigProperty;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class GeneratedRsaKeyProviderFactory extends AbstractRsaKeyProviderFactory {
private static final Logger logger = Logger.getLogger(GeneratedRsaKeyProviderFactory.class);
public static final String ID = "rsa-generated";
private static final String HELP_TEXT = "Generates RSA keys and creates a self-signed certificate";
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = AbstractRsaKeyProviderFactory.configurationBuilder()
.property(Attributes.KEY_SIZE_PROPERTY)
.build();
@Override
public KeyProvider create(KeycloakSession session, ComponentModel model) {
return new RsaKeyProvider(session.getContext().getRealm(), model);
}
@Override
public void validateConfiguration(KeycloakSession session, ComponentModel model) throws ComponentValidationException {
super.validateConfiguration(session, model);
ConfigurationValidationHelper.check(model)
.checkInt(Attributes.KEY_SIZE_PROPERTY, false);
int size;
if (!model.contains(Attributes.KEY_SIZE_KEY)) {
size = 2048;
model.put(Attributes.KEY_SIZE_KEY, size);
} else {
size = model.get(Attributes.KEY_SIZE_KEY, 2048);
if (size != 1024 && size != 2048 && size != 4096) {
throw new ComponentValidationException("Keysize should be 1024, 2048 or 4096");
}
}
if (!(model.contains(Attributes.PRIVATE_KEY_KEY) && model.contains(Attributes.CERTIFICATE_KEY))) {
RealmModel realm = session.realms().getRealm(model.getParentId());
generateKeys(realm, model, size);
logger.debugv("Generated keys for {0}", realm.getName());
} else {
PrivateKey privateKey = PemUtils.decodePrivateKey(model.get(Attributes.PRIVATE_KEY_KEY));
int currentSize = ((RSAPrivateKey) privateKey).getModulus().bitLength();
if (currentSize != size) {
RealmModel realm = session.realms().getRealm(model.getParentId());
generateKeys(realm, model, size);
logger.debugv("Key size changed, generating new keys for {0}", realm.getName());
}
}
}
private void generateKeys(RealmModel realm, ComponentModel model, int size) {
KeyPair keyPair;
try {
keyPair = KeyUtils.generateRsaKeyPair(size);
model.put(Attributes.PRIVATE_KEY_KEY, PemUtils.encodeKey(keyPair.getPrivate()));
} catch (Throwable t) {
throw new ComponentValidationException("Failed to generate keys", t);
}
generateCertificate(realm, model, keyPair);
}
private void generateCertificate(RealmModel realm, ComponentModel model, KeyPair keyPair) {
try {
Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, realm.getName());
model.put(Attributes.CERTIFICATE_KEY, PemUtils.encodeCertificate(certificate));
} catch (Throwable t) {
throw new ComponentValidationException("Failed to generate certificate", t);
}
}
@Override
public String getHelpText() {
return HELP_TEXT;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return CONFIG_PROPERTIES;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return ID;
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.jboss.logging.Logger;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.RealmModel;
import java.io.FileInputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JavaKeystoreKeyProvider extends AbstractRsaKeyProvider {
private static final Logger logger = Logger.getLogger(JavaKeystoreKeyProvider.class);
public JavaKeystoreKeyProvider(RealmModel realm, ComponentModel model) {
super(realm, model);
}
@Override
protected Keys loadKeys(RealmModel realm, ComponentModel model) {
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_KEY)), model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_PASSWORD_KEY).toCharArray());
PrivateKey privateKey = (PrivateKey) keyStore.getKey(model.get(JavaKeystoreKeyProviderFactory.KEY_ALIAS_KEY), model.get(JavaKeystoreKeyProviderFactory.KEY_PASSWORD_KEY).toCharArray());
PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
KeyPair keyPair = new KeyPair(publicKey, privateKey);
X509Certificate certificate;
if (model.contains(JavaKeystoreKeyProviderFactory.CERTIFICATE_ALIAS_KEY)) {
certificate = (X509Certificate) keyStore.getCertificate(model.get(JavaKeystoreKeyProviderFactory.CERTIFICATE_ALIAS_KEY));
} else {
certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, realm.getName());
}
String kid = KeyUtils.createKeyId(keyPair.getPublic());
return new Keys(kid, keyPair, certificate);
} catch (Exception e) {
throw new RuntimeException("Failed to load keys", e);
}
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ConfigurationValidationHelper;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.List;
import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactory {
public static final String ID = "java-keystore";
public static String KEYSTORE_KEY = "keystore";
public static ProviderConfigProperty KEYSTORE_PROPERTY = new ProviderConfigProperty(KEYSTORE_KEY, "Keystore", "Path to keys file", STRING_TYPE, null);
public static String KEYSTORE_PASSWORD_KEY = "keystorePassword";
public static ProviderConfigProperty KEYSTORE_PASSWORD_PROPERTY = new ProviderConfigProperty(KEYSTORE_PASSWORD_KEY, "Keystore Password", "Password for the keys", STRING_TYPE, null, true);
public static String KEY_ALIAS_KEY = "keyAlias";
public static ProviderConfigProperty KEY_ALIAS_PROPERTY = new ProviderConfigProperty(KEY_ALIAS_KEY, "Private Key Alias", "Alias for the private key", STRING_TYPE, null);
public static String KEY_PASSWORD_KEY = "keyPassword";
public static ProviderConfigProperty KEY_PASSWORD_PROPERTY = new ProviderConfigProperty(KEY_PASSWORD_KEY, "Private Key password", "Password for the private key", STRING_TYPE, null, true);
public static String CERTIFICATE_ALIAS_KEY = "certificateAlias";
public static ProviderConfigProperty CERTIFICATE_ALIAS_PROPERTY = new ProviderConfigProperty(CERTIFICATE_ALIAS_KEY, "Certificate Alias", "Alias for the certificate", STRING_TYPE, null);
private static final String HELP_TEXT = "Loads keys from a Java keys file";
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = AbstractRsaKeyProviderFactory.configurationBuilder()
.property(KEYSTORE_PROPERTY)
.property(KEYSTORE_PASSWORD_PROPERTY)
.property(KEY_ALIAS_PROPERTY)
.property(KEY_PASSWORD_PROPERTY)
.property(CERTIFICATE_ALIAS_PROPERTY)
.build();
@Override
public KeyProvider create(KeycloakSession session, ComponentModel model) {
return new JavaKeystoreKeyProvider(session.getContext().getRealm(), model);
}
@Override
public void validateConfiguration(KeycloakSession session, ComponentModel model) throws ComponentValidationException {
super.validateConfiguration(session, model);
ConfigurationValidationHelper.check(model)
.checkSingle(KEYSTORE_PROPERTY, true)
.checkSingle(KEYSTORE_PASSWORD_PROPERTY, true)
.checkSingle(KEY_ALIAS_PROPERTY, true)
.checkSingle(KEY_PASSWORD_PROPERTY, true)
.checkSingle(CERTIFICATE_ALIAS_PROPERTY, false);
try {
new JavaKeystoreKeyProvider(session.getContext().getRealm(), model)
.loadKeys(session.getContext().getRealm(), model);
} catch (Throwable t) {
throw new ComponentValidationException("Failed to load keys", t);
}
}
@Override
public String getHelpText() {
return HELP_TEXT;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return CONFIG_PROPERTIES;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return ID;
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.RealmModel;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RsaKeyProvider extends AbstractRsaKeyProvider {
public RsaKeyProvider(RealmModel realm, ComponentModel model) {
super(realm, model);
}
@Override
public Keys loadKeys(RealmModel realm, ComponentModel model) {
String privateRsaKeyPem = model.getConfig().getFirst(Attributes.PRIVATE_KEY_KEY);
String certificatePem = model.getConfig().getFirst(Attributes.CERTIFICATE_KEY);
PrivateKey privateKey = PemUtils.decodePrivateKey(privateRsaKeyPem);
PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
KeyPair keyPair = new KeyPair(publicKey, privateKey);
X509Certificate certificate = PemUtils.decodeCertificate(certificatePem);
String kid = KeyUtils.createKeyId(keyPair.getPublic());
return new Keys(kid, keyPair, certificate);
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.keys;
import org.keycloak.Config;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ConfigurationValidationHelper;
import org.keycloak.provider.ProviderConfigProperty;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RsaKeyProviderFactory extends AbstractRsaKeyProviderFactory {
public static final String ID = "rsa";
private static final String HELP_TEXT = "RSA key provider that can optionally generated a self-signed certificate";
private static final List<ProviderConfigProperty> CONFIG_PROPERTIES = AbstractRsaKeyProviderFactory.configurationBuilder()
.property(Attributes.PRIVATE_KEY_PROPERTY)
.property(Attributes.CERTIFICATE_PROPERTY)
.build();
@Override
public KeyProvider create(KeycloakSession session, ComponentModel model) {
return new RsaKeyProvider(session.getContext().getRealm(), model);
}
@Override
public void validateConfiguration(KeycloakSession session, ComponentModel model) throws ComponentValidationException {
super.validateConfiguration(session, model);
ConfigurationValidationHelper.check(model)
.checkSingle(Attributes.PRIVATE_KEY_PROPERTY, true)
.checkSingle(Attributes.CERTIFICATE_PROPERTY, false);
KeyPair keyPair;
try {
PrivateKey privateKey = PemUtils.decodePrivateKey(model.get(Attributes.PRIVATE_KEY_KEY));
PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
keyPair = new KeyPair(publicKey, privateKey);
} catch (Throwable t) {
throw new ComponentValidationException("Failed to decode private key", t);
}
if (model.contains(Attributes.CERTIFICATE_KEY)) {
Certificate certificate = null;
try {
certificate = PemUtils.decodeCertificate(model.get(Attributes.CERTIFICATE_KEY));
} catch (Throwable t) {
throw new ComponentValidationException("Failed to decode certificate", t);
}
if (certificate == null) {
throw new ComponentValidationException("Failed to decode certificate");
}
if (!certificate.getPublicKey().equals(keyPair.getPublic())) {
throw new ComponentValidationException("Certificate does not match private key");
}
} else {
try {
RealmModel realm = session.realms().getRealm(model.getParentId());
Certificate certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, realm.getName());
model.put(Attributes.CERTIFICATE_KEY, PemUtils.encodeCertificate(certificate));
} catch (Throwable t) {
throw new ComponentValidationException("Failed to generate self-signed certificate");
}
}
}
@Override
public String getHelpText() {
return HELP_TEXT;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return CONFIG_PROPERTIES;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return ID;
}
}

View file

@ -23,9 +23,9 @@ import java.util.Collections;
import java.util.Map;
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.keys.PublicKeyLoader;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@ -69,7 +69,7 @@ public class ClientPublicKeyLoader implements PublicKeyLoader {
PublicKey publicKey = getSignatureValidationKey(certInfo);
// Check if we have kid in DB, generate otherwise
String kid = certInfo.getKid() != null ? certInfo.getKid() : JWKBuilder.createKeyId(publicKey);
String kid = certInfo.getKid() != null ? certInfo.getKid() : KeyUtils.createKeyId(publicKey);
return Collections.singletonMap(kid, publicKey);
} catch (ModelException me) {
logger.warnf(me, "Unable to retrieve publicKey for verify signature of client '%s' . Error details: %s", client.getClientId(), me.getMessage());

View file

@ -22,10 +22,10 @@ import java.util.Collections;
import java.util.Map;
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.keys.PublicKeyLoader;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.utils.JWKSHttpUtils;
@ -60,7 +60,7 @@ public class OIDCIdentityProviderPublicKeyLoader implements PublicKeyLoader {
return Collections.emptyMap();
}
String kid = JWKBuilder.createKeyId(publicKey);
String kid = KeyUtils.createKeyId(publicKey);
return Collections.singletonMap(kid, publicKey);
} catch (Exception e) {
logger.warnf(e, "Unable to retrieve publicKey for verify signature of identityProvider '%s' . Error details: %s", config.getAlias(), e.getMessage());

View file

@ -118,7 +118,7 @@ public abstract class AuthorizationEndpointBase {
return processor.finishAuthentication(protocol);
} else {
try {
RestartLoginCookie.setRestartCookie(realm, clientConnection, uriInfo, clientSession);
RestartLoginCookie.setRestartCookie(session, realm, clientConnection, uriInfo, clientSession);
if (redirectToAuthentication) {
return processor.redirectToFlow();
}

View file

@ -22,8 +22,10 @@ import org.keycloak.common.ClientConnection;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.ServicesLogger;
@ -33,6 +35,7 @@ import org.keycloak.services.util.CookieHelper;
import javax.crypto.SecretKey;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.UriInfo;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
@ -112,11 +115,12 @@ public class RestartLoginCookie {
this.action = action;
}
public String encode(RealmModel realm) {
public String encode(KeycloakSession session, RealmModel realm) {
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
JWSBuilder builder = new JWSBuilder();
return builder.jsonContent(this)
.hmac256((SecretKey)realm.getCodeSecretKey());
//.rsa256(realm.getPrivateKey());
return builder.kid(keys.getKid()).jsonContent(this)
.rsa256(keys.getPrivateKey());
}
@ -133,11 +137,9 @@ public class RestartLoginCookie {
}
}
public static void setRestartCookie(RealmModel realm, ClientConnection connection, UriInfo uriInfo, ClientSessionModel clientSession) {
public static void setRestartCookie(KeycloakSession session, RealmModel realm, ClientConnection connection, UriInfo uriInfo, ClientSessionModel clientSession) {
RestartLoginCookie restart = new RestartLoginCookie(clientSession);
String encoded = restart.encode(realm);
int keySize = realm.getCodeSecret().length();
int size = encoded.length();
String encoded = restart.encode(session, realm);
String path = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
boolean secureOnly = realm.getSslRequired().isRequired(connection);
CookieHelper.addCookie(KC_RESTART, encoded, path, null, null, -1, secureOnly, true);
@ -157,13 +159,8 @@ public class RestartLoginCookie {
}
String encodedCookie = cook.getValue();
JWSInput input = new JWSInput(encodedCookie);
/*
if (!RSAProvider.verify(input, realm.getPublicKey())) {
logger.debug("Failed to verify encoded RestartLoginCookie");
return null;
}
*/
if (!HMACProvider.verify(input, (SecretKey)realm.getCodeSecretKey())) {
PublicKey publicKey = session.keys().getPublicKey(realm, input.getHeader().getKeyId());
if (!RSAProvider.verify(input, publicKey)) {
logger.debug("Failed to verify encoded RestartLoginCookie");
return null;
}

View file

@ -24,10 +24,12 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.Urls;
import org.keycloak.util.JsonSerialization;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.security.PublicKey;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -46,13 +48,33 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
public Response introspect(String token) {
try {
AccessToken toIntrospect = toAccessToken(token);
boolean valid = true;
RSATokenVerifier verifier = RSATokenVerifier.create(token)
.realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
PublicKey publicKey = session.keys().getPublicKey(realm, verifier.getHeader().getKeyId());
if (publicKey == null) {
valid = false;
} else {
try {
verifier.publicKey(publicKey);
verifier.verify();
} catch (VerificationException e) {
valid = false;
}
}
RealmModel realm = this.session.getContext().getRealm();
ObjectNode tokenMetadata;
boolean active = tokenManager.isTokenValid(session, realm, toIntrospect);
AccessToken toIntrospect = verifier.getToken();
if (active) {
if (valid) {
valid = tokenManager.isTokenValid(session, realm, toIntrospect);
}
if (valid) {
tokenMetadata = JsonSerialization.createObjectNode(toIntrospect);
tokenMetadata.put("client_id", toIntrospect.getIssuedFor());
tokenMetadata.put("username", toIntrospect.getPreferredUsername());
@ -60,7 +82,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
tokenMetadata = JsonSerialization.createObjectNode();
}
tokenMetadata.put("active", active);
tokenMetadata.put("active", valid);
return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).type(MediaType.APPLICATION_JSON_TYPE).build();
} catch (Exception e) {
@ -70,7 +92,13 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
protected AccessToken toAccessToken(String token) {
try {
return RSATokenVerifier.toAccessToken(token, realm.getPublicKey());
RSATokenVerifier verifier = RSATokenVerifier.create(token)
.realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
PublicKey publicKey = session.keys().getPublicKey(realm, verifier.getHeader().getKeyId());
verifier.publicKey(publicKey);
return verifier.verify().getToken();
} catch (VerificationException e) {
throw new ErrorResponseException("invalid_request", "Invalid token.", Response.Status.UNAUTHORIZED);
}

View file

@ -20,10 +20,10 @@ package org.keycloak.protocol.oidc;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.EventBuilder;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.keys.KeyMetadata;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint;
@ -31,6 +31,7 @@ import org.keycloak.protocol.oidc.endpoints.LoginStatusIframeEndpoint;
import org.keycloak.protocol.oidc.endpoints.LogoutEndpoint;
import org.keycloak.protocol.oidc.endpoints.TokenEndpoint;
import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.resources.RealmsResource;
@ -44,6 +45,7 @@ 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.List;
/**
* Resource class for the oauth/openid connect token service
@ -174,8 +176,16 @@ public class OIDCLoginProtocolService {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public JSONWebKeySet certs() {
List<KeyMetadata> publicKeys = session.keys().getKeys(realm, false);
JWK[] keys = new JWK[publicKeys.size()];
int i = 0;
for (KeyMetadata k : publicKeys) {
keys[i++] = JWKBuilder.create().kid(k.getKid()).rs256(k.getPublicKey());
}
JSONWebKeySet keySet = new JSONWebKeySet();
keySet.setKeys(new JWK[]{JWKBuilder.create().rs256(realm.getPublicKey())});
keySet.setKeys(keys);
return keySet;
}

View file

@ -17,11 +17,11 @@
package org.keycloak.protocol.oidc;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.jboss.logging.Logger;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.util.Time;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
@ -35,6 +35,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
@ -55,11 +56,11 @@ import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.util.TokenUtil;
import org.keycloak.common.util.Time;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
@ -80,7 +81,8 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class TokenManager {
protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
private static final Logger logger = Logger.getLogger(TokenManager.class);
private static final String JWT = "JWT";
// Harcoded for now
Algorithm jwsAlgorithm = Algorithm.RS256;
@ -213,7 +215,7 @@ public class TokenManager {
}
public RefreshResult refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken);
RefreshToken refreshToken = verifyRefreshToken(session, realm, encodedRefreshToken);
event.user(refreshToken.getSubject()).session(refreshToken.getSessionState())
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
@ -248,13 +250,13 @@ public class TokenManager {
return new RefreshResult(res, TokenUtil.TOKEN_TYPE_OFFLINE.equals(refreshToken.getType()));
}
public RefreshToken verifyRefreshToken(RealmModel realm, String encodedRefreshToken) throws OAuthErrorException {
return verifyRefreshToken(realm, encodedRefreshToken, true);
public RefreshToken verifyRefreshToken(KeycloakSession session, RealmModel realm, String encodedRefreshToken) throws OAuthErrorException {
return verifyRefreshToken(session, realm, encodedRefreshToken, true);
}
public RefreshToken verifyRefreshToken(RealmModel realm, String encodedRefreshToken, boolean checkExpiration) throws OAuthErrorException {
public RefreshToken verifyRefreshToken(KeycloakSession session, RealmModel realm, String encodedRefreshToken, boolean checkExpiration) throws OAuthErrorException {
try {
RefreshToken refreshToken = toRefreshToken(realm, encodedRefreshToken);
RefreshToken refreshToken = toRefreshToken(session, realm, encodedRefreshToken);
if (checkExpiration) {
if (refreshToken.getExpiration() != 0 && refreshToken.isExpired()) {
@ -272,21 +274,21 @@ public class TokenManager {
}
}
public RefreshToken toRefreshToken(RealmModel realm, String encodedRefreshToken) throws JWSInputException, OAuthErrorException {
public RefreshToken toRefreshToken(KeycloakSession session, RealmModel realm, String encodedRefreshToken) throws JWSInputException, OAuthErrorException {
JWSInput jws = new JWSInput(encodedRefreshToken);
if (!RSAProvider.verify(jws, realm.getPublicKey())) {
if (!RSAProvider.verify(jws, session.keys().getPublicKey(realm, jws.getHeader().getKeyId()))) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token");
}
return jws.readJsonContent(RefreshToken.class);
}
public IDToken verifyIDToken(RealmModel realm, String encodedIDToken) throws OAuthErrorException {
public IDToken verifyIDToken(KeycloakSession session, RealmModel realm, String encodedIDToken) throws OAuthErrorException {
try {
JWSInput jws = new JWSInput(encodedIDToken);
IDToken idToken;
if (!RSAProvider.verify(jws, realm.getPublicKey())) {
if (!RSAProvider.verify(jws, session.keys().getPublicKey(realm, jws.getHeader().getKeyId()))) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid IDToken");
}
idToken = jws.readJsonContent(IDToken.class);
@ -499,7 +501,7 @@ public class TokenManager {
public AccessToken transformAccessToken(KeycloakSession session, AccessToken token, RealmModel realm, ClientModel client, UserModel user,
UserSessionModel userSession, ClientSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = new ClientSessionCode(realm, clientSession).getRequestedProtocolMappers();
Set<ProtocolMapperModel> mappings = new ClientSessionCode(session, realm, clientSession).getRequestedProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
@ -513,7 +515,7 @@ public class TokenManager {
public AccessToken transformUserInfoAccessToken(KeycloakSession session, AccessToken token, RealmModel realm, ClientModel client, UserModel user,
UserSessionModel userSession, ClientSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = new ClientSessionCode(realm, clientSession).getRequestedProtocolMappers();
Set<ProtocolMapperModel> mappings = new ClientSessionCode(session, realm, clientSession).getRequestedProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
@ -533,7 +535,7 @@ public class TokenManager {
public void transformIDToken(KeycloakSession session, IDToken token, RealmModel realm, ClientModel client, UserModel user,
UserSessionModel userSession, ClientSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = new ClientSessionCode(realm, clientSession).getRequestedProtocolMappers();
Set<ProtocolMapperModel> mappings = new ClientSessionCode(session, realm, clientSession).getRequestedProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
@ -618,13 +620,9 @@ public class TokenManager {
}
public String encodeToken(RealmModel realm, Object token) {
String encodedToken = new JWSBuilder()
.type(OAuth2Constants.JWT)
.kid(realm.getKeyId())
.jsonContent(token)
.sign(jwsAlgorithm, realm.getPrivateKey());
return encodedToken;
public String encodeToken(KeycloakSession session, RealmModel realm, Object token) {
KeyManager.ActiveKey activeKey = session.keys().getActiveKey(realm);
return new JWSBuilder().type(JWT).kid(activeKey.getKid()).jsonContent(token).sign(jwsAlgorithm, activeKey.getPrivateKey());
}
public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
@ -731,6 +729,8 @@ public class TokenManager {
public AccessTokenResponse build() {
KeyManager.ActiveKey activeKey = session.keys().getActiveKey(realm);
if (accessToken != null) {
event.detail(Details.TOKEN_ID, accessToken.getId());
}
@ -746,7 +746,7 @@ public class TokenManager {
AccessTokenResponse res = new AccessTokenResponse();
if (accessToken != null) {
String encodedToken = new JWSBuilder().type(OAuth2Constants.JWT).kid(realm.getKeyId()).jsonContent(accessToken).sign(jwsAlgorithm, realm.getPrivateKey());
String encodedToken = new JWSBuilder().type(JWT).kid(activeKey.getKid()).jsonContent(accessToken).sign(jwsAlgorithm, activeKey.getPrivateKey());
res.setToken(encodedToken);
res.setTokenType("bearer");
res.setSessionState(accessToken.getSessionState());
@ -764,11 +764,11 @@ public class TokenManager {
}
if (idToken != null) {
String encodedToken = new JWSBuilder().type(OAuth2Constants.JWT).kid(realm.getKeyId()).jsonContent(idToken).sign(jwsAlgorithm, realm.getPrivateKey());
String encodedToken = new JWSBuilder().type(JWT).kid(activeKey.getKid()).jsonContent(idToken).sign(jwsAlgorithm, activeKey.getPrivateKey());
res.setIdToken(encodedToken);
}
if (refreshToken != null) {
String encodedToken = new JWSBuilder().type(OAuth2Constants.JWT).kid(realm.getKeyId()).jsonContent(refreshToken).sign(jwsAlgorithm, realm.getPrivateKey());
String encodedToken = new JWSBuilder().type(JWT).kid(activeKey.getKid()).jsonContent(refreshToken).sign(jwsAlgorithm, activeKey.getPrivateKey());
res.setRefreshToken(encodedToken);
if (refreshToken.getExpiration() != 0) {
res.setRefreshExpiresIn(refreshToken.getExpiration() - Time.currentTime());

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