diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index b46b13f5c3..6ebea5fa8a 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -476,8 +476,8 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ApplicationClusteringNodeCtrl'
})
- .when('/realms/:realm/applications/:application/certificate', {
- templateUrl : 'partials/application-keys.html',
+ .when('/realms/:realm/applications/:application/saml/keys', {
+ templateUrl : 'partials/application-saml-keys.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
@@ -486,7 +486,31 @@ module.config([ '$routeProvider', function($routeProvider) {
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', {
templateUrl : 'partials/application-role-list.html',
@@ -553,6 +577,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
application : function() {
return {};
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
}
},
controller : 'ApplicationDetailCtrl'
@@ -568,6 +595,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
application : function(ApplicationLoader) {
return ApplicationLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
}
},
controller : 'ApplicationDetailCtrl'
@@ -580,10 +610,26 @@ module.config([ '$routeProvider', function($routeProvider) {
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
}
+
},
controller : 'ApplicationListCtrl'
})
+ .when('/import/application/:realm', {
+ templateUrl : 'partials/application-import.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'ApplicationImportCtrl'
+ })
// OAUTH Client
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index e1efcba147..3d80c2b2bc 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -43,16 +43,82 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
});
});
-module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, $upload, realm, application,
+module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application,
ApplicationCertificate, ApplicationCertificateGenerate,
ApplicationCertificateDownload, Notifications) {
$scope.realm = realm;
$scope.application = application;
- var jks = {
- keyAlias: application.name,
- realmAlias: realm.realm
+
+ 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) {
@@ -68,8 +134,6 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
"PKCS12"
];
- $scope.jks = jks;
- $scope.jks.format = $scope.keyFormats[0];
$scope.uploadKeyFormat = $scope.keyFormats[0];
$scope.uploadFile = function() {
@@ -77,13 +141,13 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
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/upload/jks',
+ 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
+ keyAlias: $scope.uploadKeyAlias,
+ keyPassword: $scope.uploadKeyPassword,
+ storePassword: $scope.uploadStorePassword
},
file: $file
/* set file formData name for 'Content-Desposition' header. Default: 'file' */
@@ -93,8 +157,8 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers) {
- $scope.keyInfo = data;
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.");
@@ -104,30 +168,42 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
}
};
+ $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 },
+ 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.generate = function() {
- var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id },
- function() {
- Notifications.success('Client keypair and cert has been changed.');
- $scope.keyInfo = keyInfo;
- },
- function() {
- Notifications.error("Client keypair and cert was not changed due to a problem.");
- }
- );
- };
-
- $scope.downloadJKS = function() {
+ $scope.download = function() {
$http({
- url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/download',
+ url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/download',
method: 'POST',
responseType: 'arraybuffer',
data: $scope.jks,
@@ -290,9 +366,62 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati
});
-module.controller('ApplicationListCtrl', function($scope, realm, applications, Application, $location) {
+module.controller('ApplicationImportCtrl', function($scope, $location, $upload, realm, serverInfo, Notifications) {
+
+ $scope.realm = realm;
+ $scope.configFormats = serverInfo.applicationImporters;
+ $scope.configFormat = null;
+
+ $scope.files = [];
+
+ $scope.onFileSelect = function($files) {
+ $scope.files = $files;
+ };
+
+ $scope.clearFileSelect = function() {
+ $scope.files = null;
+ }
+
+ $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 + '/application-importers/' + $scope.configFormat.id + '/upload',
+ // method: POST or PUT,
+ // headers: {'headerKey': 'headerValue'}, withCredential: true,
+ data: {myObj: ""},
+ 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("Uploaded successfully.");
+ $location.url("/realms/" + realm.realm + "/applications");
+ })
+ .error(function() {
+ Notifications.error("The file 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('ApplicationListCtrl', function($scope, realm, applications, Application, serverInfo, $location) {
$scope.realm = realm;
$scope.applications = applications;
+ $scope.importButton = serverInfo.applicationImporters.length > 0;
$scope.$watch(function() {
return $location.path();
}, function() {
@@ -335,7 +464,7 @@ module.controller('ApplicationInstallationCtrl', function($scope, realm, applica
}
});
-module.controller('ApplicationDetailCtrl', function($scope, realm, application, Application, $location, Dialog, Notifications) {
+module.controller('ApplicationDetailCtrl', function($scope, realm, application, serverInfo, Application, $location, Dialog, Notifications) {
console.log('ApplicationDetailCtrl');
$scope.accessTypes = [
@@ -344,10 +473,8 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
"bearer-only"
];
- $scope.protocols = [
- "openid-connect",
- "saml"
- ];
+ $scope.protocols = serverInfo.protocols;
+
$scope.signatureAlgorithms = [
"RSA_SHA1",
"RSA_SHA256",
@@ -375,11 +502,9 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
} else if (application.publicClient) {
$scope.accessType = $scope.accessTypes[1];
}
- if (application.protocol == 'openid-connect') {
- $scope.protocol = $scope.protocols[0];
- } else if (application.protocol == 'saml') {
- $scope.protocol = $scope.protocols[1];
- } else { // protocol could be null due to older keycloak installs
+ if (application.protocol) {
+ $scope.protocol = $scope.protocols[$scope.protocols.indexOf(application.protocol)];
+ } else {
$scope.protocol = $scope.protocols[0];
}
if (application.attributes['saml.signature.algorithm'] == 'RSA_SHA1') {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index ee4d635ccf..d47e3abbce 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -708,16 +708,18 @@ module.factory('ApplicationTestNodesAvailable', 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',
- application : "@application"
+ application : "@application",
+ attribute: "@attribute"
});
});
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',
- application : "@application"
+ application : "@application",
+ attribute: "@attribute"
},
{
generate : {
@@ -727,9 +729,10 @@ module.factory('ApplicationCertificateGenerate', 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',
- application : "@application"
+ application : "@application",
+ attribute: "@attribute"
},
{
download : {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-import.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-import.html
new file mode 100755
index 0000000000..723bf011b3
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-import.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
{{application.name}} Application Import
+
+
+
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html
index f6eec65ed4..364b4c7c53 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html
@@ -19,7 +19,8 @@
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html
new file mode 100755
index 0000000000..692080a70f
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ Applications
+ {{application.name}}
+ SAML Keys
+ SAML {{keyType}} Key Export
+
+
{{application.name}} SAML {{keyType}} Key Export
+
+
+
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html
new file mode 100755
index 0000000000..e72583ea8b
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+ Applications
+ {{application.name}}
+ SAML Keys
+ SAML {{keyType}} Key Import
+
+
{{application.name}} SAML {{keyType}} Key Import
+
+
+
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html
new file mode 100755
index 0000000000..f871bb9c4a
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+ Applications
+ {{application.name}}
+ SAML Keys
+
+
{{application.name}} SAML Keys
+
+
+
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
index 8e3a96305a..02343270d4 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
@@ -1,7 +1,7 @@
Settings
Credentials
- Application Keys
+ SAML Keys
Roles
Claims
Scope
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index 93836a467d..9d25e92267 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -93,8 +93,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (json == null) {
return null;
}
- log.info("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
- log.info(json);
+ log.debug("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
+ log.debug(json);
return new ByteArrayInputStream(json.getBytes());
}
@@ -103,7 +103,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (is == null) {
String path = context.getServletContext().getInitParameter("keycloak.config.file");
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");
} else {
try {
@@ -165,7 +165,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
- log.info("*** deployment isn't configured return false");
+ log.debug("*** deployment isn't configured return false");
return false;
}
AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment);
diff --git a/integration/tomcat7/adapter/pom.xml b/integration/tomcat7/adapter/pom.xml
index f60daa3ba4..5f27a92420 100755
--- a/integration/tomcat7/adapter/pom.xml
+++ b/integration/tomcat7/adapter/pom.xml
@@ -14,6 +14,12 @@
+
+ org.jboss.logging
+ jboss-logging
+ ${jboss.logging.version}
+ provided
+
org.keycloak
keycloak-core
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java
index c7ea65eab2..aae9b29351 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java
@@ -5,6 +5,7 @@ import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.KeycloakDeployment;
@@ -12,7 +13,6 @@ import org.keycloak.adapters.KeycloakDeployment;
import javax.management.ObjectName;
import javax.servlet.ServletException;
import java.io.IOException;
-import java.util.logging.Logger;
/**
* Pre-installed actions that must be authenticated
@@ -22,16 +22,16 @@ import java.util.logging.Logger;
* CORS Origin Check and Response headers
* k_query_bearer_token: Get bearer token from server for Javascripts CORS requests
*
- * @author Davide Ungari
+ * @author Bill Burke
* @version $Revision: 1 $
*/
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;
- public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container, ObjectName controller) {
+ public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
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);
setContainer(container);
}
@@ -39,7 +39,7 @@ public class AuthenticatedActionsValve extends ValveBase {
@Override
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);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) {
@@ -51,6 +51,4 @@ public class AuthenticatedActionsValve extends ValveBase {
}
getNext().invoke(request, response);
}
-
-
-}
\ No newline at end of file
+}
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java
index 66258ddf05..8263e94cca 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java
@@ -5,20 +5,19 @@ import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.realm.GenericPrincipal;
+import org.jboss.logging.Logger;
import java.io.IOException;
import java.util.List;
-import java.util.logging.Logger;
/**
* Manages relationship to users and sessions so that forced admin logout can be implemented
*
- * @author Davide Ungari
+ * @author Bill Burke
* @version $Revision: 1 $
*/
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) {
session.addSessionListener(this);
@@ -32,7 +31,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
}
public void logoutHttpSessions(Manager sessionManager, List sessionIds) {
- log.fine("logoutHttpSessions: " + sessionIds);
+ log.debug("logoutHttpSessions: " + sessionIds);
for (String sessionId : sessionIds) {
logoutSession(sessionManager, sessionId);
@@ -40,14 +39,13 @@ public class CatalinaUserSessionManagement implements SessionListener {
}
protected void logoutSession(Manager manager, String httpSessionId) {
- log.fine("logoutHttpSession: " + httpSessionId);
+ log.debug("logoutHttpSession: " + httpSessionId);
Session session;
try {
session = manager.findSession(httpSessionId);
} catch (IOException ioe) {
- log.warning("IO exception when looking for session " + httpSessionId);
- ioe.printStackTrace();
+ log.warn("IO exception when looking for session " + httpSessionId, ioe);
return;
}
@@ -58,7 +56,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
try {
session.expire();
} 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)
Session session = event.getSession();
- log.fine("Session " + session.getId() + " destroyed");
+ log.debugf("Session %s destroyed", session.getId());
GenericPrincipal principal = (GenericPrincipal) session.getPrincipal();
if (principal == null) return;
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java
index 7dc3f406ee..f4fc302391 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java
@@ -2,17 +2,17 @@ package org.keycloak.adapters.tomcat7;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
+import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakDeployment;
import javax.servlet.http.HttpServletResponse;
-import java.util.logging.Logger;
/**
- * @author Davide Ungari
+ * @author Bill Burke
* @version $Revision: 1 $
*/
public class CorsPreflightChecker {
- private static final Logger log = Logger.getLogger(""+CorsPreflightChecker.class);
+ private static final Logger log = Logger.getLogger(CorsPreflightChecker.class);
protected KeycloakDeployment deployment;
public CorsPreflightChecker(KeycloakDeployment deployment) {
@@ -20,17 +20,17 @@ public class CorsPreflightChecker {
}
public boolean checkCorsPreflight(Request request, Response response) {
- log.finer("checkCorsPreflight " + request.getRequestURI());
+ log.debugv("checkCorsPreflight {0}", request.getRequestURI());
if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
- log.finer("checkCorsPreflight: not options ");
+ log.debug("checkCorsPreflight: not options ");
return false;
}
if (request.getHeader("Origin") == null) {
- log.finer("checkCorsPreflight: no origin header");
+ log.debug("checkCorsPreflight: no origin header");
return false;
}
- log.finer("Preflight request returning");
+ log.debug("Preflight request returning");
response.setStatus(HttpServletResponse.SC_OK);
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 574a76afc7..f5a1b3afe6 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -132,6 +132,7 @@ public final class KeycloakModelUtils {
}
public static void generateClientKeyPairCertificate(ClientModel client) {
+ String subject = client.getClientId();
KeyPair keyPair = null;
try {
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
@@ -140,7 +141,7 @@ public final class KeycloakModelUtils {
}
X509Certificate certificate = null;
try {
- certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, client.getClientId());
+ certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/saml/saml-protocol/pom.xml b/saml/saml-protocol/pom.xml
index ea64438253..7e9c389ff2 100755
--- a/saml/saml-protocol/pom.xml
+++ b/saml/saml-protocol/pom.xml
@@ -18,6 +18,11 @@
yyyy-MM-dd HH:mm
+
+ org.jboss.resteasy
+ resteasy-multipart-provider
+ provided
+
org.keycloak
keycloak-core
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java
new file mode 100755
index 0000000000..32f0990d6c
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java
@@ -0,0 +1,21 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.exportimport.ApplicationImporter;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class EntityDescriptorImporter implements ApplicationImporter {
+ @Override
+ public Object createJaxrsService(RealmModel realm, RealmAuth auth) {
+ return new EntityDescriptorImporterService(realm, auth);
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterFactory.java
new file mode 100755
index 0000000000..43252b7a05
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterFactory.java
@@ -0,0 +1,37 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ApplicationImporter;
+import org.keycloak.exportimport.ApplicationImporterFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class EntityDescriptorImporterFactory implements ApplicationImporterFactory {
+ @Override
+ public String getDisplayName() {
+ return "SAML 2.0 Entity Descriptor";
+ }
+
+ @Override
+ public ApplicationImporter create(KeycloakSession session) {
+ return new EntityDescriptorImporter();
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getId() {
+ return "saml2-entity-descriptor";
+ }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
new file mode 100755
index 0000000000..833fa2de3f
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
@@ -0,0 +1,144 @@
+package org.keycloak.protocol.saml;
+
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.resources.admin.RealmAuth;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ParsingException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
+import org.picketlink.identity.federation.core.saml.v2.util.SAMLMetadataUtil;
+import org.picketlink.identity.federation.core.util.CoreConfigUtil;
+import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.KeyTypes;
+import org.picketlink.identity.federation.saml.v2.metadata.SPSSODescriptorType;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class EntityDescriptorImporterService {
+ protected RealmModel realm;
+ protected RealmAuth auth;
+
+ public EntityDescriptorImporterService(RealmModel realm, RealmAuth auth) {
+ this.realm = realm;
+ this.auth = auth;
+ }
+
+ @POST
+ @Path("upload")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public void updateEntityDescriptor(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+ auth.requireManage();
+
+ Map> uploadForm = input.getFormDataMap();
+ List inputParts = uploadForm.get("file");
+
+ InputStream is = inputParts.get(0).getBody(InputStream.class, null);
+
+ loadEntityDescriptors(is, realm);
+
+ }
+
+ public static void loadEntityDescriptors(InputStream is, RealmModel realm) {
+ Object metadata = null;
+ try {
+ metadata = new SAMLParser().parse(is);
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ }
+ EntitiesDescriptorType entities;
+
+ if (EntitiesDescriptorType.class.isInstance(metadata)) {
+ entities = (EntitiesDescriptorType) metadata;
+ } else {
+ entities = new EntitiesDescriptorType();
+ entities.addEntityDescriptor(metadata);
+ }
+
+ for (Object o : entities.getEntityDescriptor()) {
+ EntityDescriptorType entity = (EntityDescriptorType)o;
+ String entityId = entity.getEntityID();
+ ApplicationModel app = realm.addApplication(entityId);
+ app.setFullScopeAllowed(true);
+ app.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
+ app.setAttribute(SamlProtocol.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true
+ app.setAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString());
+ app.setAttribute(SamlProtocol.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+ SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity);
+ if (spDescriptorType.isWantAssertionsSigned()) {
+ app.setAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+ }
+ String adminUrl = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
+ if (adminUrl != null) app.setManagementUrl(adminUrl);
+
+ String urlPattern = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
+ if (urlPattern == null) {
+ urlPattern = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
+ }
+ if (urlPattern != null) {
+ app.addRedirectUri(urlPattern);
+ }
+
+ for (KeyDescriptorType keyDescriptor : spDescriptorType.getKeyDescriptor()) {
+ X509Certificate cert = null;
+ try {
+ cert = SAMLMetadataUtil.getCertificate(keyDescriptor);
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ } catch (ProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ String certPem = KeycloakModelUtils.getPemFromCertificate(cert);
+ if (keyDescriptor.getUse() == KeyTypes.SIGNING) {
+ app.setAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+ app.setAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
+ } else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) {
+ app.setAttribute(SamlProtocol.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+ app.setAttribute(SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem);
+ }
+ }
+ }
+ }
+
+ public static String getLogoutLocation(SPSSODescriptorType idp, String bindingURI) {
+ String logoutResponseLocation = null;
+
+ List endpoints = idp.getSingleLogoutService();
+ for (EndpointType endpoint : endpoints) {
+ if (endpoint.getBinding().toString().equals(bindingURI)) {
+ if (endpoint.getLocation() != null) {
+ logoutResponseLocation = endpoint.getLocation().toString();
+ } else {
+ logoutResponseLocation = null;
+ }
+
+ break;
+ }
+
+ }
+ return logoutResponseLocation;
+ }
+
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 99b786161c..e5d1571799 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -17,8 +17,8 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.util.PemUtils;
import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants;
@@ -35,6 +35,13 @@ import java.security.PublicKey;
*/
public class SamlProtocol implements LoginProtocol {
protected static final Logger logger = Logger.getLogger(SamlProtocol.class);
+
+
+ public static final String ATTRIBUTE_TRUE_VALUE = "true";
+ public static final String ATTRIBUTE_FALSE_VALUE = "false";
+ 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 final String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
public static final String LOGIN_PROTOCOL = "saml";
public static final String SAML_BINDING = "saml_binding";
public static final String SAML_POST_BINDING = "post";
@@ -46,7 +53,7 @@ public class SamlProtocol implements LoginProtocol {
public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
public static final String SAML_ENCRYPT = "saml.encrypt";
public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
- public static final String REQUEST_ID = "REQUEST_ID";
+ public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID";
protected KeycloakSession session;
@@ -114,7 +121,7 @@ public class SamlProtocol implements LoginProtocol {
public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
ClientSessionModel clientSession = accessCode.getClientSession();
ClientModel client = clientSession.getClient();
- String requestID = clientSession.getNote(REQUEST_ID);
+ String requestID = clientSession.getNote(SAML_REQUEST_ID);
String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
String redirectUri = clientSession.getRedirectUri();
String responseIssuer = getResponseIssuer(realm);
@@ -155,7 +162,7 @@ public class SamlProtocol implements LoginProtocol {
if (requiresEncryption(client)) {
PublicKey publicKey = null;
try {
- publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
+ publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
} catch (Exception e) {
logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
index 66828b2ddc..7ddabe981a 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
@@ -6,11 +6,9 @@ import org.keycloak.util.PemUtils;
import org.picketlink.common.exceptions.ProcessingException;
import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import java.security.KeyPair;
import java.security.PublicKey;
-import java.security.cert.X509Certificate;
+import java.security.cert.Certificate;
/**
* @author Bill Burke
@@ -19,11 +17,11 @@ import java.security.cert.X509Certificate;
public class SamlProtocolUtils {
public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException {
- if (!"true".equals(client.getAttribute("saml.client.signature"))) {
+ if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
return;
}
SAML2Signature saml2Signature = new SAML2Signature();
- PublicKey publicKey = getPublicKey(client);
+ PublicKey publicKey = getSignatureValidationKey(client);
try {
if (!saml2Signature.validate(document, publicKey)) {
throw new VerificationException("Invalid signature on document");
@@ -33,16 +31,24 @@ public class SamlProtocolUtils {
}
}
- public static PublicKey getPublicKey(ClientModel client) throws VerificationException {
- String publicKeyPem = client.getAttribute(ClientModel.PUBLIC_KEY);
- if (publicKeyPem == null) throw new VerificationException("Client does not have a public key.");
- PublicKey publicKey = null;
+ public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException {
+ return getPublicKey(client, SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
+ }
+
+ public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
+ return getPublicKey(client, SamlProtocol.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 {
- publicKey = PemUtils.decodePublicKey(publicKeyPem);
+ cert = PemUtils.decodeCertificate(certPem);
} 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();
}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 0fc67a8e47..8f51234a48 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -198,7 +198,7 @@ public class SamlService {
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
clientSession.setNote(SamlProtocol.SAML_BINDING, getBindingType());
clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
- clientSession.setNote(SamlProtocol.REQUEST_ID, requestAbstractType.getID());
+ clientSession.setNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID());
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
if (response != null) return response;
@@ -309,7 +309,7 @@ public class SamlService {
// todo maybe a flag?
// SamlProtocolUtils.verifyDocumentSignature(client, documentHolder.getSamlDocument());
- PublicKey publicKey = SamlProtocolUtils.getPublicKey(client);
+ PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
UriBuilder builder = UriBuilder.fromPath("/")
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ApplicationImporterFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ApplicationImporterFactory
new file mode 100755
index 0000000000..0971c24781
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ApplicationImporterFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.EntityDescriptorImporterFactory
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/exportimport/ApplicationImportSpi.java b/services/src/main/java/org/keycloak/exportimport/ApplicationImportSpi.java
new file mode 100755
index 0000000000..4493b19e22
--- /dev/null
+++ b/services/src/main/java/org/keycloak/exportimport/ApplicationImportSpi.java
@@ -0,0 +1,26 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Marek Posolda
+ */
+public class ApplicationImportSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "application-import";
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return ApplicationImporter.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory> getProviderFactoryClass() {
+ return ApplicationImporterFactory.class;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/exportimport/ApplicationImporter.java b/services/src/main/java/org/keycloak/exportimport/ApplicationImporter.java
new file mode 100755
index 0000000000..f4c526ef40
--- /dev/null
+++ b/services/src/main/java/org/keycloak/exportimport/ApplicationImporter.java
@@ -0,0 +1,15 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+/**
+ * Provider plugin interface for importing applications from an arbitrary configuration format
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface ApplicationImporter extends Provider {
+ public Object createJaxrsService(RealmModel realm, RealmAuth auth);
+}
diff --git a/services/src/main/java/org/keycloak/exportimport/ApplicationImporterFactory.java b/services/src/main/java/org/keycloak/exportimport/ApplicationImporterFactory.java
new file mode 100755
index 0000000000..b73e2e68c3
--- /dev/null
+++ b/services/src/main/java/org/keycloak/exportimport/ApplicationImporterFactory.java
@@ -0,0 +1,13 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * Provider plugin interface for importing applications from an arbitrary configuration format
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface ApplicationImporterFactory extends ProviderFactory {
+ public String getDisplayName();
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index 5e97698a02..5d4f176e43 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -120,9 +120,9 @@ public class ApplicationResource {
return ModelToRepresentation.toRepresentation(application);
}
- @Path("certificates")
- public ClientCertificateResource getCertficateResource() {
- return new ClientCertificateResource(realm, auth, application, session);
+ @Path("certificates/{attr}")
+ public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
+ return new ClientAttributeCertificateResource(realm, auth, application, session, attributePrefix);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
similarity index 65%
rename from services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
rename to services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index 068ee09558..9e064c4fab 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -6,17 +6,11 @@ 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.AdminRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.services.ForbiddenException;
-import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.CertificateUtils;
import org.keycloak.util.PemUtils;
import javax.ws.rs.Consumes;
@@ -24,27 +18,20 @@ import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
import java.security.PrivateKey;
-import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
@@ -53,17 +40,26 @@ import java.util.Map;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class ClientCertificateResource {
+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 ClientCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session) {
+ 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 {
@@ -79,14 +75,6 @@ public class ClientCertificateResource {
this.privateKey = privateKey;
}
- public String getPublicKey() {
- return publicKey;
- }
-
- public void setPublicKey(String publicKey) {
- this.publicKey = publicKey;
- }
-
public String getCertificate() {
return certificate;
}
@@ -101,9 +89,8 @@ public class ClientCertificateResource {
@Produces(MediaType.APPLICATION_JSON)
public ClientKeyPairInfo getKeyInfo() {
ClientKeyPairInfo info = new ClientKeyPairInfo();
- info.setCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
- info.setPrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
- info.setPublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
+ info.setCertificate(client.getAttribute(certificateAttribute));
+ info.setPrivateKey(client.getAttribute(privateAttribute));
return info;
}
@@ -115,51 +102,79 @@ public class ClientCertificateResource {
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(ClientModel.X509CERTIFICATE));
- info.setPrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
- info.setPublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
+ info.setCertificate(client.getAttribute(certificateAttribute));
+ info.setPrivateKey(client.getAttribute(privateAttribute));
return info;
}
@POST
- @Path("upload/jks")
+ @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> uploadForm = input.getFormDataMap();
List inputParts = uploadForm.get("file");
String keystoreFormat = uploadForm.get("keystoreFormat").get(0).getBodyAsString();
String keyAlias = uploadForm.get("keyAlias").get(0).getBodyAsString();
- String keyPassword = uploadForm.get("keyPassword").get(0).getBodyAsString();
- String storePassword = uploadForm.get("storePassword").get(0).getBodyAsString();
- System.out.println("format = '" + keystoreFormat + "'");
+ List keyPasswordPart = uploadForm.get("keyPassword");
+ char[] keyPassword = keyPasswordPart != null ? keyPasswordPart.get(0).getBodyAsString().toCharArray() : null;
+
+ List 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.toCharArray());
- privateKey = (PrivateKey)keyStore.getKey(keyAlias, keyPassword.toCharArray());
+ 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);
}
- String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
- String publicKeyPem = KeycloakModelUtils.getPemFromKey(certificate.getPublicKey());
- String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
- client.setAttribute(ClientModel.PRIVATE_KEY, privateKeyPem);
- client.setAttribute(ClientModel.PUBLIC_KEY, publicKeyPem);
- client.setAttribute(ClientModel.X509CERTIFICATE, certPem);
+ if (privateKey != null) {
+ String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+ client.setAttribute(privateAttribute, privateKeyPem);
+ info.setPrivateKey(privateKeyPem);
+ } else if (certificate != null) {
+ client.removeAttribute(privateAttribute);
+ }
- ClientKeyPairInfo info = new ClientKeyPairInfo();
- info.setCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
- info.setPrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
- info.setPublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
+ if (certificate != null) {
+ String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
+ client.setAttribute(certificateAttribute, certPem);
+ info.setCertificate(certPem);
+ }
return info;
@@ -234,10 +249,12 @@ public class ClientCertificateResource {
throw new NotAcceptableException("Only support jks format.");
}
String format = config.getFormat();
- if (client.getAttribute(ClientModel.PRIVATE_KEY) == null) {
+ String privatePem = client.getAttribute(privateAttribute);
+ String certPem = client.getAttribute(certificateAttribute);
+ if (privatePem == null && certPem == null) {
throw new NotFoundException("keypair not generated for client");
}
- if (config.getKeyPassword() == null) {
+ if (privatePem != null && config.getKeyPassword() == null) {
throw new BadRequestException("Need to specify a key password for jks download");
}
if (config.getStorePassword() == null) {
@@ -250,13 +267,19 @@ public class ClientCertificateResource {
keyStore.load(null, null);
String keyAlias = config.getKeyAlias();
if (keyAlias == null) keyAlias = client.getClientId();
- PrivateKey privateKey = PemUtils.decodePrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
- X509Certificate clientCert = PemUtils.decodeCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
+ if (privatePem != null) {
+ PrivateKey privateKey = PemUtils.decodePrivateKey(privatePem);
+ X509Certificate clientCert = PemUtils.decodeCertificate(certPem);
- Certificate[] chain = {clientCert};
+ Certificate[] chain = {clientCert};
+
+ keyStore.setKeyEntry(keyAlias, privateKey, config.getKeyPassword().trim().toCharArray(), chain);
+ } else {
+ X509Certificate clientCert = PemUtils.decodeCertificate(certPem);
+ keyStore.setCertificateEntry(keyAlias, clientCert);
+ }
- keyStore.setKeyEntry(keyAlias, privateKey, config.getKeyPassword().trim().toCharArray(), chain);
if (config.isRealmCertificate() == null || config.isRealmCertificate().booleanValue()) {
X509Certificate certificate = realm.getCertificate();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
index 4a03d08a30..63805e4447 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
@@ -24,6 +24,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -72,9 +73,9 @@ public class OAuthClientResource {
return new ClaimResource(oauthClient, auth);
}
- @Path("certificates")
- public ClientCertificateResource getCertficateResource() {
- return new ClientCertificateResource(realm, auth, oauthClient, session);
+ @Path("certificates/{attr}")
+ public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
+ return new ClientAttributeCertificateResource(realm, auth, oauthClient, session, attributePrefix);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index bc4167e3eb..a8f3707c52 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -8,6 +8,7 @@ import org.keycloak.events.Event;
import org.keycloak.events.EventQuery;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType;
+import org.keycloak.exportimport.ApplicationImporter;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
@@ -73,6 +74,17 @@ public class RealmAdminResource {
auth.init(RealmAuth.Resource.REALM);
}
+ /**
+ * Base path for importing applications under this realm.
+ *
+ * @return
+ */
+ @Path("application-importers/{formatId}")
+ public Object getApplicationImporter(@PathParam("formatId") String formatId) {
+ ApplicationImporter importer = session.getProvider(ApplicationImporter.class, formatId);
+ return importer.createJaxrsService(realm, auth);
+ }
+
/**
* Base path for managing applications under this realm.
*
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index 42f3264df5..a0af968d75 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -2,9 +2,14 @@ package org.keycloak.services.resources.admin;
import org.keycloak.Version;
import org.keycloak.events.EventListenerProvider;
+import org.keycloak.exportimport.ApplicationImporter;
+import org.keycloak.exportimport.ApplicationImporterFactory;
import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocolFactory;
+import org.keycloak.provider.ProviderFactory;
import org.keycloak.social.SocialProvider;
import org.keycloak.util.ProviderLoader;
@@ -37,6 +42,8 @@ public class ServerInfoAdminResource {
setSocialProviders(info);
setThemes(info);
setEventListeners(info);
+ setProtocols(info);
+ setApplicationImporters(info);
return info;
}
@@ -69,6 +76,26 @@ public class ServerInfoAdminResource {
}
}
+
+ private void setProtocols(ServerInfoRepresentation info) {
+ info.protocols = new LinkedList();
+ for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {
+ info.protocols.add(p.getId());
+ }
+ Collections.sort(info.protocols);
+ }
+
+ private void setApplicationImporters(ServerInfoRepresentation info) {
+ info.applicationImporters = new LinkedList>();
+ for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ApplicationImporter.class)) {
+ ApplicationImporterFactory factory = (ApplicationImporterFactory)p;
+ Map data = new HashMap();
+ data.put("id", factory.getId());
+ data.put("name", factory.getDisplayName());
+ info.applicationImporters.add(data);
+ }
+ }
+
public static class ServerInfoRepresentation {
private String version;
@@ -76,6 +103,8 @@ public class ServerInfoAdminResource {
private Map> themes;
private List socialProviders;
+ private List protocols;
+ private List> applicationImporters;
private List eventListeners;
@@ -102,6 +131,14 @@ public class ServerInfoAdminResource {
public List getEventListeners() {
return eventListeners;
}
+
+ public List getProtocols() {
+ return protocols;
+ }
+
+ public List> getApplicationImporters() {
+ return applicationImporters;
+ }
}
}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 62aa8e7809..7cea20de62 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,2 @@
-org.keycloak.protocol.LoginProtocolSpi
\ No newline at end of file
+org.keycloak.protocol.LoginProtocolSpi
+org.keycloak.exportimport.ApplicationImportSpi
\ No newline at end of file
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index b7b5428fc4..a117e14a81 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -222,6 +222,18 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.2
+
+
+
+ test-jar
+
+
+
+
org.apache.maven.plugins
maven-deploy-plugin
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index 2700d0f3cf..302fc91b4a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -1,15 +1,40 @@
package org.keycloak.testsuite.saml;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
+import org.keycloak.Config;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.admin.AdminRoot;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.InputStream;
+
/**
* @author Bill Burke
* @version $Revision: 1 $
@@ -24,10 +49,12 @@ public class SamlBindingTest {
initializeSamlSecuredWar("/saml/simple-post", "/sales-post", "post.war", classLoader);
initializeSamlSecuredWar("/saml/signed-post", "/sales-post-sig", "post-sig.war", classLoader);
+ initializeSamlSecuredWar("/saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
initializeSamlSecuredWar("/saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
initializeSamlSecuredWar("/saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
initializeSamlSecuredWar("/saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
initializeSamlSecuredWar("/saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
+ uploadSP();
}
@@ -113,5 +140,64 @@ public class SamlBindingTest {
Assert.assertTrue(driver.getPageSource().contains("null"));
}
+ private static String createToken() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+
+ RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
+ ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
+ TokenManager tm = new TokenManager();
+ UserModel admin = session.users().getUserByUsername("admin", adminRealm);
+ UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
+ return tm.encodeToken(adminRealm, token);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ @Test
+ public void testMetadataPostSignedLoginLogout() throws Exception {
+
+ driver.navigate().to("http://localhost:8081/sales-metadata/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-metadata/");
+ String pageSource = driver.getPageSource();
+ Assert.assertTrue(pageSource.contains("bburke"));
+ driver.navigate().to("http://localhost:8081/sales-metadata?GLO=true");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+
+ }
+
+ public static void uploadSP() {
+ String token = createToken();
+ final String authHeader = "Bearer " + token;
+ ClientRequestFilter authFilter = new ClientRequestFilter() {
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+ }
+ };
+ Client client = ClientBuilder.newBuilder().register(authFilter).build();
+ UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
+ WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
+
+
+ MultipartFormDataOutput formData = new MultipartFormDataOutput();
+ InputStream is = SamlBindingTest.class.getResourceAsStream("/saml/sp-metadata.xml");
+ Assert.assertNotNull(is);
+ formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
+
+ WebTarget upload = adminRealms.path("demo/application-importers/saml2-entity-descriptor/upload");
+ System.out.println(upload.getUri());
+ Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
+ Assert.assertEquals(204, response.getStatus());
+ response.close();
+ client.close();
+ }
+
}
diff --git a/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/keystore.jks
new file mode 100755
index 0000000000..144830bc77
Binary files /dev/null and b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/picketlink.xml b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/picketlink.xml
new file mode 100755
index 0000000000..17d37df5f9
--- /dev/null
+++ b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/picketlink.xml
@@ -0,0 +1,31 @@
+
+
+ ${idp-sig.url::http://localhost:8081/auth/realms/demo/protocol/saml}
+
+ ${sales-post-sig.url::http://localhost:8081/sales-metadata/}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testsuite/integration/src/test/resources/saml/sp-metadata.xml b/testsuite/integration/src/test/resources/saml/sp-metadata.xml
new file mode 100755
index 0000000000..9b8b8990f3
--- /dev/null
+++ b/testsuite/integration/src/test/resources/saml/sp-metadata.xml
@@ -0,0 +1,38 @@
+
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+
+
+
+
+
+
+
+ MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw==
+
+
+
+
+
+
+ JBoss
+ JBoss by Red Hat
+ http://localhost:8080/sales-metadata/
+
+
+ The
+ Admin
+ admin@mycompany.com
+
+
+
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json
index 9196dd7fb8..8098d5b0bb 100755
--- a/testsuite/integration/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/saml/testsaml.json
@@ -57,9 +57,8 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "privateKey": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
- "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB",
- "X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
{
@@ -76,9 +75,8 @@
"saml.server.signature": "true",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "privateKey": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
- "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB",
- "X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
{
@@ -95,9 +93,8 @@
"saml.server.signature": "true",
"saml.client.signature": "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",
- "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB",
- "X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+ "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",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
}
},
{
@@ -116,9 +113,10 @@
"saml.client.signature": "true",
"saml.encrypt": "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",
- "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB",
- "X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+ "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",
+ "saml.signing.certificate": "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.signature.algorithm": "RSA_SHA1",
"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",
- "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQAB",
- "X509Certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
+ "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",
+ "saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
}
}
],
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index 8780d19241..9c2186d993 100755
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -26,6 +26,7 @@
integration
+ tomcat7
performance
tools
performance-web
diff --git a/testsuite/tomcat7/pom.xml b/testsuite/tomcat7/pom.xml
new file mode 100755
index 0000000000..419d6e8bda
--- /dev/null
+++ b/testsuite/tomcat7/pom.xml
@@ -0,0 +1,513 @@
+
+
+
+ keycloak-testsuite-pom
+ org.keycloak
+ 1.1.0-Alpha1-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ keycloak-testsuite-tomcat7
+ Keycloak Tomcat 7Integration TestSuite
+
+
+
+
+ org.keycloak
+ keycloak-dependencies-server-all
+ ${project.version}
+ pom
+
+
+ org.keycloak
+ keycloak-admin-client
+ ${project.version}
+
+
+ log4j
+ log4j
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-log4j12
+ ${slf4j.version}
+
+
+ org.jboss.spec.javax.servlet
+ jboss-servlet-api_3.0_spec
+
+
+ org.jboss.resteasy
+ jaxrs-api
+ ${resteasy.version.latest}
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+ ${resteasy.version.latest}
+
+
+ log4j
+ log4j
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-simple
+
+
+
+
+ org.jboss.resteasy
+ resteasy-client
+ ${resteasy.version.latest}
+
+
+ org.jboss.resteasy
+ resteasy-crypto
+ ${resteasy.version.latest}
+
+
+ org.jboss.resteasy
+ resteasy-multipart-provider
+ ${resteasy.version.latest}
+
+
+ org.jboss.resteasy
+ resteasy-jackson-provider
+ ${resteasy.version.latest}
+
+
+ org.jboss.resteasy
+ resteasy-undertow
+ ${resteasy.version.latest}
+
+
+ com.google.zxing
+ javase
+
+
+ org.bouncycastle
+ bcprov-jdk16
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${keycloak.apache.httpcomponents.version}
+
+
+ org.keycloak
+ keycloak-ldap-federation
+ ${project.version}
+
+
+ org.keycloak
+ keycloak-undertow-adapter
+ ${project.version}
+
+
+ org.keycloak
+ federation-properties-example
+ ${project.version}
+
+
+ org.jboss.logging
+ jboss-logging
+
+
+ io.undertow
+ undertow-servlet
+
+
+ io.undertow
+ undertow-core
+
+
+ org.codehaus.jackson
+ jackson-core-asl
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+
+
+ org.codehaus.jackson
+ jackson-xc
+
+
+ junit
+ junit
+
+
+ org.hamcrest
+ hamcrest-all
+
+
+ org.hibernate.javax.persistence
+ hibernate-jpa-2.0-api
+
+
+ com.h2database
+ h2
+
+
+ org.hibernate
+ hibernate-entitymanager
+
+
+ com.icegreen
+ greenmail
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ org.infinispan
+ infinispan-core
+
+
+ org.seleniumhq.selenium
+ selenium-java
+
+
+ xml-apis
+ xml-apis
+
+
+ org.seleniumhq.selenium
+ selenium-chrome-driver
+
+
+ org.picketbox
+ picketbox-ldap
+ test-jar
+
+
+ org.picketbox
+ picketbox-ldap
+
+
+ org.picketlink
+ picketlink-wildlfy-common
+
+
+ org.wildfly
+ wildfly-undertow
+ ${wildfly.version}
+ test
+
+
+ org.keycloak
+ keycloak-testsuite-integration
+ ${project.version}
+ test
+
+
+ org.keycloak
+ keycloak-testsuite-integration
+ ${project.version}
+ test-jar
+ test
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+ 7.0.54
+
+
+ org.apache.tomcat
+ tomcat-util
+ 7.0.54
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ 7.0.54
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.2
+
+
+
+ test-jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ ${project.basedir}
+
+
+
+
+
+
+
+ keycloak-server
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ org.keycloak.testutils.KeycloakServer
+
+
+
+
+
+
+ mail-server
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ org.keycloak.testutils.MailServer
+
+
+
+
+
+
+ totp
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ org.keycloak.testutils.TotpGenerator
+
+
+
+
+
+
+
+ jpa
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ jpa
+ jpa
+ jpa
+ jpa
+
+
+
+
+
+
+
+
+ mongo
+
+
+ localhost
+ 27018
+ keycloak
+ true
+ 127.0.0.1
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ test
+ integration-test
+
+ test
+
+
+
+ mongo
+ mongo
+ mongo
+ mongo
+ ${keycloak.connectionsMongo.host}
+ ${keycloak.connectionsMongo.port}
+ ${keycloak.connectionsMongo.db}
+ ${keycloak.connectionsMongo.clearOnStartup}
+ ${keycloak.connectionsMongo.bindIp}
+
+
+
+
+ default-test
+
+ true
+
+
+
+
+
+
+
+ com.github.joelittlejohn.embedmongo
+ embedmongo-maven-plugin
+
+
+ start-mongodb
+ pre-integration-test
+
+ start
+
+
+ ${keycloak.connectionsMongo.port}
+ file
+ ${project.build.directory}/mongodb.log
+ ${keycloak.connectionsMongo.bindIp}
+
+
+
+ stop-mongodb
+ post-integration-test
+
+ stop
+
+
+
+
+
+
+
+
+
+
+ infinispan
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ infinispan
+ infinispan
+ infinispan
+
+
+
+
+
+
+
+
+
+
+
+ keycloak.connectionsJpa.driver
+ com.mysql.jdbc.Driver
+
+
+ mysql
+
+
+ mysql
+ mysql-connector-java
+ ${mysql.version}
+
+
+
+
+
+
+
+
+ keycloak.connectionsJpa.driver
+ org.postgresql.Driver
+
+
+ postgresql
+
+
+ org.postgresql
+ postgresql
+ ${postgresql.version}
+
+
+
+
+
+ clean-jpa
+
+
+
+ org.liquibase
+ liquibase-maven-plugin
+
+ META-INF/jpa-changelog-master.xml
+
+ ${keycloak.connectionsJpa.url}
+ ${keycloak.connectionsJpa.driver}
+ ${keycloak.connectionsJpa.user}
+ ${keycloak.connectionsJpa.password}
+
+ false
+
+
+
+ clean-jpa
+ clean
+
+ dropAll
+
+
+
+
+
+
+
+
+
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
new file mode 100755
index 0000000000..71ecbae0c2
--- /dev/null
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
@@ -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 Stian Thorgersen
+ */
+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 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 m = new HashMap();
+ List 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");
+ }
+
+}
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java
new file mode 100755
index 0000000000..78072330a3
--- /dev/null
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java
@@ -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 Bill Burke
+ * @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();
+ }}