saml key refactor

This commit is contained in:
Bill Burke 2014-10-24 10:58:32 -04:00
parent ad7e2cd12e
commit ce76270ad8
26 changed files with 1688 additions and 130 deletions

View file

@ -476,8 +476,8 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'ApplicationClusteringNodeCtrl' controller : 'ApplicationClusteringNodeCtrl'
}) })
.when('/realms/:realm/applications/:application/certificate', { .when('/realms/:realm/applications/:application/saml/keys', {
templateUrl : 'partials/application-keys.html', templateUrl : 'partials/application-saml-keys.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
return RealmLoader(); return RealmLoader();
@ -486,7 +486,31 @@ module.config([ '$routeProvider', function($routeProvider) {
return ApplicationLoader(); return ApplicationLoader();
} }
}, },
controller : 'ApplicationCertificateCtrl' controller : 'ApplicationSamlKeyCtrl'
})
.when('/realms/:realm/applications/:application/saml/:keyType/import/:attribute', {
templateUrl : 'partials/application-saml-key-import.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
}
},
controller : 'ApplicationCertificateImportCtrl'
})
.when('/realms/:realm/applications/:application/saml/:keyType/export/:attribute', {
templateUrl : 'partials/application-saml-key-export.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
}
},
controller : 'ApplicationCertificateExportCtrl'
}) })
.when('/realms/:realm/applications/:application/roles', { .when('/realms/:realm/applications/:application/roles', {
templateUrl : 'partials/application-role-list.html', templateUrl : 'partials/application-role-list.html',

View file

@ -43,6 +43,193 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
}); });
}); });
module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application,
ApplicationCertificate, ApplicationCertificateGenerate,
ApplicationCertificateDownload, Notifications) {
$scope.realm = realm;
$scope.application = application;
var signingKeyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: 'saml.signing' },
function() {
$scope.signingKeyInfo = signingKeyInfo;
}
);
$scope.generateSigningKey = function() {
var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id, attribute: 'saml.signing' },
function() {
Notifications.success('Signing key has been regenerated.');
$scope.signingKeyInfo = keyInfo;
},
function() {
Notifications.error("Signing key was not regenerated.");
}
);
};
$scope.importSigningKey = function() {
$location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Signing/import/saml.signing");
};
$scope.exportSigningKey = function() {
$location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Signing/export/saml.signing");
};
var encryptionKeyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: 'saml.encryption' },
function() {
$scope.encryptionKeyInfo = encryptionKeyInfo;
}
);
$scope.generateEncryptionKey = function() {
var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id, attribute: 'saml.encryption' },
function() {
Notifications.success('Encryption key has been regenerated.');
$scope.encryptionKeyInfo = keyInfo;
},
function() {
Notifications.error("Encryption key was not regenerated.");
}
);
};
$scope.importEncryptionKey = function() {
$location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Encryption/import/saml.encryption");
};
$scope.exportEncryptionKey = function() {
$location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Encryption/export/saml.encryption");
};
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('ApplicationCertificateImportCtrl', function($scope, $location, $http, $upload, realm, application, $routeParams,
ApplicationCertificate, ApplicationCertificateGenerate,
ApplicationCertificateDownload, Notifications) {
var keyType = $routeParams.keyType;
var attribute = $routeParams.attribute;
$scope.realm = realm;
$scope.application = application;
$scope.keyType = keyType;
$scope.files = [];
$scope.onFileSelect = function($files) {
$scope.files = $files;
};
$scope.clearFileSelect = function() {
$scope.files = null;
}
$scope.keyFormats = [
"JKS",
"PKCS12"
];
$scope.uploadKeyFormat = $scope.keyFormats[0];
$scope.uploadFile = function() {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $scope.files.length; i++) {
var $file = $scope.files[i];
$scope.upload = $upload.upload({
url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/upload',
// method: POST or PUT,
// headers: {'headerKey': 'headerValue'}, withCredential: true,
data: {keystoreFormat: $scope.uploadKeyFormat,
keyAlias: $scope.uploadKeyAlias,
keyPassword: $scope.uploadKeyPassword,
storePassword: $scope.uploadStorePassword
},
file: $file
/* set file formData name for 'Content-Desposition' header. Default: 'file' */
//fileFormDataName: myFile,
/* customize how data is added to formData. See #40#issuecomment-28612000 for example */
//formDataAppender: function(formData, key, val){}
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers) {
Notifications.success("Keystore uploaded successfully.");
$location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/keys");
})
.error(function() {
Notifications.error("The key store can not be uploaded. Please verify the file.");
});
//.then(success, error, progress);
}
};
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('ApplicationCertificateExportCtrl', function($scope, $location, $http, $upload, realm, application, $routeParams,
ApplicationCertificate, ApplicationCertificateGenerate,
ApplicationCertificateDownload, Notifications) {
var keyType = $routeParams.keyType;
var attribute = $routeParams.attribute;
$scope.realm = realm;
$scope.application = application;
$scope.keyType = keyType;
var jks = {
keyAlias: application.name,
realmAlias: realm.realm
};
$scope.keyFormats = [
"JKS",
"PKCS12"
];
var keyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: attribute },
function() {
$scope.keyInfo = keyInfo;
}
);
$scope.jks = jks;
$scope.jks.format = $scope.keyFormats[0];
$scope.download = function() {
$http({
url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/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'
});
var ext = ".jks";
if ($scope.jks.format == 'PKCS12') ext = ".p12";
saveAs(blob, 'keystore' + ext);
}).error(function(){
Notifications.error("Error downloading.");
});
}
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, $upload, realm, application, module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, $upload, realm, application,
ApplicationCertificate, ApplicationCertificateGenerate, ApplicationCertificate, ApplicationCertificateGenerate,
ApplicationCertificateDownload, Notifications) { ApplicationCertificateDownload, Notifications) {

View file

@ -708,16 +708,18 @@ module.factory('ApplicationTestNodesAvailable', function($resource) {
}); });
module.factory('ApplicationCertificate', function($resource) { module.factory('ApplicationCertificate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates', { return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute', {
realm : '@realm', realm : '@realm',
application : "@application" application : "@application",
attribute: "@attribute"
}); });
}); });
module.factory('ApplicationCertificateGenerate', function($resource) { module.factory('ApplicationCertificateGenerate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/generate', { return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute/generate', {
realm : '@realm', realm : '@realm',
application : "@application" application : "@application",
attribute: "@attribute"
}, },
{ {
generate : { generate : {
@ -727,9 +729,10 @@ module.factory('ApplicationCertificateGenerate', function($resource) {
}); });
module.factory('ApplicationCertificateDownload', function($resource) { module.factory('ApplicationCertificateDownload', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/download', { return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute/download', {
realm : '@realm', realm : '@realm',
application : "@application" application : "@application",
attribute: "@attribute"
}, },
{ {
download : { download : {

View file

@ -0,0 +1,62 @@
<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><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/saml/keys">SAML Keys</a></li>
<li class="active">SAML {{keyType}} Key Export</li>
</ol>
<h2><span>{{application.name}}</span> SAML {{keyType}} Key Export <span tooltip-placement="right" tooltip="Export Appliations key and certificate to file." 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">
<div class="form-group">
<label class="col-sm-2 control-label" for="downloadKeyFormat">Archive Format</label>
<div class="col-sm-6">
<div class="select-kc">
<select id="downloadKeyFormat"
ng-model="jks.format"
ng-options="f for f in keyFormats">
</select>
</div>
</div>
<span tooltip-placement="right" tooltip="Java keystore or PKCS12 archive format." class="fa fa-info-circle"></span>
</div>
<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>
<span tooltip-placement="right" tooltip="Archive alias for your private key and certificate." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-hide="!keyInfo.privateKey">
<label class="col-sm-2 control-label" for="keyPassword">Key Password</label>
<div class="col-sm-4">
<input class="form-control" type="password" id="keyPassword" name="keyPassword" data-ng-model="jks.keyPassword" autofocus required>
</div>
<span tooltip-placement="right" tooltip="Password to access the private key in the archive" class="fa fa-info-circle"></span>
</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>
<span tooltip-placement="right" tooltip="Realm certificate is stored in archive too. This is the alias to it." class="fa fa-info-circle"></span>
</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="password" id="storePassword" name="storePassword" data-ng-model="jks.storePassword" autofocus required>
</div>
<span tooltip-placement="right" tooltip="Password to access the archive itself" class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="access.manageRealm">
<div class="pull-right">
<button class="btn btn-primary" type="submit" data-ng-click="download()">Download</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>

View file

@ -0,0 +1,59 @@
<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><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/saml/keys">SAML Keys</a></li>
<li class="active">SAML {{keyType}} Key Import</li>
</ol>
<h2><span>{{application.name}}</span> SAML {{keyType}} Key Import <span tooltip-placement="right" tooltip="Upload Key." class="fa fa-info-circle"></span></h2>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group">
<label class="col-sm-2 control-label" for="uploadKeyFormat">Archive Format</label>
<div class="col-sm-6">
<div class="select-kc">
<select id="uploadKeyFormat"
ng-model="uploadKeyFormat"
ng-options="f for f in keyFormats">
</select>
</div>
</div>
<span tooltip-placement="right" tooltip="Java keystore or PKCS12 archive format." class="fa fa-info-circle"></span>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="uploadKeyAlias">Key Alias</label>
<div class="col-sm-4">
<input class="form-control" type="text" id="uploadKeyAlias" name="uploadKeyAlias" data-ng-model="uploadKeyAlias" autofocus required>
</div>
<span tooltip-placement="right" tooltip="Archive alias for your certificate." class="fa fa-info-circle"></span>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="uploadStorePassword">Store Password</label>
<div class="col-sm-4">
<input class="form-control" type="password" id="uploadStorePassword" name="uploadStorePassword" data-ng-model="uploadStorePassword" autofocus required>
</div>
<span tooltip-placement="right" tooltip="Password to access the archive itself" class="fa fa-info-circle"></span>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Import File </label>
<div class="col-sm-4">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
<a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a>
<input id="import-file" type="file" class="transparent" ng-file-select="onFileSelect($files)">
</div>
<span class="kc-uploaded-file" data-ng-show="files.length > 0">
{{files[0].name}}
</span>
</div>
</div>
<div class="pull-right form-actions" data-ng-show="files.length > 0">
<button type="submit" data-ng-click="clearFileSelect()" class="btn btn-lg btn-default">Cancel</button>
<button type="submit" data-ng-click="uploadFile()" class="btn btn-lg btn-primary">Import</button>
</div>
</fieldset>
</form>
</div>
</div>

View file

@ -0,0 +1,66 @@
<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">SAML Keys</li>
</ol>
<h2><span>{{application.name}}</span> SAML Keys <span tooltip-placement="right" tooltip="Application certificates used to sign and encrypt documents." 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-show="application.attributes['saml.client.signature'] == 'true'">
<legend uncollapsed><span class="text">Signing Key</span> <span tooltip-placement="right" tooltip="SAML Signing Key." class="fa fa-info-circle"></span></legend>
<div class="form-group" data-ng-hide="!signingKeyInfo.privateKey">
<label class="col-sm-2 control-label" for="signingPrivateKey">Private key</label>
<div class="col-sm-10">
<textarea type="text" id="signingPrivateKey" name="signingPrivateKey" class="form-control" rows="5"
kc-select-action="click" readonly>{{signingKeyInfo.privateKey}}</textarea>
</div>
</div>
<div class="form-group" data-ng-hide="!signingKeyInfo.certificate">
<label class="col-sm-2 control-label" for="signingCert">Certificate</label>
<div class="col-sm-10">
<textarea type="text" id="signingCert" name="signingCert" class="form-control" rows="5"
kc-select-action="click" readonly>{{signingKeyInfo.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="generateSigningKey()">Generate new keys</button>
<button class="btn btn-primary" type="submit" data-ng-click="importSigningKey()">Import</button>
<button class="btn btn-primary" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">Export</button>
</div>
</div>
</fieldset>
<fieldset class="form-group col-sm-10" data-ng-show="application.attributes['saml.encrypt'] == 'true'">
<legend uncollapsed><span class="text">Encryption Key</span> <span tooltip-placement="right" tooltip="SAML Encryption Key." class="fa fa-info-circle"></span></legend>
<div class="form-group" data-ng-hide="!encryptionKeyInfo.privateKey">
<label class="col-sm-2 control-label" for="encryptionPrivateKey">Private key</label>
<div class="col-sm-10">
<textarea type="text" id="encryptionPrivateKey" name="encryptionPrivateKey" class="form-control" rows="5"
kc-select-action="click" readonly>{{encryptionKeyInfo.privateKey}}</textarea>
</div>
</div>
<div class="form-group" data-ng-hide="!encryptionKeyInfo.certificate">
<label class="col-sm-2 control-label" for="encryptionCert">Certificate</label>
<div class="col-sm-10">
<textarea type="text" id="encryptionCert" name="encryptionCert" class="form-control" rows="5"
kc-select-action="click" readonly>{{encryptionKeyInfo.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="generateEncryptionKey()">Generate new keys</button>
<button class="btn btn-primary" type="submit" data-ng-click="importEncryptionKey()">Import</button>
<button class="btn btn-primary" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">Export</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>

View file

@ -1,7 +1,7 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create"> <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]}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">Settings</a></li>
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li> <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!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['saml.client.signature'] == 'true'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/certificate">Application Keys</a></li> <li ng-class="{active: path[4] == 'saml'}" data-ng-show="application.protocol == 'saml' && (application.attributes['saml.client.signature'] == 'true' || application.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/saml/keys">SAML 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] == '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] == '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] == 'scope-mappings'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/scope-mappings">Scope</a></li>

View file

@ -89,8 +89,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (json == null) { if (json == null) {
return null; return null;
} }
log.info("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME); log.debug("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
log.info(json); log.debug(json);
return new ByteArrayInputStream(json.getBytes()); return new ByteArrayInputStream(json.getBytes());
} }
@ -99,7 +99,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (is == null) { if (is == null) {
String path = context.getServletContext().getInitParameter("keycloak.config.file"); String path = context.getServletContext().getInitParameter("keycloak.config.file");
if (path == null) { if (path == null) {
log.info("**** using /WEB-INF/keycloak.json"); log.debug("**** using /WEB-INF/keycloak.json");
is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"); is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
} else { } else {
try { try {
@ -161,7 +161,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) { if (deployment == null || !deployment.isConfigured()) {
log.info("*** deployment isn't configured return false"); log.debug("*** deployment isn't configured return false");
return false; return false;
} }

View file

@ -14,6 +14,12 @@
<description /> <description />
<dependencies> <dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>

View file

@ -5,6 +5,7 @@ import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase; import org.apache.catalina.valves.ValveBase;
import org.jboss.logging.Logger;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AuthenticatedActionsHandler; import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
@ -12,7 +13,6 @@ import org.keycloak.adapters.KeycloakDeployment;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger;
/** /**
* Pre-installed actions that must be authenticated * Pre-installed actions that must be authenticated
@ -22,16 +22,16 @@ import java.util.logging.Logger;
* CORS Origin Check and Response headers * CORS Origin Check and Response headers
* k_query_bearer_token: Get bearer token from server for Javascripts CORS requests * k_query_bearer_token: Get bearer token from server for Javascripts CORS requests
* *
* @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AuthenticatedActionsValve extends ValveBase { public class AuthenticatedActionsValve extends ValveBase {
private static final Logger log = Logger.getLogger(""+AuthenticatedActionsValve.class); private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class);
protected AdapterDeploymentContext deploymentContext; protected AdapterDeploymentContext deploymentContext;
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container, ObjectName controller) { public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
this.deploymentContext = deploymentContext; this.deploymentContext = deploymentContext;
if (next == null) throw new RuntimeException("WTF is next null?!"); if (next == null) throw new RuntimeException("Next valve is null!!!");
setNext(next); setNext(next);
setContainer(container); setContainer(container);
} }
@ -39,7 +39,7 @@ public class AuthenticatedActionsValve extends ValveBase {
@Override @Override
public void invoke(Request request, Response response) throws IOException, ServletException { public void invoke(Request request, Response response) throws IOException, ServletException {
log.finer("AuthenticatedActionsValve.invoke" + request.getRequestURI()); log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) { if (deployment != null && deployment.isConfigured()) {
@ -51,6 +51,4 @@ public class AuthenticatedActionsValve extends ValveBase {
} }
getNext().invoke(request, response); getNext().invoke(request, response);
} }
}
}

View file

@ -4,6 +4,7 @@ import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants; import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
@ -16,17 +17,15 @@ import java.io.IOException;
import java.security.Principal; import java.security.Principal;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
/** /**
* @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class CatalinaRequestAuthenticator extends RequestAuthenticator { public class CatalinaRequestAuthenticator extends RequestAuthenticator {
private static final Logger log = Logger.getLogger(""+CatalinaRequestAuthenticator.class); private static final Logger log = Logger.getLogger(CatalinaRequestAuthenticator.class);
protected KeycloakAuthenticatorValve valve; protected KeycloakAuthenticatorValve valve;
protected CatalinaUserSessionManagement userSessionManagement; protected CatalinaUserSessionManagement userSessionManagement;
protected Request request; protected Request request;
@ -59,14 +58,14 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
protected void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) { protected void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext(); RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext); request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
Set<String> roles = getRolesFromToken(securityContext); Set<String> roles = getRolesFromToken(securityContext);
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles, securityContext); GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles, securityContext);
Session session = request.getSessionInternal(true); Session session = request.getSessionInternal(true);
session.setPrincipal(principal); session.setPrincipal(principal);
session.setAuthType("OAUTH"); session.setAuthType("OAUTH");
session.setNote(KeycloakSecurityContext.class.getName(), securityContext); session.setNote(KeycloakSecurityContext.class.getName(), securityContext);
String username = securityContext.getToken().getSubject(); String username = securityContext.getToken().getSubject();
log.finer("userSessionManagement.login: " + username); log.debug("userSessionManage.login: " + username);
userSessionManagement.login(session); userSessionManagement.login(session);
} }
@ -74,8 +73,8 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) { protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext(); RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
Set<String> roles = getRolesFromToken(securityContext); Set<String> roles = getRolesFromToken(securityContext);
if (log.isLoggable(Level.FINE)) { if (log.isDebugEnabled()) {
log.fine("Completing bearer authentication. Bearer roles: " + roles); log.debug("Completing bearer authentication. Bearer roles: " + roles);
} }
Principal generalPrincipal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), principal, roles, securityContext); Principal generalPrincipal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), principal, roles, securityContext);
request.setUserPrincipal(generalPrincipal); request.setUserPrincipal(generalPrincipal);
@ -100,7 +99,7 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
protected boolean isCached() { protected boolean isCached() {
if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null) if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null)
return false; return false;
log.finer("remote logged in already"); log.debug("remote logged in already");
GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal(); GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
request.setUserPrincipal(principal); request.setUserPrincipal(principal);
request.setAuthType("KEYCLOAK"); request.setAuthType("KEYCLOAK");
@ -119,9 +118,9 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
protected void restoreRequest() { protected void restoreRequest() {
if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null) { if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null) {
if (valve.keycloakRestoreRequest(request)) { if (valve.keycloakRestoreRequest(request)) {
log.finer("restoreRequest"); log.debug("restoreRequest");
} else { } else {
log.finer("Restore of original request failed"); log.debug("Restore of original request failed");
throw new RuntimeException("Restore of original request failed"); throw new RuntimeException("Restore of original request failed");
} }
} }

View file

@ -5,20 +5,19 @@ import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent; import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener; import org.apache.catalina.SessionListener;
import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.logging.Logger;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
/** /**
* Manages relationship to users and sessions so that forced admin logout can be implemented * Manages relationship to users and sessions so that forced admin logout can be implemented
* *
* @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class CatalinaUserSessionManagement implements SessionListener { public class CatalinaUserSessionManagement implements SessionListener {
private static final Logger log = Logger.getLogger(CatalinaUserSessionManagement.class);
private static final Logger log = Logger.getLogger(""+CatalinaUserSessionManagement.class);
public void login(Session session) { public void login(Session session) {
session.addSessionListener(this); session.addSessionListener(this);
@ -32,7 +31,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
} }
public void logoutHttpSessions(Manager sessionManager, List<String> sessionIds) { public void logoutHttpSessions(Manager sessionManager, List<String> sessionIds) {
log.fine("logoutHttpSessions: " + sessionIds); log.debug("logoutHttpSessions: " + sessionIds);
for (String sessionId : sessionIds) { for (String sessionId : sessionIds) {
logoutSession(sessionManager, sessionId); logoutSession(sessionManager, sessionId);
@ -40,14 +39,13 @@ public class CatalinaUserSessionManagement implements SessionListener {
} }
protected void logoutSession(Manager manager, String httpSessionId) { protected void logoutSession(Manager manager, String httpSessionId) {
log.fine("logoutHttpSession: " + httpSessionId); log.debug("logoutHttpSession: " + httpSessionId);
Session session; Session session;
try { try {
session = manager.findSession(httpSessionId); session = manager.findSession(httpSessionId);
} catch (IOException ioe) { } catch (IOException ioe) {
log.warning("IO exception when looking for session " + httpSessionId); log.warn("IO exception when looking for session " + httpSessionId, ioe);
ioe.printStackTrace();
return; return;
} }
@ -58,7 +56,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
try { try {
session.expire(); session.expire();
} catch (Exception e) { } catch (Exception e) {
log.warning("Session not present or already invalidated."); log.warnf("Session not present or already invalidated.");
} }
} }
@ -70,7 +68,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
// Look up the single session id associated with this session (if any) // Look up the single session id associated with this session (if any)
Session session = event.getSession(); Session session = event.getSession();
log.fine("Session " + session.getId() + " destroyed"); log.debugf("Session %s destroyed", session.getId());
GenericPrincipal principal = (GenericPrincipal) session.getPrincipal(); GenericPrincipal principal = (GenericPrincipal) session.getPrincipal();
if (principal == null) return; if (principal == null) return;

View file

@ -2,17 +2,17 @@ package org.keycloak.adapters.tomcat7;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.logging.Logger;
/** /**
* @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class CorsPreflightChecker { public class CorsPreflightChecker {
private static final Logger log = Logger.getLogger(""+CorsPreflightChecker.class); private static final Logger log = Logger.getLogger(CorsPreflightChecker.class);
protected KeycloakDeployment deployment; protected KeycloakDeployment deployment;
public CorsPreflightChecker(KeycloakDeployment deployment) { public CorsPreflightChecker(KeycloakDeployment deployment) {
@ -20,17 +20,17 @@ public class CorsPreflightChecker {
} }
public boolean checkCorsPreflight(Request request, Response response) { public boolean checkCorsPreflight(Request request, Response response) {
log.finer("checkCorsPreflight " + request.getRequestURI()); log.debugv("checkCorsPreflight {0}", request.getRequestURI());
if (!request.getMethod().equalsIgnoreCase("OPTIONS")) { if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
log.finer("checkCorsPreflight: not options "); log.debug("checkCorsPreflight: not options ");
return false; return false;
} }
if (request.getHeader("Origin") == null) { if (request.getHeader("Origin") == null) {
log.finer("checkCorsPreflight: no origin header"); log.debug("checkCorsPreflight: no origin header");
return false; return false;
} }
log.finer("Preflight request returning"); log.debug("Preflight request returning");
response.setStatus(HttpServletResponse.SC_OK); response.setStatus(HttpServletResponse.SC_OK);
String origin = request.getHeader("Origin"); String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Origin", origin);

View file

@ -12,6 +12,7 @@ import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.LoginConfig;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterConstants; import org.keycloak.adapters.AdapterConstants;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
@ -23,7 +24,6 @@ import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.NodesRegistrationManagement; import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler; import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.ServerRequest;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -33,20 +33,19 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.logging.Logger;
/** /**
* Web deployment whose security is managed by a remote OAuth Skeleton Key authentication server * Web deployment whose security is managed by a remote OAuth Skeleton Key authentication server
* <p/> * <p/>
* Redirects browser to remote authentication server if not logged in. Also allows OAuth Bearer Token requests * Redirects browser to remote authentication server if not logged in. Also allows OAuth Bearer Token requests
* that contain a Skeleton Key bearer tokens. * that contain a Skeleton Key bearer tokens.
* *
* @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class KeycloakAuthenticatorValve extends FormAuthenticator implements LifecycleListener { public class KeycloakAuthenticatorValve extends FormAuthenticator implements LifecycleListener {
private final static Logger log = Logger.getLogger(""+KeycloakAuthenticatorValve.class); private static final Logger log = Logger.getLogger(KeycloakAuthenticatorValve.class);
protected CatalinaUserSessionManagement userSessionManagement = new CatalinaUserSessionManagement(); protected CatalinaUserSessionManagement userSessionManagement = new CatalinaUserSessionManagement();
protected AdapterDeploymentContext deploymentContext; protected AdapterDeploymentContext deploymentContext;
protected NodesRegistrationManagement nodesRegistrationManagement; protected NodesRegistrationManagement nodesRegistrationManagement;
@ -56,33 +55,15 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
try { try {
startDeployment(); startDeployment();
} catch (LifecycleException e) { } catch (LifecycleException e) {
log.severe("Error starting deployment. " + e.getMessage()); log.error("Error starting deployment. " + e.getMessage());
} }
} else if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) { } else if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) {
initInternal(); initInternal();
} else if (event.getType() == Lifecycle.BEFORE_STOP_EVENT) { } else if (event.getType() == Lifecycle.BEFORE_STOP_EVENT) {
beforeStop(); beforeStop();
} }
} }
@Override
public void logout(Request request) throws ServletException {
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) {
request.removeAttribute(KeycloakSecurityContext.class.getName());
Session session = request.getSessionInternal(false);
if (session != null) {
session.removeNote(KeycloakSecurityContext.class.getName());
try {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
ServerRequest.invokeLogout(deploymentContext.resolveDeployment(facade), ksc.getToken().getSessionState());
} catch (Exception e) {
log.severe("failed to invoke remote logout. " + e.getMessage());
}
}
}
super.logout(request);
}
public void startDeployment() throws LifecycleException { public void startDeployment() throws LifecycleException {
super.start(); super.start();
@ -91,28 +72,24 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
cache = false; cache = false;
} }
public void initInternal() { @Override
InputStream configInputStream = getConfigInputStream(context); public void logout(Request request) throws ServletException {
KeycloakDeployment kd = null; KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
if (configInputStream == null) { if (ksc != null) {
log.warning("No adapter configuration. Keycloak is unconfigured and will deny all requests."); request.removeAttribute(KeycloakSecurityContext.class.getName());
kd = new KeycloakDeployment(); Session session = request.getSessionInternal(false);
} else { if (session != null) {
kd = KeycloakDeploymentBuilder.build(configInputStream); session.removeNote(KeycloakSecurityContext.class.getName());
if (ksc instanceof RefreshableKeycloakSecurityContext) {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
((RefreshableKeycloakSecurityContext)ksc).logout(deploymentContext.resolveDeployment(facade));
}
}
} }
deploymentContext = new AdapterDeploymentContext(kd); super.logout(request);
context.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer(), getObjectName());
setNext(actions);
nodesRegistrationManagement = new NodesRegistrationManagement();
} }
protected void beforeStop() { private static InputStream getJSONFromServletContext(ServletContext servletContext) {
nodesRegistrationManagement.stop();
}
private static InputStream getJSONFromServletContext(ServletContext servletContext) {
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME); String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
if (json == null) { if (json == null) {
return null; return null;
@ -127,13 +104,12 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (is == null) { if (is == null) {
String path = context.getServletContext().getInitParameter("keycloak.config.file"); String path = context.getServletContext().getInitParameter("keycloak.config.file");
if (path == null) { if (path == null) {
log.info("**** using /WEB-INF/keycloak.json"); log.debug("**** using /WEB-INF/keycloak.json");
is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"); is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
} else { } else {
try { try {
is = new FileInputStream(path); is = new FileInputStream(path);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
log.severe("NOT FOUND /WEB-INF/keycloak.json");
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@ -141,9 +117,34 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
return is; return is;
} }
public void initInternal() {
InputStream configInputStream = getConfigInputStream(context);
KeycloakDeployment kd = null;
if (configInputStream == null) {
log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
kd = new KeycloakDeployment();
} else {
kd = KeycloakDeploymentBuilder.build(configInputStream);
}
deploymentContext = new AdapterDeploymentContext(kd);
context.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer());
setNext(actions);
nodesRegistrationManagement = new NodesRegistrationManagement();
}
protected void beforeStop() {
nodesRegistrationManagement.stop();
}
@Override @Override
public void invoke(Request request, Response response) throws IOException, ServletException { public void invoke(Request request, Response response) throws IOException, ServletException {
try { try {
if (log.isTraceEnabled()) {
log.trace("invoke");
}
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
Manager sessionManager = request.getContext().getManager(); Manager sessionManager = request.getContext().getManager();
CatalinaUserSessionManagementWrapper sessionManagementWrapper = new CatalinaUserSessionManagementWrapper(userSessionManagement, sessionManager); CatalinaUserSessionManagementWrapper sessionManagementWrapper = new CatalinaUserSessionManagementWrapper(userSessionManagement, sessionManager);
@ -159,9 +160,13 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
@Override @Override
public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException { public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
if (log.isTraceEnabled()) {
log.trace("*** authenticate");
}
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) { if (deployment == null || !deployment.isConfigured()) {
log.info("*** deployment isn't configured return false");
return false; return false;
} }
@ -188,7 +193,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
* @param request * @param request
*/ */
protected void checkKeycloakSession(Request request, HttpFacade facade) { protected void checkKeycloakSession(Request request, HttpFacade facade) {
if (request.getSessionInternal(false) == null || request.getPrincipal() == null) return; if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null) return;
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName()); RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
if (session == null) return; if (session == null) return;
// just in case session got serialized // just in case session got serialized
@ -202,7 +207,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session // Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
Session catalinaSession = request.getSessionInternal(); Session catalinaSession = request.getSessionInternal();
log.fine("Cleanup and expire session " + catalinaSession + " after failed refresh"); log.debugf("Cleanup and expire session %s after failed refresh", catalinaSession.getId());
catalinaSession.removeNote(KeycloakSecurityContext.class.getName()); catalinaSession.removeNote(KeycloakSecurityContext.class.getName());
request.setUserPrincipal(null); request.setUserPrincipal(null);
request.setAuthType(null); request.setAuthType(null);

View file

@ -132,6 +132,7 @@ public final class KeycloakModelUtils {
} }
public static void generateClientKeyPairCertificate(ClientModel client) { public static void generateClientKeyPairCertificate(ClientModel client) {
String subject = client.getClientId();
KeyPair keyPair = null; KeyPair keyPair = null;
try { try {
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
@ -140,7 +141,7 @@ public final class KeycloakModelUtils {
} }
X509Certificate certificate = null; X509Certificate certificate = null;
try { try {
certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, client.getClientId()); certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View file

@ -155,7 +155,7 @@ public class SamlProtocol implements LoginProtocol {
if (requiresEncryption(client)) { if (requiresEncryption(client)) {
PublicKey publicKey = null; PublicKey publicKey = null;
try { try {
publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY)); publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
} catch (Exception e) { } catch (Exception e) {
logger.error("failed", e); logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response"); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");

View file

@ -2,6 +2,7 @@ package org.keycloak.protocol.saml;
import org.keycloak.VerificationException; import org.keycloak.VerificationException;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
import org.keycloak.util.PemUtils; import org.keycloak.util.PemUtils;
import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.common.exceptions.ProcessingException;
import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature; import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
@ -10,6 +11,7 @@ import org.w3c.dom.Node;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
/** /**
@ -18,12 +20,15 @@ import java.security.cert.X509Certificate;
*/ */
public class SamlProtocolUtils { public class SamlProtocolUtils {
public static final String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
public static final String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException { public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException {
if (!"true".equals(client.getAttribute("saml.client.signature"))) { if (!"true".equals(client.getAttribute("saml.client.signature"))) {
return; return;
} }
SAML2Signature saml2Signature = new SAML2Signature(); SAML2Signature saml2Signature = new SAML2Signature();
PublicKey publicKey = getPublicKey(client); PublicKey publicKey = getSignatureValidationKey(client);
try { try {
if (!saml2Signature.validate(document, publicKey)) { if (!saml2Signature.validate(document, publicKey)) {
throw new VerificationException("Invalid signature on document"); throw new VerificationException("Invalid signature on document");
@ -33,16 +38,24 @@ public class SamlProtocolUtils {
} }
} }
public static PublicKey getPublicKey(ClientModel client) throws VerificationException { public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException {
String publicKeyPem = client.getAttribute(ClientModel.PUBLIC_KEY); return getPublicKey(client, SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
if (publicKeyPem == null) throw new VerificationException("Client does not have a public key."); }
PublicKey publicKey = null;
public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
return getPublicKey(client, SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
}
public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException {
String certPem = client.getAttribute(attribute);
if (certPem == null) throw new VerificationException("Client does not have a public key.");
Certificate cert = null;
try { try {
publicKey = PemUtils.decodePublicKey(publicKeyPem); cert = PemUtils.decodeCertificate(certPem);
} catch (Exception e) { } catch (Exception e) {
throw new VerificationException("Could not decode public key", e); throw new VerificationException("Could not decode cert", e);
} }
return publicKey; return cert.getPublicKey();
} }

View file

@ -309,7 +309,7 @@ public class SamlService {
// todo maybe a flag? // todo maybe a flag?
// SamlProtocolUtils.verifyDocumentSignature(client, documentHolder.getSamlDocument()); // SamlProtocolUtils.verifyDocumentSignature(client, documentHolder.getSamlDocument());
PublicKey publicKey = SamlProtocolUtils.getPublicKey(client); PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
UriBuilder builder = UriBuilder.fromPath("/") UriBuilder builder = UriBuilder.fromPath("/")

View file

@ -120,9 +120,9 @@ public class ApplicationResource {
return ModelToRepresentation.toRepresentation(application); return ModelToRepresentation.toRepresentation(application);
} }
@Path("certificates") @Path("certificates/{attr}")
public ClientCertificateResource getCertficateResource() { public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
return new ClientCertificateResource(realm, auth, application, session); return new ClientAttributeCertificateResource(realm, auth, application, session, attributePrefix);
} }

View file

@ -0,0 +1,307 @@
package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
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.CertificateUtils;
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.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientAttributeCertificateResource {
public static final String PRIVATE_KEY = "private.key";
public static final String X509CERTIFICATE = "certificate";
protected RealmModel realm;
private RealmAuth auth;
protected ClientModel client;
protected KeycloakSession session;
protected String attributePrefix;
protected String privateAttribute;
protected String certificateAttribute;
public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix) {
this.realm = realm;
this.auth = auth;
this.client = client;
this.session = session;
this.attributePrefix = attributePrefix;
this.privateAttribute = attributePrefix + "." + PRIVATE_KEY;
this.certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
}
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 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(certificateAttribute));
info.setPrivateKey(client.getAttribute(privateAttribute));
return info;
}
@POST
@NoCache
@Path("generate")
@Produces(MediaType.APPLICATION_JSON)
public ClientKeyPairInfo generate() {
auth.requireManage();
String subject = client.getClientId();
KeyPair keyPair = null;
try {
keyPair = KeyPairGenerator.getInstance("RSA").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);
client.setAttribute(privateAttribute, privateKeyPem);
client.setAttribute(certificateAttribute, certPem);
KeycloakModelUtils.generateClientKeyPairCertificate(client);
ClientKeyPairInfo info = new ClientKeyPairInfo();
info.setCertificate(client.getAttribute(certificateAttribute));
info.setPrivateKey(client.getAttribute(privateAttribute));
return info;
}
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public ClientKeyPairInfo uploadJks(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
auth.requireManage();
ClientKeyPairInfo info = new ClientKeyPairInfo();
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
List<InputPart> inputParts = uploadForm.get("file");
String keystoreFormat = uploadForm.get("keystoreFormat").get(0).getBodyAsString();
String keyAlias = uploadForm.get("keyAlias").get(0).getBodyAsString();
List<InputPart> keyPasswordPart = uploadForm.get("keyPassword");
char[] keyPassword = keyPasswordPart != null ? keyPasswordPart.get(0).getBodyAsString().toCharArray() : null;
List<InputPart> storePasswordPart = uploadForm.get("storePassword");
char[] storePassword = storePasswordPart != null ? storePasswordPart.get(0).getBodyAsString().toCharArray() : null;
PrivateKey privateKey = null;
X509Certificate certificate = null;
try {
KeyStore keyStore = null;
if (keystoreFormat.equals("JKS")) keyStore = KeyStore.getInstance("JKS");
else keyStore = KeyStore.getInstance(keystoreFormat, "BC");
keyStore.load(inputParts.get(0).getBody(InputStream.class, null), storePassword);
try {
privateKey = (PrivateKey)keyStore.getKey(keyAlias, keyPassword);
} catch (Exception e) {
// ignore
}
certificate = (X509Certificate)keyStore.getCertificate(keyAlias);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (privateKey != null) {
String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
client.setAttribute(privateAttribute, privateKeyPem);
info.setPrivateKey(privateKeyPem);
} else if (certificate != null) {
client.removeAttribute(privateAttribute);
}
if (certificate != null) {
String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
client.setAttribute(certificateAttribute, certPem);
info.setCertificate(certPem);
}
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[] getKeystore(final KeyStoreConfig config) {
auth.requireView();
if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
throw new NotAcceptableException("Only support jks format.");
}
String format = config.getFormat();
String privatePem = client.getAttribute(privateAttribute);
String certPem = client.getAttribute(certificateAttribute);
if (privatePem == null && certPem == null) {
throw new NotFoundException("keypair not generated for client");
}
if (privatePem != null && 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 {
if (format.equals("JKS")) keyStore = KeyStore.getInstance("JKS");
else keyStore = KeyStore.getInstance(format, "BC");
keyStore.load(null, null);
String keyAlias = config.getKeyAlias();
if (keyAlias == null) keyAlias = client.getClientId();
if (privatePem != null) {
PrivateKey privateKey = PemUtils.decodePrivateKey(privatePem);
X509Certificate clientCert = PemUtils.decodeCertificate(certPem);
Certificate[] chain = {clientCert};
keyStore.setKeyEntry(keyAlias, privateKey, config.getKeyPassword().trim().toCharArray(), chain);
} else {
X509Certificate clientCert = PemUtils.decodeCertificate(certPem);
keyStore.setCertificateEntry(keyAlias, clientCert);
}
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);
}
}
}

View file

@ -222,6 +222,18 @@
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId> <artifactId>maven-deploy-plugin</artifactId>

View file

@ -57,9 +57,8 @@
"saml.signature.algorithm": "RSA_SHA256", "saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true", "saml.client.signature": "true",
"saml.authnstatement": "true", "saml.authnstatement": "true",
"privateKey": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==", "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB", "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
"X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
} }
}, },
{ {
@ -76,9 +75,8 @@
"saml.server.signature": "true", "saml.server.signature": "true",
"saml.client.signature": "true", "saml.client.signature": "true",
"saml.authnstatement": "true", "saml.authnstatement": "true",
"privateKey": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==", "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB", "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
"X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
} }
}, },
{ {
@ -95,9 +93,8 @@
"saml.server.signature": "true", "saml.server.signature": "true",
"saml.client.signature": "true", "saml.client.signature": "true",
"saml.authnstatement": "true", "saml.authnstatement": "true",
"privateKey": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t", "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB", "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
"X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
} }
}, },
{ {
@ -116,9 +113,10 @@
"saml.client.signature": "true", "saml.client.signature": "true",
"saml.encrypt": "true", "saml.encrypt": "true",
"saml.authnstatement": "true", "saml.authnstatement": "true",
"privateKey": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t", "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB", "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
"X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==" "saml.encryption.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
} }
}, },
{ {
@ -136,9 +134,8 @@
"saml.client.signature": "true", "saml.client.signature": "true",
"saml.signature.algorithm": "RSA_SHA1", "saml.signature.algorithm": "RSA_SHA1",
"saml.authnstatement": "true", "saml.authnstatement": "true",
"privateKey": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5", "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQAB", "saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
"X509Certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
} }
} }
], ],

View file

@ -26,6 +26,7 @@
</build> </build>
<modules> <modules>
<module>integration</module> <module>integration</module>
<module>tomcat7</module>
<module>performance</module> <module>performance</module>
<module>tools</module> <module>tools</module>
<module>performance-web</module> <module>performance-web</module>

513
testsuite/tomcat7/pom.xml Executable file
View file

@ -0,0 +1,513 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-testsuite-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>1.1.0-Alpha1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-testsuite-tomcat7</artifactId>
<name>Keycloak Tomcat 7Integration TestSuite</name>
<description />
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-dependencies-server-all</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy.version.latest}</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-crypto</artifactId>
<version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-undertow</artifactId>
<version>${resteasy.version.latest}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${keycloak.apache.httpcomponents.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-ldap-federation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>federation-properties-example</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
</dependency>
<dependency>
<groupId>org.picketbox</groupId>
<artifactId>picketbox-ldap</artifactId>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.picketbox</groupId>
<artifactId>picketbox-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-wildlfy-common</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-undertow</artifactId>
<version>${wildfly.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-testsuite-integration</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-testsuite-integration</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>7.0.54</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-util</artifactId>
<version>7.0.54</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>7.0.54</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<workingDirectory>${project.basedir}</workingDirectory>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>keycloak-server</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>org.keycloak.testutils.KeycloakServer</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mail-server</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>org.keycloak.testutils.MailServer</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>totp</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>org.keycloak.testutils.TotpGenerator</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>jpa</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<keycloak.realm.provider>jpa</keycloak.realm.provider>
<keycloak.user.provider>jpa</keycloak.user.provider>
<keycloak.eventStore.provider>jpa</keycloak.eventStore.provider>
<keycloak.userSessions.provider>jpa</keycloak.userSessions.provider>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mongo</id>
<properties>
<keycloak.connectionsMongo.host>localhost</keycloak.connectionsMongo.host>
<keycloak.connectionsMongo.port>27018</keycloak.connectionsMongo.port>
<keycloak.connectionsMongo.db>keycloak</keycloak.connectionsMongo.db>
<keycloak.connectionsMongo.clearOnStartup>true</keycloak.connectionsMongo.clearOnStartup>
<keycloak.connectionsMongo.bindIp>127.0.0.1</keycloak.connectionsMongo.bindIp>
</properties>
<build>
<plugins>
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.realm.provider>mongo</keycloak.realm.provider>
<keycloak.user.provider>mongo</keycloak.user.provider>
<keycloak.audit.provider>mongo</keycloak.audit.provider>
<keycloak.userSessions.provider>mongo</keycloak.userSessions.provider>
<keycloak.connectionsMongo.host>${keycloak.connectionsMongo.host}</keycloak.connectionsMongo.host>
<keycloak.connectionsMongo.port>${keycloak.connectionsMongo.port}</keycloak.connectionsMongo.port>
<keycloak.connectionsMongo.db>${keycloak.connectionsMongo.db}</keycloak.connectionsMongo.db>
<keycloak.connectionsMongo.clearOnStartup>${keycloak.connectionsMongo.clearOnStartup}</keycloak.connectionsMongo.clearOnStartup>
<keycloak.connectionsMongo.bindIp>${keycloak.connectionsMongo.bindIp}</keycloak.connectionsMongo.bindIp>
</systemPropertyVariables>
</configuration>
</execution>
<execution>
<id>default-test</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
<!-- Embedded mongo -->
<plugin>
<groupId>com.github.joelittlejohn.embedmongo</groupId>
<artifactId>embedmongo-maven-plugin</artifactId>
<executions>
<execution>
<id>start-mongodb</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.connectionsMongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
<bindIp>${keycloak.connectionsMongo.bindIp}</bindIp>
</configuration>
</execution>
<execution>
<id>stop-mongodb</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>infinispan</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<keycloak.realm.cache.provider>infinispan</keycloak.realm.cache.provider>
<keycloak.user.cache.provider>infinispan</keycloak.user.cache.provider>
<keycloak.userSessions.provider>infinispan</keycloak.userSessions.provider>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- MySQL -->
<profile>
<activation>
<property>
<name>keycloak.connectionsJpa.driver</name>
<value>com.mysql.jdbc.Driver</value>
</property>
</activation>
<id>mysql</id>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</profile>
<!-- PostgreSQL -->
<profile>
<activation>
<property>
<name>keycloak.connectionsJpa.driver</name>
<value>org.postgresql.Driver</value>
</property>
</activation>
<id>postgresql</id>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>clean-jpa</id>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
<url>${keycloak.connectionsJpa.url}</url>
<driver>${keycloak.connectionsJpa.driver}</driver>
<username>${keycloak.connectionsJpa.user}</username>
<password>${keycloak.connectionsJpa.password}</password>
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
</configuration>
<executions>
<execution>
<id>clean-jpa</id>
<phase>clean</phase>
<goals>
<goal>dropAll</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View file

@ -0,0 +1,283 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.HttpClientBuilder;
import org.keycloak.protocol.oidc.OpenIDConnectService;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.testsuite.Constants;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.util.BasicAuthHelper;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Tomcat7Test {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule();
public static class BrowserLogin implements Runnable
{
private WebDriver driver;
public BrowserLogin() {
driver = WebRule.createWebDriver();
}
@Override
public void run() {
driver.manage().deleteAllCookies();
OAuthClient oauth = new OAuthClient(driver);
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
count.incrementAndGet();
}
}
public static AtomicLong count = new AtomicLong(0);
public static class JaxrsClientLogin implements Runnable
{
ResteasyClient client;
private String baseUrl = Constants.AUTH_SERVER_ROOT;
private String realm = "test";
private String responseType = OAuth2Constants.CODE;
private String grantType = "authorization_code";
private String clientId = "test-app";
private String redirectUri = "http://localhost:8081/app/auth";
public JaxrsClientLogin() {
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
httpClient.setCookieStore(new CookieStore() {
@Override
public void addCookie(Cookie cookie) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public List<Cookie> getCookies() {
return Collections.emptyList();
}
@Override
public boolean clearExpired(Date date) {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void clear() {
//To change body of implemented methods use File | Settings | File Templates.
}
});
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
this.client = new ResteasyClientBuilder().httpEngine(engine).build();
}
public String getLoginFormUrl(String state) {
UriBuilder b = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri(baseUrl));
if (responseType != null) {
b.queryParam(OAuth2Constants.RESPONSE_TYPE, responseType);
}
if (clientId != null) {
b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
}
if (redirectUri != null) {
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
}
if (state != null) {
b.queryParam(OAuth2Constants.STATE, state);
}
return b.build(realm).toString();
}
public String getProcessLoginUrl(String state) {
UriBuilder b = LoginActionsService.processLoginUrl(UriBuilder.fromUri(baseUrl));
if (clientId != null) {
b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
}
if (redirectUri != null) {
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
}
if (state != null) {
b.queryParam(OAuth2Constants.STATE, state);
}
return b.build(realm).toString();
}
static Pattern actionParser = Pattern.compile("action=\"([^\"]+)\"");
public void run() {
//this.client = new ResteasyClientBuilder().build();
String state = "42";
String loginFormUrl = getLoginFormUrl(state);
String html = client.target(loginFormUrl).request().get(String.class);
Matcher matcher = actionParser.matcher(html);
matcher.find();
String actionUrl = matcher.group(1);
if (!actionUrl.startsWith("http")) {
actionUrl = UriBuilder.fromUri(actionUrl).scheme("http").host("localhost").port(8081).build().toString();
}
Form form = new Form();
form.param("username", "test-user@localhost");
form.param("password", "password");
Response response = client.target(actionUrl).request().post(Entity.form(form));
URI uri = null;
Assert.assertEquals(302, response.getStatus());
uri = response.getLocation();
for (String header : response.getHeaders().keySet()) {
for (Object value : response.getHeaders().get(header)) {
System.out.println(header + ": " + value);
}
}
response.close();
Assert.assertNotNull(uri);
String code = getCode(uri);
Assert.assertNotNull(code);
form = new Form();
form.param(OAuth2Constants.GRANT_TYPE, grantType)
.param(OAuth2Constants.CODE, code)
.param(OAuth2Constants.REDIRECT_URI, redirectUri);
String authorization = BasicAuthHelper.createHeader(clientId, "password");
String res = client.target(OpenIDConnectService.accessCodeToTokenUrl(UriBuilder.fromUri(baseUrl)).build(realm)).request()
.header(HttpHeaders.AUTHORIZATION, authorization)
.post(Entity.form(form), String.class);
count.incrementAndGet();
//client.close();
}
public String getCode(URI uri) {
Map<String, String> m = new HashMap<String, String>();
List<NameValuePair> pairs = URLEncodedUtils.parse(uri, "UTF-8");
for (NameValuePair p : pairs) {
if (p.getName().equals("code")) return p.getValue();
m.put(p.getName(), p.getValue());
}
return null;
}
public void close()
{
client.close();
}
}
@Test
public void perfJaxrsClientLogin()
{
long ITERATIONS = 3;
JaxrsClientLogin login = new JaxrsClientLogin();
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
//System.out.println("*************************");
login.run();
}
long end = System.currentTimeMillis() - start;
System.out.println("took: " + end);
}
@Test
public void perfBrowserLogin()
{
long ITERATIONS = 3;
long start = System.currentTimeMillis();
BrowserLogin login = new BrowserLogin();
for (int i = 0; i < ITERATIONS; i++) {
//System.out.println("----------------------------------");
login.run();
}
long end = System.currentTimeMillis() - start;
System.out.println("took: " + end);
}
@Test
public void multiThread() throws Exception {
int num_threads = 20;
Thread[] threads = new Thread[num_threads];
for (int i = 0; i < num_threads; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
perfJaxrsClientLogin();
}
});
}
long start = System.currentTimeMillis();
for (int i = 0; i < num_threads; i++) {
threads[i].start();
}
for (int i = 0; i < num_threads; i++) {
threads[i].join();
}
long end = System.currentTimeMillis() - start;
System.out.println(count.toString() + " took: " + end);
System.out.println(count.floatValue() / ((float)end) * 1000+ " logins/s");
}
}

View file

@ -0,0 +1,24 @@
package org.keycloak.testsuite.tomcat7;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import java.io.File;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class TomcatAdapterTest {
public void startServer() throws LifecycleException {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
//tomcat.addWebapp()
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootCtx = tomcat.addContext("/app", base.getAbsolutePath());
//Tomcat.addServlet(rootCtx, "dateServlet", new DatePrintServlet());
rootCtx.addServletMapping("/date", "dateServlet");
tomcat.start();
tomcat.getServer().await();
}}