From ce76270ad84fb38725dbb9178f610ec00cc76eff Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Fri, 24 Oct 2014 10:58:32 -0400 Subject: [PATCH 1/4] saml key refactor --- .../theme/admin/base/resources/js/app.js | 30 +- .../resources/js/controllers/applications.js | 187 +++++++ .../theme/admin/base/resources/js/services.js | 15 +- .../partials/application-saml-key-export.html | 62 +++ .../partials/application-saml-key-import.html | 59 ++ .../partials/application-saml-keys.html | 66 +++ .../templates/kc-navigation-application.html | 2 +- .../as7/KeycloakAuthenticatorValve.java | 8 +- integration/tomcat7/adapter/pom.xml | 6 + .../tomcat7/AuthenticatedActionsValve.java | 16 +- .../tomcat7/CatalinaRequestAuthenticator.java | 21 +- .../CatalinaUserSessionManagement.java | 18 +- .../tomcat7/CorsPreflightChecker.java | 14 +- .../tomcat7/KeycloakAuthenticatorValve.java | 105 ++-- .../models/utils/KeycloakModelUtils.java | 3 +- .../keycloak/protocol/saml/SamlProtocol.java | 2 +- .../protocol/saml/SamlProtocolUtils.java | 29 +- .../keycloak/protocol/saml/SamlService.java | 2 +- .../resources/admin/ApplicationResource.java | 6 +- .../ClientAttributeCertificateResource.java | 307 +++++++++++ testsuite/integration/pom.xml | 12 + .../src/test/resources/saml/testsaml.json | 27 +- testsuite/pom.xml | 1 + testsuite/tomcat7/pom.xml | 513 ++++++++++++++++++ .../org/keycloak/testsuite/Tomcat7Test.java | 283 ++++++++++ .../testsuite/tomcat7/TomcatAdapterTest.java | 24 + 26 files changed, 1688 insertions(+), 130 deletions(-) create mode 100755 forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html create mode 100755 forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html create mode 100755 forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html create mode 100755 services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java create mode 100755 testsuite/tomcat7/pom.xml create mode 100755 testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java create mode 100755 testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java 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..6eeed64744 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', 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..9e9758a55b 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,6 +43,193 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real }); }); +module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application, + ApplicationCertificate, ApplicationCertificateGenerate, + ApplicationCertificateDownload, Notifications) { + $scope.realm = realm; + $scope.application = application; + + var signingKeyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: 'saml.signing' }, + function() { + $scope.signingKeyInfo = signingKeyInfo; + } + ); + + $scope.generateSigningKey = function() { + var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id, attribute: 'saml.signing' }, + function() { + Notifications.success('Signing key has been regenerated.'); + $scope.signingKeyInfo = keyInfo; + }, + function() { + Notifications.error("Signing key was not regenerated."); + } + ); + }; + + $scope.importSigningKey = function() { + $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Signing/import/saml.signing"); + }; + + $scope.exportSigningKey = function() { + $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Signing/export/saml.signing"); + }; + + var encryptionKeyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: 'saml.encryption' }, + function() { + $scope.encryptionKeyInfo = encryptionKeyInfo; + } + ); + + $scope.generateEncryptionKey = function() { + var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id, attribute: 'saml.encryption' }, + function() { + Notifications.success('Encryption key has been regenerated.'); + $scope.encryptionKeyInfo = keyInfo; + }, + function() { + Notifications.error("Encryption key was not regenerated."); + } + ); + }; + + $scope.importEncryptionKey = function() { + $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Encryption/import/saml.encryption"); + }; + + $scope.exportEncryptionKey = function() { + $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Encryption/export/saml.encryption"); + }; + + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); +}); + +module.controller('ApplicationCertificateImportCtrl', function($scope, $location, $http, $upload, realm, application, $routeParams, + ApplicationCertificate, ApplicationCertificateGenerate, + ApplicationCertificateDownload, Notifications) { + + var keyType = $routeParams.keyType; + var attribute = $routeParams.attribute; + $scope.realm = realm; + $scope.application = application; + $scope.keyType = keyType; + + $scope.files = []; + + $scope.onFileSelect = function($files) { + $scope.files = $files; + }; + + $scope.clearFileSelect = function() { + $scope.files = null; + } + + $scope.keyFormats = [ + "JKS", + "PKCS12" + ]; + + $scope.uploadKeyFormat = $scope.keyFormats[0]; + + $scope.uploadFile = function() { + //$files: an array of files selected, each file has name, size, and type. + for (var i = 0; i < $scope.files.length; i++) { + var $file = $scope.files[i]; + $scope.upload = $upload.upload({ + url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/upload', + // method: POST or PUT, + // headers: {'headerKey': 'headerValue'}, withCredential: true, + data: {keystoreFormat: $scope.uploadKeyFormat, + keyAlias: $scope.uploadKeyAlias, + keyPassword: $scope.uploadKeyPassword, + storePassword: $scope.uploadStorePassword + }, + file: $file + /* set file formData name for 'Content-Desposition' header. Default: 'file' */ + //fileFormDataName: myFile, + /* customize how data is added to formData. See #40#issuecomment-28612000 for example */ + //formDataAppender: function(formData, key, val){} + }).progress(function(evt) { + console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total)); + }).success(function(data, status, headers) { + Notifications.success("Keystore uploaded successfully."); + $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/keys"); + }) + .error(function() { + Notifications.error("The key store can not be uploaded. Please verify the file."); + + }); + //.then(success, error, progress); + } + }; + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); +}); + +module.controller('ApplicationCertificateExportCtrl', function($scope, $location, $http, $upload, realm, application, $routeParams, + ApplicationCertificate, ApplicationCertificateGenerate, + ApplicationCertificateDownload, Notifications) { + var keyType = $routeParams.keyType; + var attribute = $routeParams.attribute; + $scope.realm = realm; + $scope.application = application; + $scope.keyType = keyType; + var jks = { + keyAlias: application.name, + realmAlias: realm.realm + }; + + $scope.keyFormats = [ + "JKS", + "PKCS12" + ]; + + var keyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: attribute }, + function() { + $scope.keyInfo = keyInfo; + } + ); + $scope.jks = jks; + $scope.jks.format = $scope.keyFormats[0]; + + $scope.download = function() { + $http({ + url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/download', + method: 'POST', + responseType: 'arraybuffer', + data: $scope.jks, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/octet-stream' + } + }).success(function(data){ + var blob = new Blob([data], { + type: 'application/octet-stream' + }); + var ext = ".jks"; + if ($scope.jks.format == 'PKCS12') ext = ".p12"; + saveAs(blob, 'keystore' + ext); + }).error(function(){ + Notifications.error("Error downloading."); + }); + } + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); +}); + module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, $upload, realm, application, ApplicationCertificate, ApplicationCertificateGenerate, ApplicationCertificateDownload, Notifications) { 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-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 @@ +
+
+ +
+ +

{{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 @@ +
+
+ +
+ +

{{application.name}} SAML {{keyType}} Key Import

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + + {{files[0].name}} + +
+
+
+ + +
+
+
+
+
\ 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 @@ +
+
+ +
+ +

{{application.name}} SAML Keys

+
+
+ Signing Key +
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+ + + +
+
+
+
+ Encryption Key +
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+ + + +
+
+
+
+
+
\ 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 @@