Merge pull request #765 from patriot1burke/master
app cert generation support
This commit is contained in:
commit
582ee8897e
29 changed files with 748 additions and 11 deletions
3
connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
Normal file → Executable file
3
connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
Normal file → Executable file
|
@ -32,6 +32,9 @@
|
|||
<addColumn tableName="CLIENT_SESSION">
|
||||
<column name="REALM_ID" type="VARCHAR(255)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="REALM">
|
||||
<column name="CERTIFICATE" type="VARCHAR(2048)"/>
|
||||
</addColumn>
|
||||
<addPrimaryKey columnNames="CLIENT_ID, NAME" constraintName="CONSTRAINT_3C" tableName="CLIENT_ATTRIBUTES"/>
|
||||
<addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTRAINT_5E" tableName="CLIENT_SESSION_NOTE"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_ATTRIBUTES" constraintName="FK3C47C64BEACCA966" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
|
|
|
@ -43,6 +43,7 @@ public class RealmRepresentation {
|
|||
|
||||
protected String privateKey;
|
||||
protected String publicKey;
|
||||
protected String certificate;
|
||||
protected RolesRepresentation roles;
|
||||
protected List<String> defaultRoles;
|
||||
protected Set<String> requiredCredentials;
|
||||
|
@ -220,6 +221,14 @@ public class RealmRepresentation {
|
|||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public String getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public void setCertificate(String certificate) {
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
public Boolean isPasswordCredentialGrantAllowed() {
|
||||
return passwordCredentialGrantAllowed;
|
||||
}
|
||||
|
|
63
core/src/main/java/org/keycloak/util/CertificateUtils.java
Executable file
63
core/src/main/java/org/keycloak/util/CertificateUtils.java
Executable file
|
@ -0,0 +1,63 @@
|
|||
package org.keycloak.util;
|
||||
|
||||
import org.bouncycastle.asn1.x509.X509Extensions;
|
||||
import org.bouncycastle.x509.X509V1CertificateGenerator;
|
||||
import org.bouncycastle.x509.X509V3CertificateGenerator;
|
||||
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
|
||||
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CertificateUtils {
|
||||
public static X509Certificate generateV3Certificate(KeyPair keyPair, PrivateKey caPrivateKey, X509Certificate caCert, String subject) throws Exception {
|
||||
|
||||
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
|
||||
X500Principal subjectName = new X500Principal("CN=" + subject);
|
||||
|
||||
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
|
||||
certGen.setSerialNumber(serialNumber);
|
||||
certGen.setIssuerDN(caCert.getSubjectX500Principal());
|
||||
certGen.setNotBefore(new Date(System.currentTimeMillis() - 100000));
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.YEAR, 10);
|
||||
certGen.setNotAfter(calendar.getTime());
|
||||
certGen.setSubjectDN(subjectName);
|
||||
certGen.setPublicKey(keyPair.getPublic());
|
||||
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
|
||||
|
||||
certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
|
||||
new AuthorityKeyIdentifierStructure(caCert));
|
||||
certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
|
||||
new SubjectKeyIdentifierStructure(keyPair.getPublic()));
|
||||
|
||||
X509Certificate cert = certGen.generate(caPrivateKey, "BC"); // note: private key of CA
|
||||
return cert;
|
||||
}
|
||||
|
||||
public static X509Certificate generateV1SelfSignedCertificate(KeyPair keyPair, String subject) throws Exception {
|
||||
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
|
||||
X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
|
||||
X500Principal subjectPrincipal = new X500Principal("CN=" + subject);
|
||||
certGen.setSerialNumber(serialNumber);
|
||||
certGen.setIssuerDN(subjectPrincipal);
|
||||
certGen.setNotBefore(new Date(System.currentTimeMillis() - 100000));
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.YEAR, 10);
|
||||
certGen.setNotAfter(calendar.getTime());
|
||||
certGen.setSubjectDN(subjectPrincipal);
|
||||
certGen.setPublicKey(keyPair.getPublic());
|
||||
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
|
||||
X509Certificate cert = certGen.generate(keyPair.getPrivate(), "BC");
|
||||
return cert;
|
||||
}
|
||||
}
|
|
@ -18,4 +18,5 @@ public class KeystoreUtil {
|
|||
trustStream.close();
|
||||
return trustStore;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -440,6 +440,18 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'ApplicationCredentialsCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/certificate', {
|
||||
templateUrl : 'partials/application-keys.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationCertificateCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/roles', {
|
||||
templateUrl : 'partials/application-role-list.html',
|
||||
resolve : {
|
||||
|
|
|
@ -43,6 +43,63 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
|
|||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, realm, application,
|
||||
ApplicationCertificate, ApplicationCertificateGenerate,
|
||||
ApplicationCertificateDownload, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.application = application;
|
||||
var jks = {
|
||||
keyAlias: application.name,
|
||||
realmAlias: realm.realm
|
||||
};
|
||||
|
||||
$scope.jks = jks;
|
||||
|
||||
var keyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id },
|
||||
function() {
|
||||
$scope.keyInfo = keyInfo;
|
||||
}
|
||||
);
|
||||
|
||||
$scope.generate = function() {
|
||||
var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id },
|
||||
function() {
|
||||
Notifications.success('Client keypair and cert has been changed.');
|
||||
$scope.keyInfo = keyInfo;
|
||||
},
|
||||
function() {
|
||||
Notifications.error("Client keypair and cert was not changed due to a problem.");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.downloadJKS = function() {
|
||||
$http({
|
||||
url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/download',
|
||||
method: 'POST',
|
||||
responseType: 'arraybuffer',
|
||||
data: $scope.jks,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/octet-stream'
|
||||
}
|
||||
}).success(function(data){
|
||||
var blob = new Blob([data], {
|
||||
type: 'application/octet-stream'
|
||||
});
|
||||
saveAs(blob, 'keystore' + '.jks');
|
||||
}).error(function(){
|
||||
Notifications.error("Error downloading.");
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationSessionsCtrl', function($scope, realm, sessionCount, application,
|
||||
ApplicationUserSessions) {
|
||||
$scope.realm = realm;
|
||||
|
@ -339,12 +396,21 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
$scope.save = function() {
|
||||
if ($scope.samlServerSignature == true) {
|
||||
$scope.application.attributes["samlServerSignature"] = "true";
|
||||
} else {
|
||||
$scope.application.attributes["samlServerSignature"] = "false";
|
||||
|
||||
}
|
||||
if ($scope.samlClientSignature == true) {
|
||||
$scope.application.attributes["samlClientSignature"] = "true";
|
||||
} else {
|
||||
$scope.application.attributes["samlClientSignature"] = "false";
|
||||
|
||||
}
|
||||
if ($scope.samlServerEncrypt == true) {
|
||||
$scope.application.attributes["samlServerEncrypt"] = "true";
|
||||
} else {
|
||||
$scope.application.attributes["samlServerEncrypt"] = "false";
|
||||
|
||||
}
|
||||
|
||||
$scope.application.protocol = $scope.protocol;
|
||||
|
|
|
@ -693,7 +693,37 @@ module.factory('ApplicationPushRevocation', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationCertificate', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates', {
|
||||
realm : '@realm',
|
||||
application : "@application"
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationCertificateGenerate', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/generate', {
|
||||
realm : '@realm',
|
||||
application : "@application"
|
||||
},
|
||||
{
|
||||
generate : {
|
||||
method : 'POST'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationCertificateDownload', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/download', {
|
||||
realm : '@realm',
|
||||
application : "@application"
|
||||
},
|
||||
{
|
||||
download : {
|
||||
method : 'POST',
|
||||
responseType: 'arraybuffer'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('Application', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application', {
|
||||
|
@ -783,6 +813,21 @@ module.factory('OAuthClientCredentials', function($resource) {
|
|||
|
||||
});
|
||||
|
||||
module.factory('OAuthCertificate', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/certificates', {
|
||||
realm : '@realm',
|
||||
oauth : '@oauth'
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('OAuthCertificateDownload', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/certificates/download', {
|
||||
realm : '@realm',
|
||||
oauth : '@oauth'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
module.factory('OAuthClientRealmScopeMapping', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/realm', {
|
||||
realm : '@realm',
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-sm-9" role="main">
|
||||
<kc-navigation-application></kc-navigation-application>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb" data-ng-hide="create">
|
||||
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
|
||||
<li class="active">Keys</li>
|
||||
</ol>
|
||||
<h2><span>{{application.name}}</span> Key Pair and Certificate <span tooltip-placement="right" tooltip="Application's key pair and certificate. Used for more confidential interaction between application and auth server." class="fa fa-info-circle"></span></h2>
|
||||
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
|
||||
<fieldset class="form-group col-sm-10" data-ng-hide="!keyInfo.privateKey">
|
||||
<legend collapsed><span class="text">Java Keystore Download</span> <span tooltip-placement="right" tooltip="Client key pair, cert, and realm certificate will be stuffed into a Java keystore that you can use in your applications." class="fa fa-info-circle"></span></legend>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="keyAlias">Key Alias</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="keyAlias" name="keyAlias" data-ng-model="jks.keyAlias" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="keyPassword">Key Password</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="keyPassword" name="keyPassword" data-ng-model="jks.keyPassword" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="realmAlias">Realm Certificate Alias</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="realmAlias" name="realmAlias" data-ng-model="jks.realmAlias" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="storePassword">Store Password</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="storePassword" name="storePassword" data-ng-model="jks.storePassword" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-primary" type="submit" data-ng-click="downloadJKS()">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="form-group col-sm-10">
|
||||
<legend><span class="text">Keys and Certificate</span> <span tooltip-placement="right" tooltip="Keys and cert in PEM format." class="fa fa-info-circle"></span></legend>
|
||||
<div class="form-group" data-ng-hide="!keyInfo.privateKey">
|
||||
<label class="col-sm-2 control-label" for="publicKey">Private key</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" id="Private" name="publicKey" class="form-control" rows="5"
|
||||
kc-select-action="click" readonly>{{keyInfo.privateKey}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-hide="!keyInfo.privateKey">
|
||||
<label class="col-sm-2 control-label" for="publicKey">Public key</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" id="publicKey" name="publicKey" class="form-control" rows="5"
|
||||
kc-select-action="click" readonly>{{keyInfo.publicKey}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-hide="!keyInfo.privateKey">
|
||||
<label class="col-sm-2 control-label" for="publicKey">Certificate</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" id="certificate" name="certificate" class="form-control" rows="5"
|
||||
kc-select-action="click" readonly>{{keyInfo.certificate}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="access.manageRealm">
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-primary" type="submit" data-ng-click="generate()">Generate new keys</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -5,6 +5,9 @@
|
|||
<div id="content">
|
||||
<h2><span>{{realm.realm}}</span> Realm Public Key <span tooltip-placement="right" tooltip="Realm's public key. This is used to verify any signed tokens or documents created by the realm." class="fa fa-info-circle"></span></h2>
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
|
||||
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
|
||||
</div>
|
||||
<fieldset class="border-top">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="publicKey">Public key</label>
|
||||
|
@ -14,10 +17,15 @@
|
|||
kc-select-action="click" readonly>{{realm.publicKey}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
|
||||
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="publicKey">Certificate</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" id="certificate" name="certificate" class="form-control" rows="5"
|
||||
kc-select-action="click" readonly>{{realm.certificate}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -1,10 +1,11 @@
|
|||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
|
||||
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">Settings</a></li>
|
||||
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.bearerOnly && !application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'certificate'}" data-ng-show="application.protocol == 'saml' && application.attributes['samlClientSignature'] == 'true'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/certificate">Application Keys</a></li>
|
||||
<li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/roles">Roles</a></li>
|
||||
<li ng-class="{active: path[4] == 'claims'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/claims">Claims</a></li>
|
||||
<li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/revocation">Revocation</a></li>
|
||||
<li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/sessions">Sessions</a></li>
|
||||
<li ng-class="{active: path[4] == 'installation'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/installation">Installation</a></li>
|
||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/installation">Installation</a></li>
|
||||
</ul>
|
|
@ -8,6 +8,13 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientModel {
|
||||
|
||||
// COMMON ATTRIBUTES
|
||||
|
||||
String PRIVATE_KEY = "privateKey";
|
||||
String PUBLIC_KEY = "publicKey";
|
||||
String X509CERTIFICATE = "X509Certificate";
|
||||
|
||||
/**
|
||||
* Internal database key
|
||||
*
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.enums.SslRequired;
|
|||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -96,6 +97,11 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setPublicKey(PublicKey publicKey);
|
||||
|
||||
X509Certificate getCertificate();
|
||||
void setCertificate(X509Certificate certificate);
|
||||
String getCertificatePem();
|
||||
void setCertificatePem(String certificate);
|
||||
|
||||
PrivateKey getPrivateKey();
|
||||
|
||||
void setPrivateKey(PrivateKey privateKey);
|
||||
|
|
|
@ -40,6 +40,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
|
||||
private String publicKeyPem;
|
||||
private String privateKeyPem;
|
||||
private String certificatePem;
|
||||
|
||||
private String loginTheme;
|
||||
private String accountTheme;
|
||||
|
@ -381,4 +382,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
public void setUserFederationProviders(List<UserFederationProviderEntity> userFederationProviders) {
|
||||
this.userFederationProviders = userFederationProviders;
|
||||
}
|
||||
|
||||
public String getCertificatePem() {
|
||||
return certificatePem;
|
||||
}
|
||||
|
||||
public void setCertificatePem(String certificatePem) {
|
||||
this.certificatePem = certificatePem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.util.CertificateUtils;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -22,6 +23,7 @@ import java.security.KeyPairGenerator;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -51,6 +53,19 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static X509Certificate getCertificate(String cert) {
|
||||
if (cert != null) {
|
||||
try {
|
||||
return PemUtils.decodeCertificate(cert);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static PrivateKey getPrivateKey(String privateKeyPem) {
|
||||
if (privateKeyPem != null) {
|
||||
try {
|
||||
|
@ -75,6 +90,19 @@ public final class KeycloakModelUtils {
|
|||
return PemUtils.removeBeginEnd(s);
|
||||
}
|
||||
|
||||
public static String getPemFromCertificate(X509Certificate certificate) {
|
||||
StringWriter writer = new StringWriter();
|
||||
PEMWriter pemWriter = new PEMWriter(writer);
|
||||
try {
|
||||
pemWriter.writeObject(certificate);
|
||||
pemWriter.flush();
|
||||
} 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 {
|
||||
|
@ -84,6 +112,46 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void generateClientKeyPairCertificate(ClientModel client) {
|
||||
KeyPair keyPair = null;
|
||||
try {
|
||||
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
X509Certificate certificate = null;
|
||||
try {
|
||||
certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, client.getClientId());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String privateKeyPem = KeycloakModelUtils.getPemFromKey(keyPair.getPrivate());
|
||||
String publicKeyPem = KeycloakModelUtils.getPemFromKey(keyPair.getPublic());
|
||||
String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
|
||||
|
||||
client.setAttribute(ClientModel.PRIVATE_KEY, privateKeyPem);
|
||||
client.setAttribute(ClientModel.PUBLIC_KEY, publicKeyPem);
|
||||
client.setAttribute(ClientModel.X509CERTIFICATE, certPem);
|
||||
|
||||
}
|
||||
|
||||
public static UserCredentialModel generateSecret(ClientModel app) {
|
||||
|
|
|
@ -86,6 +86,11 @@ public class ModelToRepresentation {
|
|||
rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
|
||||
rep.setPublicKey(realm.getPublicKeyPem());
|
||||
rep.setPrivateKey(realm.getPrivateKeyPem());
|
||||
String privateKeyPem = realm.getPrivateKeyPem();
|
||||
if (realm.getCertificatePem() == null && privateKeyPem != null) {
|
||||
KeycloakModelUtils.generateRealmCertificate(realm);
|
||||
}
|
||||
rep.setCertificate(realm.getCertificatePem());
|
||||
rep.setPasswordCredentialGrantAllowed(realm.isPasswordCredentialGrantAllowed());
|
||||
rep.setRegistrationAllowed(realm.isRegistrationAllowed());
|
||||
rep.setRememberMe(realm.isRememberMe());
|
||||
|
@ -114,8 +119,6 @@ public class ModelToRepresentation {
|
|||
rep.setPasswordPolicy(realm.getPasswordPolicy().toString());
|
||||
}
|
||||
|
||||
ApplicationModel accountManagementApplication = realm.getApplicationNameMap().get(Constants.ACCOUNT_MANAGEMENT_APP);
|
||||
|
||||
List<String> defaultRoles = realm.getDefaultRoles();
|
||||
if (!defaultRoles.isEmpty()) {
|
||||
List<String> roleStrings = new ArrayList<String>();
|
||||
|
|
|
@ -84,6 +84,11 @@ public class RepresentationToModel {
|
|||
newRealm.setPrivateKeyPem(rep.getPrivateKey());
|
||||
newRealm.setPublicKeyPem(rep.getPublicKey());
|
||||
}
|
||||
if (rep.getCertificate() == null) {
|
||||
KeycloakModelUtils.generateRealmCertificate(newRealm);
|
||||
} else {
|
||||
newRealm.setCertificatePem(rep.getCertificate());
|
||||
}
|
||||
if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme());
|
||||
if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme());
|
||||
if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme());
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
@ -33,6 +34,7 @@ public class RealmAdapter implements RealmModel {
|
|||
protected RealmCache cache;
|
||||
protected volatile transient PublicKey publicKey;
|
||||
protected volatile transient PrivateKey privateKey;
|
||||
protected volatile transient X509Certificate certificate;
|
||||
|
||||
public RealmAdapter(CachedRealm cached, CacheRealmProvider cacheSession) {
|
||||
this.cached = cached;
|
||||
|
@ -331,6 +333,33 @@ public class RealmAdapter implements RealmModel {
|
|||
setPublicKeyPem(publicKeyPem);
|
||||
}
|
||||
|
||||
@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 certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
|
||||
setCertificatePem(certPem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCertificatePem() {
|
||||
if (updated != null) return updated.getCertificatePem();
|
||||
return cached.getCertificatePem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCertificatePem(String certificate) {
|
||||
getDelegateForUpdate();
|
||||
updated.setCertificatePem(certificate);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey() {
|
||||
if (privateKey != null) return privateKey;
|
||||
|
@ -345,6 +374,8 @@ public class RealmAdapter implements RealmModel {
|
|||
setPrivateKeyPem(privateKeyPem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<RequiredCredentialModel> getRequiredCredentials() {
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ public class CachedRealm {
|
|||
|
||||
private String publicKeyPem;
|
||||
private String privateKeyPem;
|
||||
private String certificatePem;
|
||||
|
||||
private String loginTheme;
|
||||
private String accountTheme;
|
||||
|
@ -113,6 +114,7 @@ public class CachedRealm {
|
|||
|
||||
publicKeyPem = model.getPublicKeyPem();
|
||||
privateKeyPem = model.getPrivateKeyPem();
|
||||
certificatePem = model.getCertificatePem();
|
||||
|
||||
loginTheme = model.getLoginTheme();
|
||||
accountTheme = model.getAccountTheme();
|
||||
|
@ -328,4 +330,8 @@ public class CachedRealm {
|
|||
public List<UserFederationProviderModel> getUserFederationProviders() {
|
||||
return userFederationProviders;
|
||||
}
|
||||
|
||||
public String getCertificatePem() {
|
||||
return certificatePem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.TypedQuery;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -44,6 +45,7 @@ public class RealmAdapter implements RealmModel {
|
|||
protected EntityManager em;
|
||||
protected volatile transient PublicKey publicKey;
|
||||
protected volatile transient PrivateKey privateKey;
|
||||
protected volatile transient X509Certificate certificate;
|
||||
protected KeycloakSession session;
|
||||
private PasswordPolicy passwordPolicy;
|
||||
|
||||
|
@ -367,6 +369,32 @@ public class RealmAdapter implements RealmModel {
|
|||
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();
|
||||
|
|
|
@ -80,6 +80,8 @@ public class RealmEntity {
|
|||
protected String publicKeyPem;
|
||||
@Column(name="PRIVATE_KEY", length = 2048)
|
||||
protected String privateKeyPem;
|
||||
@Column(name="CERTIFICATE", length = 2048)
|
||||
protected String certificatePem;
|
||||
|
||||
@Column(name="LOGIN_THEME")
|
||||
protected String loginTheme;
|
||||
|
@ -432,5 +434,13 @@ public class RealmEntity {
|
|||
public void setAttributes(Collection<RealmAttributeEntity> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public String getCertificatePem() {
|
||||
return certificatePem;
|
||||
}
|
||||
|
||||
public void setCertificatePem(String certificatePem) {
|
||||
this.certificatePem = certificatePem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -49,6 +50,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
protected volatile transient PublicKey publicKey;
|
||||
protected volatile transient PrivateKey privateKey;
|
||||
protected volatile transient X509Certificate certificate;
|
||||
|
||||
private volatile transient PasswordPolicy passwordPolicy;
|
||||
private volatile transient KeycloakSession session;
|
||||
|
@ -350,6 +352,33 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
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();
|
||||
|
|
|
@ -94,7 +94,6 @@ public class ApplicationResource {
|
|||
public Response update(final ApplicationRepresentation rep) {
|
||||
auth.requireManage();
|
||||
|
||||
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
|
||||
try {
|
||||
RepresentationToModel.updateApplication(rep, application);
|
||||
return Response.noContent().build();
|
||||
|
@ -118,6 +117,11 @@ public class ApplicationResource {
|
|||
return ModelToRepresentation.toRepresentation(application);
|
||||
}
|
||||
|
||||
@Path("certificates")
|
||||
public ClientCertificateResource getCertficateResource() {
|
||||
return new ClientCertificateResource(realm, auth, application, session);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return keycloak.json file for this application to be used to configure the adapter of that application.
|
||||
|
|
|
@ -16,4 +16,10 @@ public class ApplicationsByIdResource extends ApplicationsResource {
|
|||
protected ApplicationModel getApplicationByPathParam(String id) {
|
||||
return realm.getApplicationById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getApplicationPath(ApplicationModel applicationModel) {
|
||||
return applicationModel.getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -88,12 +88,16 @@ public class ApplicationsResource {
|
|||
|
||||
try {
|
||||
ApplicationModel applicationModel = RepresentationToModel.createApplication(realm, rep, true);
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(applicationModel.getName()).build()).build();
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(getApplicationPath(applicationModel)).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return Flows.errors().exists("Application " + rep.getName() + " already exists");
|
||||
}
|
||||
}
|
||||
|
||||
protected String getApplicationPath(ApplicationModel applicationModel) {
|
||||
return applicationModel.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base path for managing a specific application.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.BadRequestException;
|
||||
import org.jboss.resteasy.spi.NotAcceptableException;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
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:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientCertificateResource {
|
||||
protected RealmModel realm;
|
||||
private RealmAuth auth;
|
||||
protected ClientModel client;
|
||||
protected KeycloakSession session;
|
||||
|
||||
public ClientCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.client = client;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public static class ClientKeyPairInfo {
|
||||
protected String privateKey;
|
||||
protected String publicKey;
|
||||
protected String certificate;
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public void setPrivateKey(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ClientKeyPairInfo getKeyInfo() {
|
||||
ClientKeyPairInfo info = new ClientKeyPairInfo();
|
||||
info.setCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
|
||||
info.setPrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
|
||||
info.setPublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@POST
|
||||
@NoCache
|
||||
@Path("generate")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ClientKeyPairInfo generate() {
|
||||
auth.requireManage();
|
||||
|
||||
KeycloakModelUtils.generateClientKeyPairCertificate(client);
|
||||
ClientKeyPairInfo info = new ClientKeyPairInfo();
|
||||
info.setCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
|
||||
info.setPrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
|
||||
info.setPublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
|
||||
return info;
|
||||
}
|
||||
|
||||
public static class KeyStoreConfig {
|
||||
protected Boolean realmCertificate;
|
||||
protected String storePassword;
|
||||
protected String keyPassword;
|
||||
protected String keyAlias;
|
||||
protected String realmAlias;
|
||||
protected String format;
|
||||
|
||||
public Boolean isRealmCertificate() {
|
||||
return realmCertificate;
|
||||
}
|
||||
|
||||
public void setRealmCertificate(Boolean realmCertificate) {
|
||||
this.realmCertificate = realmCertificate;
|
||||
}
|
||||
|
||||
public String getStorePassword() {
|
||||
return storePassword;
|
||||
}
|
||||
|
||||
public void setStorePassword(String storePassword) {
|
||||
this.storePassword = storePassword;
|
||||
}
|
||||
|
||||
public String getKeyPassword() {
|
||||
return keyPassword;
|
||||
}
|
||||
|
||||
public void setKeyPassword(String keyPassword) {
|
||||
this.keyPassword = keyPassword;
|
||||
}
|
||||
|
||||
public String getKeyAlias() {
|
||||
return keyAlias;
|
||||
}
|
||||
|
||||
public void setKeyAlias(String keyAlias) {
|
||||
this.keyAlias = keyAlias;
|
||||
}
|
||||
|
||||
public String getRealmAlias() {
|
||||
return realmAlias;
|
||||
}
|
||||
|
||||
public void setRealmAlias(String realmAlias) {
|
||||
this.realmAlias = realmAlias;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@NoCache
|
||||
@Path("/download")
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public byte[] getJavaKeyStore(final KeyStoreConfig config) {
|
||||
auth.requireView();
|
||||
if (config.getFormat() != null && !config.getFormat().equals("jks")) {
|
||||
throw new NotAcceptableException("Only support jks format.");
|
||||
}
|
||||
if (client.getAttribute(ClientModel.PRIVATE_KEY) == null) {
|
||||
throw new NotFoundException("keypair not generated for client");
|
||||
}
|
||||
if (config.getKeyPassword() == null) {
|
||||
throw new BadRequestException("Need to specify a key password for jks download");
|
||||
}
|
||||
if (config.getStorePassword() == null) {
|
||||
throw new BadRequestException("Need to specify a store password for jks download");
|
||||
}
|
||||
final KeyStore keyStore;
|
||||
try {
|
||||
keyStore = KeyStore.getInstance("JKS");
|
||||
keyStore.load(null, null);
|
||||
String keyAlias = config.getKeyAlias();
|
||||
if (keyAlias == null) keyAlias = client.getClientId();
|
||||
PrivateKey privateKey = PemUtils.decodePrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
|
||||
X509Certificate clientCert = PemUtils.decodeCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
|
||||
|
||||
|
||||
Certificate[] chain = {clientCert};
|
||||
|
||||
keyStore.setKeyEntry(keyAlias, privateKey, config.getKeyPassword().trim().toCharArray(), chain);
|
||||
|
||||
if (config.isRealmCertificate() == null || config.isRealmCertificate().booleanValue()) {
|
||||
X509Certificate certificate = realm.getCertificate();
|
||||
if (certificate == null) {
|
||||
KeycloakModelUtils.generateRealmCertificate(realm);
|
||||
certificate = realm.getCertificate();
|
||||
}
|
||||
String certificateAlias = config.getRealmAlias();
|
||||
if (certificateAlias == null) certificateAlias = realm.getName();
|
||||
keyStore.setCertificateEntry(certificateAlias, certificate);
|
||||
|
||||
}
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
keyStore.store(stream, config.getStorePassword().trim().toCharArray());
|
||||
stream.flush();
|
||||
stream.close();
|
||||
byte[] rtn = stream.toByteArray();
|
||||
return rtn;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -72,6 +72,13 @@ public class OAuthClientResource {
|
|||
return new ClaimResource(oauthClient, auth);
|
||||
}
|
||||
|
||||
@Path("certificates")
|
||||
public ClientCertificateResource getCertficateResource() {
|
||||
return new ClientCertificateResource(realm, auth, oauthClient, session);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Update the oauth client
|
||||
*
|
||||
|
|
|
@ -35,8 +35,14 @@ public class OAuthClientsByIdResource extends OAuthClientsResource {
|
|||
super(realm, auth, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuthClientModel getOAuthClientModel(String id) {
|
||||
return realm.getOAuthClientById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getClientPath(OAuthClientModel oauth) {
|
||||
return oauth.getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -90,12 +90,16 @@ public class OAuthClientsResource {
|
|||
|
||||
try {
|
||||
OAuthClientModel oauth = RepresentationToModel.createOAuthClient(rep, realm);
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(oauth.getClientId()).build()).build();
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(getClientPath(oauth)).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return Flows.errors().exists("Client " + rep.getName() + " already exists");
|
||||
}
|
||||
}
|
||||
|
||||
protected String getClientPath(OAuthClientModel oauth) {
|
||||
return oauth.getClientId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base path to manage one specific oauth client
|
||||
*
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.junit.Test;
|
|||
import org.keycloak.enums.SslRequired;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
|
@ -24,8 +25,7 @@ public class ModelTest extends AbstractModelTest {
|
|||
realm.setPasswordPolicy(new PasswordPolicy("length"));
|
||||
realm.setAccessCodeLifespan(1001);
|
||||
realm.setAccessCodeLifespanUserAction(1002);
|
||||
realm.setPublicKeyPem("0234234");
|
||||
realm.setPrivateKeyPem("1234234");
|
||||
KeycloakModelUtils.generateRealmKeys(realm);
|
||||
realm.addDefaultRole("default-role");
|
||||
|
||||
HashMap<String, String> smtp = new HashMap<String,String>();
|
||||
|
|
Loading…
Reference in a new issue