From 833bf9864356abe6f2c9f672edf1438b8635f48c Mon Sep 17 00:00:00 2001 From: Jan Lieskovsky Date: Wed, 2 Sep 2020 15:29:07 +0200 Subject: [PATCH] [KEYCLOAK-15692] Upgrade to Wildfly "21.0.1.Final" Base fixes: * [KEYCLOAK-15780] Upgrade Keycloak to Wildfly 21.0.0.Beta1 / Wildfly Core 13.0.0.Beta6 * [KEYCLOAK-16031] Upgrade Keycloak to Wildfly 21.0.0.Final / Wildfly Core 13.0.1.Final * [KEYCLOAK-16442] Upgrade Keycloak to Wildfly 21.0.1.Final / Wildfly Core 13.0.3.Final Other (dependent) fixes: * [KEYCLOAK-15408] Deprecate former Wildfly and Wildfly Core versions in Arquillian's testsuite pom.xml file as part of the upgrade script * [KEYCLOAK-15442] Update the version of 'jboss-parent' as part of the Wildfly upgrade script if necessary * [KEYCLOAK-15474] Add --verbose and --force options to the Wildfly upgrade automated script * [KEYCLOAK-15649] Update "urn:jboss:domain:infinispan:10.0" version as part of the Wildfly upgrade automated script * [KEYCLOAK-15652] Wildfly upgrade automated script - Align Python artifact version comparsion algorithm with the Maven / Java one Signed-off-by: Jan Lieskovsky --- .gitignore | 4 + boms/pom.xml | 4 +- .../common/util/CertificateUtils.java | 3 +- .../org/keycloak/common/util/PemUtils.java | 4 +- .../java/org/keycloak/RSAVerifierTest.java | 67 ++- ...ns,2.10.5,Apache Software License 2.0.txt} | 0 ...re,2.10.5,Apache Software License 2.0.txt} | 0 ...nd,2.10.5,Apache Software License 2.0.txt} | 0 ...se,2.10.5,Apache Software License 2.0.txt} | 0 ...er,2.10.5,Apache Software License 2.0.txt} | 0 ...ns,2.10.5,Apache Software License 2.0.txt} | 0 .../resources/licenses/keycloak/licenses.xml | 24 +- ...ns,2.10.5,Apache Software License 2.0.txt} | 0 ...re,2.10.5,Apache Software License 2.0.txt} | 0 ...nd,2.10.5,Apache Software License 2.0.txt} | 0 ...se,2.10.5,Apache Software License 2.0.txt} | 0 ...er,2.10.5,Apache Software License 2.0.txt} | 0 ...ns,2.10.5,Apache Software License 2.0.txt} | 0 .../resources/licenses/keycloak/licenses.xml | 46 +- ...ent,4.5.12,Apache Software License 2.0.txt | 558 ------------------ ...ore,4.4.13,Apache Software License 2.0.txt | 178 ------ .../resources/licenses/rh-sso/licenses.xml | 22 - ...dhat-00001,Apache Software License 2.0.txt | 558 ------------------ ...dhat-00001,Apache Software License 2.0.txt | 178 ------ .../configuration/domain/template.xml | 2 +- .../configuration/host/host-master.xml | 2 +- .../configuration/host/host-slave.xml | 2 +- .../resources/configuration/host/host.xml | 2 +- .../configuration/standalone/template.xml | 2 +- .../content/bin/migrate-standalone-ha.cli | 14 + .../content/bin/migrate-standalone.cli | 14 + .../{ => lib}/wildfly/upgrade/__init__.py | 466 +++++++++++++-- .../README.md | 10 + .../f8a_version_comparator/__init__.py | 28 + .../f8a_version_comparator/base.py | 28 + .../comparable_version.py | 213 +++++++ .../f8a_version_comparator/item_object.py | 204 +++++++ .../upgrade-keycloak-to-wildfly-tag.py | 125 +++- .../InfinispanClusterProviderFactory.java | 1 - .../infinispan/LockEntryPredicate.java | 14 +- ...ltInfinispanConnectionProviderFactory.java | 9 +- .../entities/AuthenticationSessionEntity.java | 25 +- .../RemoteCacheSessionsLoader.java | 30 +- .../cluster/infinispan/JDGPutTest.java | 30 +- .../infinispan/TestCacheManagerFactory.java | 23 +- .../InfinispanKeyStorageProviderTest.java | 5 +- .../ClusteredCacheBehaviorTest.java | 22 +- .../initializer/ConcurrencyLockingTest.java | 12 +- .../ConcurrencyVersioningTest.java | 20 +- .../DistributedCacheConcurrentWritesTest.java | 31 +- .../DistributedCacheWriteSkewTest.java | 65 +- .../initializer/L1SerializationIssueTest.java | 11 +- ...tdatedTopologyExceptionReproducerTest.java | 24 +- pom.xml | 38 +- .../src/main/resources/cluster-default.xml | 46 +- .../src/main/resources/cluster-local.xml | 46 +- testsuite/integration-arquillian/pom.xml | 14 +- .../src/main/content/conf/cluster-ha.xml | 46 +- .../src/main/content/conf/cluster-local.xml | 46 +- .../integration-arquillian/tests/base/pom.xml | 1 + .../arquillian/AuthServerTestEnricher.java | 199 +++++++ .../metrics/MetricsRestServiceTest.java | 2 +- .../src/test/resources/wildfly-config.xml | 57 ++ .../tests/other/pom.xml | 2 +- .../integration-arquillian/tests/pom.xml | 20 +- .../keycloak-datasources.xml | 2 +- .../keycloak-infinispan.xml | 37 +- .../subsystem-templates/keycloak-undertow.xml | 2 +- 68 files changed, 1672 insertions(+), 1966 deletions(-) rename distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.core,jackson-annotations,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.core,jackson-annotations,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.core,jackson-core,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.core,jackson-core,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.core,jackson-databind,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.core,jackson-databind,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.jaxrs,jackson-jaxrs-base,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.jaxrs,jackson-jaxrs-base,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.jaxrs,jackson-jaxrs-json-provider,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.jaxrs,jackson-jaxrs-json-provider,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.module,jackson-module-jaxb-annotations,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.module,jackson-module-jaxb-annotations,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.core,jackson-annotations,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.core,jackson-annotations,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.core,jackson-core,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.core,jackson-core,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.core,jackson-databind,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.core,jackson-databind,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.jaxrs,jackson-jaxrs-base,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.jaxrs,jackson-jaxrs-base,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.jaxrs,jackson-jaxrs-json-provider,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.jaxrs,jackson-jaxrs-json-provider,2.10.5,Apache Software License 2.0.txt} (100%) rename distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/{com.fasterxml.jackson.module,jackson-module-jaxb-annotations,2.10.4,Apache Software License 2.0.txt => com.fasterxml.jackson.module,jackson-module-jaxb-annotations,2.10.5,Apache Software License 2.0.txt} (100%) delete mode 100644 distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/org.apache.httpcomponents,httpclient,4.5.12,Apache Software License 2.0.txt delete mode 100644 distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/keycloak/org.apache.httpcomponents,httpcore,4.4.13,Apache Software License 2.0.txt delete mode 100644 distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/rh-sso/org.apache.httpcomponents,httpclient,4.5.12.redhat-00001,Apache Software License 2.0.txt delete mode 100644 distribution/adapters/fuse-adapter-zip/src/main/resources/licenses/rh-sso/org.apache.httpcomponents,httpcore,4.4.13.redhat-00001,Apache Software License 2.0.txt rename misc/scripts/upgrade-wildfly/{ => lib}/wildfly/upgrade/__init__.py (67%) create mode 100644 misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/README.md create mode 100644 misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/__init__.py create mode 100644 misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/base.py create mode 100644 misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/comparable_version.py create mode 100644 misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/item_object.py create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-config.xml diff --git a/.gitignore b/.gitignore index 67bae0d58d..593b98c69e 100644 --- a/.gitignore +++ b/.gitignore @@ -77,3 +77,7 @@ target # testsuite # ############# *offline-token.txt + +# Quarkus ephemeral data # +########################## +quarkus/data/*.db diff --git a/boms/pom.xml b/boms/pom.xml index 848f3862bd..884869aecc 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -1,4 +1,4 @@ - + - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml index f05fe32d86..556d016295 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml @@ -22,7 +22,7 @@ is also started by this host controller file. The other instance must be started via host-slave.xml --> - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml index 1a731414b0..dd7f46932d 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml @@ -17,7 +17,7 @@ ~ limitations under the License. --> - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml index ef52bd38f2..79279bc0cb 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml @@ -23,7 +23,7 @@ via host-slave.xml --> - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml index 27702cbc0e..9cd7c0abee 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml @@ -1,6 +1,6 @@ - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli index 92bcdb612d..bfc3f66bce 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli @@ -782,4 +782,18 @@ if (result != org.keycloak.keycloak-model-infinispan) of /subsystem=infinispan/c echo end-if +# Migrate from 11.0.0 to 12.0.0 + +if (result != expression "${jboss.mail.server.host:localhost}") of /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:read-attribute(name=host) + echo Adding host expression to the SMTP configuration of a remote destination outbound socket binding in the mail subsystem + /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:write-attribute(name=host, value=expression "${jboss.mail.server.host:localhost}") + echo +end-if + +if (result != expression "${jboss.mail.server.port:25}") of /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:read-attribute(name=port) + echo Adding port expression to the SMTP configuration of a remote destination outbound socket binding in the mail subsystem + /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:write-attribute(name=port, value=expression "${jboss.mail.server.port:25}") + echo +end-if + echo *** End Migration *** diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli index e82e198c45..988b83d1d9 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli @@ -647,4 +647,18 @@ if (result != org.keycloak.keycloak-model-infinispan) of /subsystem=infinispan/c echo end-if +# Migrate from 11.0.0 to 12.0.0 + +if (result != expression "${jboss.mail.server.host:localhost}") of /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:read-attribute(name=host) + echo Adding host expression to the SMTP configuration of a remote destination outbound socket binding in the mail subsystem + /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:write-attribute(name=host, value=expression "${jboss.mail.server.host:localhost}") + echo +end-if + +if (result != expression "${jboss.mail.server.port:25}") of /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:read-attribute(name=port) + echo Adding port expression to the SMTP configuration of a remote destination outbound socket binding in the mail subsystem + /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-smtp:write-attribute(name=port, value=expression "${jboss.mail.server.port:25}") + echo +end-if + echo *** End Migration *** diff --git a/misc/scripts/upgrade-wildfly/wildfly/upgrade/__init__.py b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/__init__.py similarity index 67% rename from misc/scripts/upgrade-wildfly/wildfly/upgrade/__init__.py rename to misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/__init__.py index 1e169d64de..6d1c89ccda 100644 --- a/misc/scripts/upgrade-wildfly/wildfly/upgrade/__init__.py +++ b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/__init__.py @@ -28,7 +28,7 @@ To use, simply 'import wildfly.upgrade' and call the necessary routines. import colorlog, copy, itertools, logging, lxml.etree, os, os.path, re, sys -from packaging.version import parse as parseVersion +from importlib import import_module from shutil import copyfileobj from subprocess import check_call, check_output from tempfile import NamedTemporaryFile @@ -36,12 +36,14 @@ from urllib.request import HTTPError, urlopen __all__ = [ 'getElementsByXPath', + 'getElementsByXPathFromRemoteXml', 'getKeycloakGitRepositoryRoot', 'getModuleLogger', 'getStepLogger', 'getTaskLogger', 'getPomDependencyByArtifactId', 'getPomProperty', + 'getPomPropertyFromRemoteXml', 'getVersionOfPomDependency', 'getXmlRoot', 'isWellFormedWildflyTag', @@ -49,23 +51,55 @@ __all__ = [ 'loadGavDictionaryFromXmlFile', 'saveUrlToNamedTemporaryFile' 'updateAdapterLicenseFile', - 'updateMainKeycloakPomFile' + 'performMainKeycloakPomFileUpdateTask', + 'performKeycloakAdapterLicenseFilesUpdateTask', + 'synchronizeInfinispanSubsystemXmlNamespaceWithWildfly' ] -__author__ = "Jan Lieskovsky " -__status__ = "Alpha" -__version__ = "0.0.1" +__author__ = "Jan Lieskovsky " +__loglevel__ = logging.INFO +__status__ = "Alpha" +__version__ = "0.0.2" # -# Various data structures for the module +# Various constants / data structures for the module # + +# Empty string +_empty_string = '' +# Base URL of the Keycloak's main pom.xml file from GitHub master branch +_keycloak_github_master_main_pom_base_url = "https://raw.githubusercontent.com/keycloak/keycloak/master/pom.xml" +# Python implementation of the Maven artifact versions comparator +_maven_version_comparator_ = import_module( + 'lib.wildfly.upgrade.dependencies.3rd_party.fabric8-analytics-version-comparator.f8a_version_comparator' +) +# GitHub raw form of the base URL to the specific Wildfly tag repository root +# Note: To get the final version of the URL '%s' format specifier needs to be +# expanded to a particular Wildfly tag / release! +_wildfly_github_raw_base_url_for_tag = ( + 'https://raw.githubusercontent.com/wildfly/wildfly/%s/' +) +# Base URL of the Wildfly's main pom.xml XML file from GitHub tag branch +_wildfly_github_tag_main_pom_base_url = ( + _wildfly_github_raw_base_url_for_tag + + 'pom.xml' +) +# Base URL of the Wildfly's Infinispan subsystem-template XML file from GitHub +# tag branch +_wildfly_github_tag_ispn_subtempl_base_url = ( + _wildfly_github_raw_base_url_for_tag + + 'clustering/infinispan/extension/src/main/resources/subsystem-templates/infinispan.xml' +) + +# POM namespace prefix definition for lxml +_pom_ns = "http://maven.apache.org/POM/4.0.0" + # Module loggers -_moduleLoggers = {} -# 'pom' namespace prefix definition for lxml -_pom_ns = "http://maven.apache.org/POM/4.0.0" -# Maven GAV (groupId:artifactId:version) related stuff -_gav_elements = ['groupId', 'artifactId', 'version'] -_gav_delimiter = ':' +_moduleLoggers = {} + +# Maven GAV (groupId:artifactId:version) related constants / data structures +_gav_delimiter = ':' +_gav_elements = ['groupId', 'artifactId', 'version'] # # Various base helper routines @@ -119,14 +153,18 @@ def _emptyNewLine(): """ print() -def _logErrorAndExitIf(errorMessage, condition): +def _logErrorAndExitIf(errorMessage, condition, logger = None): """ - Log particular error message and exit with error if specified condition was - met. + Log particular error message using either the default or custom logger + specified via the 'logger' parameter, and exit with error if the specified + condition was met. """ + # Use the default logger if a custom one wasn't requested + if logger is None: + logger = getModuleLogger() if condition: _emptyNewLine() - getModuleLogger().error(errorMessage) + logger.error(errorMessage) _emptyNewLine() sys.exit(1) @@ -143,7 +181,7 @@ def setupLogger(loggerName = 'upgrade-wildfly', loggerFormatter = '%(log_color)s stdOutLogHandler.setFormatter(loggerFormatter) logger = logging.getLogger(loggerName) logger.addHandler(stdOutLogHandler) - logger.setLevel(logging.INFO) + logger.setLevel(__loglevel__) return logger @@ -168,7 +206,7 @@ def getTaskLogger(taskLoggerName): """ Return custom logger handling (sub)tasks. """ - taskLogFormatter = '\n%(log_color)s[%(levelname)s] [%(name)s] Performing Task:\n\n\t%(message)s\n' + taskLogFormatter = '\n%(log_color)s[%(levelname)s] [%(name)s]\n\n\t%(message)s\n' return getLogger(loggerName = taskLoggerName, loggerFormatter = taskLogFormatter) def getStepLogger(): @@ -187,13 +225,35 @@ def getStepLogger(): def getElementsByXPath(xmlTree, xPath, nameSpace = { "pom" : "%s" % _pom_ns }): """ Given the XML tree return the list of elements matching the 'xPath' from - the XML 'nameSpace'. 'nameSpace' is optional argument. If not specified - defaults to the POM XML namespace. + the XML 'nameSpace'. - Returns empty list if no such element specified by 'xPath' is found. + 'nameSpace' is optional argument. If not specified defaults to the POM XML + namespace. + + Returns empty list if no such element matching the specified by 'xPath' is + found. """ return xmlTree.xpath(xPath, namespaces = nameSpace) +def getElementsByXPathFromRemoteXml(xmlUrl, xPath, nameSpace = { "pom" : "%s" % _pom_ns }, errorMessage = None, expectedElementsCount = None): + """ + Given the URL of the remote XML file as 'xmlUrl' return the list of + elements matching the 'xPath' from the XML 'nameSpace'. + + 'nameSpace' is optional argument. If not specified defaults to the POM XML + namespace. + + Returns empty list if no such element matching the specified by 'xPath' is + found. + """ + xmlFile = saveUrlToNamedTemporaryFile(xmlUrl) + xmlRoot = getXmlRoot(xmlFile) + xmlElements = getElementsByXPath(xmlRoot, xPath, nameSpace = nameSpace) + if None not in (errorMessage, expectedElementsCount) and len(xmlElements) != expectedElementsCount: + _logErrorAndExitIf(errorMessage, True) + + return xmlElements + def getPomDependencyByArtifactId(xmlTree, artifactIdText): """ Given the XML tree return list of POM dependency elements matching @@ -212,6 +272,31 @@ def getPomProperty(xmlTree, propertyText): """ return xmlTree.xpath('/pom:project/pom:properties/pom:%s' % propertyText, namespaces = { "pom" : "%s" % _pom_ns }) +def getPomPropertyFromRemoteXml(xmlUrl, propertyText, errorMessage = None, expectedElementsCount = None): + """ + Given the URL of the remote XML file as 'xmlUrl' and name of the property + to be retrieved from the XML file as 'propertyText' perform: + * Save the remote XML to local file, + * Obtain the XML root from the content, + * Locate the specified POM property element within the file. + + Moreover, if both 'errorMessage' and 'expectedElementsCount' are provided, + display the 'errorMessage' error message and exit with failure if the count + of found elements matching the 'propertyText' doesn't match the + 'expectedElemsCount' value. + + Returns list of matching property elements or exits with error if count + other than specified in 'expectedElementsCount' is found. + """ + + xmlFile = saveUrlToNamedTemporaryFile(xmlUrl) + xmlRoot = getXmlRoot(xmlFile) + propertyElement = getPomProperty(xmlRoot, propertyText) + if None not in (errorMessage, expectedElementsCount) and len(propertyElement) != expectedElementsCount: + _logErrorAndExitIf(errorMessage, True) + + return propertyElement + def getVersionOfPomDependency(xmlElem, groupIdText, artifactIdText): """ Given the list of XML POM dependency elements, return the value of @@ -257,6 +342,23 @@ def getXmlRoot(filename): # performed within a Wildfly upgrade # +def compareMavenVersions(updatedVersion, currentVersion): + """ + Compare 'updatedVersion' with 'currentVersion' using the generic Maven + version comparison method. + + Return a negative integer, zero, or a positive integer as the + 'updatedVersion' is less than, equal to, or greater than the + 'currentVersion'. + """ + comparableVersion = ( + _maven_version_comparator_ + .comparable_version + .ComparableVersion(updatedVersion) + ) + + return comparableVersion.compare_to(currentVersion) + def getProductNamesForKeycloakPomProfile(profile = 'community'): """ Return values of and elements @@ -418,7 +520,7 @@ def loadGavDictionaryFromXmlFile(xmlFile, xPathPrefix = '/pom:project/pom:depend # 'project.version' value. Create a custom XPath query to fetch the actual numeric value if not propertyElem: # Build xpath from version value, turn e.g. 'project.version' to '/pom:project/pom:version' - customXPath = ''.join(list(map(lambda x: '/pom:' + x, gavDictValue.split('.')))) + customXPath = _empty_string.join(list(map(lambda x: '/pom:' + x, gavDictValue.split('.')))) # Fetch the numeric version propertyElem = getElementsByXPath(xmlRoot, customXPath) # Exit with error if it wasn't possible to determine the artifact version even this way @@ -466,7 +568,7 @@ def mergeTwoGavDictionaries(firstGavDictionary, secondGavDictionary): # Update the artifact version in resulting GAV dictionary only if # the value from the second dictionary is higher than the current # one - if parseVersion(secondDictValue) > parseVersion(currentValue): + if compareMavenVersions(secondDictValue, currentValue) > 0: unitedGavDictionary[secondDictKey] = secondDictValue except KeyError: @@ -560,6 +662,7 @@ _keycloakSpecificProperties = [ "spring-boot15.version", "spring-boot21.version", "spring-boot22.version", + "spring-boot23.version", "webauthn4j.version", "org.apache.kerby.kerby-asn1.version", ] @@ -658,12 +761,12 @@ _keycloakToWildflyProperties = { # Skip "jmeter.analysis.plugin.version" since Keycloak specific # Skip "minify.plugin.version" since Keycloak specific # Skip "osgi.bundle.plugin.version" since Keycloak specific - "wildfly.plugin.version" : "version.org.wildfly.maven.plugins", + "wildfly.plugin.version" : "version.org.wildfly.plugin", # Skip "nexus.staging.plugin.version" since Keycloak specific # Skip "frontend.plugin.version" since Keycloak specific # Skip "docker.maven.plugin.version" since Keycloak specific # Skip "tomcat7.version", "tomcat8.version", and "tomcat9.version" since Keycloak specific - # Skip "spring-boot15.version", "spring-boot21.version", and "spring-boot22.version" since Keycloak specific + # Skip "spring-boot15.version", "spring-boot21.version", "spring-boot22.version", and "spring-boot23.version" since Keycloak specific # Skip "webauthn4j.version" since Keycloak specific # Skip "org.apache.kerby.kerby-asn1.version" since Keycloak specific } @@ -725,7 +828,7 @@ _wildflyCoreProperties = [ "junit.version", ] -def updateMainKeycloakPomFile(wildflyPomFile, wildflyCorePomFile): +def performMainKeycloakPomFileUpdateTask(wildflyPomFile, wildflyCorePomFile, forceUpdates = False): """ Synchronize the versions of artifacts listed as properties in the main Keycloak pom.xml file with their counterparts taken from 'wildflyPomFile' @@ -764,7 +867,10 @@ def updateMainKeycloakPomFile(wildflyPomFile, wildflyCorePomFile): "Not updating version of '%s' from '%s' to '%s' since the artifact is excluded!" % (keycloakElemName, keycloakElem[0].text, wildflyElem[0].text) ) - elif parseVersion(wildflyElem[0].text) > parseVersion(keycloakElem[0].text): + elif ( + forceUpdates or + compareMavenVersions(wildflyElem[0].text, keycloakElem[0].text) > 0 + ): stepLogger.debug( "Updating version of '%s' artifact to '%s'. Current '%s' version is less than that." % (keycloakElemName, wildflyElem[0].text, keycloakElem[0].text) @@ -782,7 +888,7 @@ def updateMainKeycloakPomFile(wildflyPomFile, wildflyCorePomFile): ) lxml.etree.ElementTree(keycloakXmlTreeRoot).write(mainKeycloakPomPath, encoding = "UTF-8", pretty_print = True, xml_declaration = True) - stepLogger.info("Done syncing artifact version changes to: '%s'" % mainKeycloakPomPath.replace(getKeycloakGitRepositoryRoot(), '.')) + stepLogger.info("Done syncing artifact version changes to: '%s'!" % mainKeycloakPomPath.replace(getKeycloakGitRepositoryRoot(), '.')) stepLogger.debug("Wrote updated main Keycloak pom.xml file to: '%s'" % mainKeycloakPomPath) # @@ -790,7 +896,7 @@ def updateMainKeycloakPomFile(wildflyPomFile, wildflyCorePomFile): # adapter license files related with a Wildfly upgrade # -def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile): +def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile, forceLicenseFileUpdates = False): """ Save GAV dictionary 'gavDictionary' back to XML 'licenseFile'. """ @@ -812,7 +918,8 @@ def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile) groupIdElem, artifactIdElem, versionElem = gavEntry[0], gavEntry[1], gavEntry[2] _logErrorAndExitIf( "Failed to update '%s' XML dependency!" % gavEntry, - groupIdElem is None or artifactIdElem is None or versionElem is None + groupIdElem is None or artifactIdElem is None or versionElem is None, + logger = stepLogger ) currentArtifactVersion = versionElem.text gavDictKey = groupIdElem.text + _gav_delimiter + artifactIdElem.text @@ -820,11 +927,17 @@ def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile) # Value of the artifact version might be a child dictionary again. # Get numeric artifact version first expectedArtifactVersion = getNumericArtifactVersion(gavDictionary, gavDictKey) + # Update the version of artifact if version from GAV dictionary is higher - if expectedArtifactVersion and parseVersion(expectedArtifactVersion) > parseVersion(versionElem.text): + if ( + expectedArtifactVersion != currentArtifactVersion and + forceLicenseFileUpdates or + compareMavenVersions(expectedArtifactVersion, versionElem.text) > 0 + ): + updatingArtifactVersionMessage = ( - "Updating the version of '%s, %s' artifact in license file from: '%s' to: '%s'" % - (groupIdElem.text, artifactIdElem.text, currentArtifactVersion, expectedArtifactVersion) + "Updating the version of '%s, %s' artifact in the '%s' license file from: '%s' to: '%s'" % + (groupIdElem.text, artifactIdElem.text, licenseFile, currentArtifactVersion, expectedArtifactVersion) ) stepLogger.debug(updatingArtifactVersionMessage) versionElem.text = expectedArtifactVersion @@ -836,13 +949,17 @@ def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile) currentFilename = filename currentFileName = currentFilename.replace(repositoryRoot, '').rstrip() newFilename = currentFilename.replace(currentArtifactVersion, expectedArtifactVersion) + # Delete & recreate the TXT file if it previously existed (be idempotent) + if os.path.isfile(os.path.join(root, newFilename)): + os.remove(os.path.join(root, newFilename)) check_call(['git', 'mv', "%s" % os.path.join(root, currentFilename), "%s" % os.path.join(root, newFilename)], cwd = repositoryRoot) # Subtask: Update artifact version in license URL to the expected one dependencyElem = groupIdElem.getparent() urlElements = getElementsByXPath(dependencyElem, './licenses/license/url', nameSpace) _logErrorAndExitIf( "Failed to retrieve element of the '%s' artifact!" % gavDictKey, - len(urlElements) != 1 + len(urlElements) != 1, + logger = stepLogger ) urlElem = urlElements[0] # Strip the '.redhat-\d+' suffix from artifact versions when processing RH-SSO adapters @@ -870,7 +987,8 @@ def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile) else: _logErrorAndExitIf( "Unable to locate previous '%s' artifact version in the URL!" % gavDictKey, - True + True, + logger = stepLogger ) else: try: @@ -885,7 +1003,8 @@ def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile) else: _logErrorAndExitIf( "Unable to locate previous '%s' artifact version in the URL!" % gavDictKey, - True + True, + logger = stepLogger ) except AttributeError: # Ignore generic URLs not containing 'major.minor.micro' information of this specific artifact @@ -904,14 +1023,14 @@ def updateAdapterLicenseFile(gavDictionary, xPathPrefix, nameSpace, licenseFile) lxml.etree.ElementTree(licenseFileXmlTreeRoot).write(licenseFile, encoding = "UTF-8", pretty_print = True, xml_declaration = True) relativeLicenseFilePath = licenseFile.replace(getKeycloakGitRepositoryRoot(), '.') - stepLogger.info("Done syncing artifact version changes to: '%s'" % relativeLicenseFilePath) + stepLogger.info("Done syncing artifact version changes to: '%s'!" % relativeLicenseFilePath) stepLogger.debug("Wrote updated license file to: '%s'" % licenseFile) # # Routines performing particular tasks within a Wildfly upgrade # -def performKeycloakAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile): +def performKeycloakAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile, forceUpdates = False): """ Update artifacts versions of selected dependencies utilized by various Keycloak adapter license XML files. Also update the location of the @@ -944,15 +1063,16 @@ def performKeycloakAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomF taskLogger.info(taskLabel) isTaskLogged = True for filename in files: - if re.search(r'distribution.*%s.*licenses.xml' % productName.lower(), os.path.join(root, filename)): + if re.search(r'distribution.*/src/main/resources/licenses/%s/licenses.xml' % productName.lower(), os.path.join(root, filename)): updateAdapterLicenseFile( unitedGavDictionary, xPathPrefix = '/licenseSummary/dependencies/dependency', nameSpace = {}, - licenseFile = os.path.join(root, filename) + licenseFile = os.path.join(root, filename), + forceLicenseFileUpdates = forceUpdates ) -def performRhssoAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile): +def performRhssoAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile, forceUpdates = False): """ Update artifacts versions of selected dependencies utilized by various RH-SSO adapter license XML files. Also update the location of the @@ -988,10 +1108,270 @@ def performRhssoAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile taskLogger.info(taskLabel) isTaskLogged = True for filename in files: - if re.search(r'distribution.*%s.*licenses.xml' % productName.lower(), os.path.join(root, filename)): + if re.search(r'distribution.*/src/main/resources/licenses/%s/licenses.xml' % productName.lower(), os.path.join(root, filename)): updateAdapterLicenseFile( gavDictionary, xPathPrefix = '/licenseSummary/dependencies/dependency', nameSpace = {}, - licenseFile = os.path.join(root, filename) + licenseFile = os.path.join(root, filename), + forceLicenseFileUpdates = forceUpdates ) + +def performDeprecatedWildflyTestingModuleUpdateTask(forceUpdates = False): + """ + Update the properties of the deprecated Wildfly testing module present in + the Arquillian testsuite if necessary. The properties are needed to be + updated if and only if the Wildfly and Wildfly Core versions in the main + Keycloak pom.xml file from the local Keycloak git clone are higher than + Widfly and Wildfly Core versions in the main Keycloak pom.xml file of the + master branch of official Keycloak GitHub repository (IOW if and only the + main Keycloak pom.xml file in the local repository got already updated with + new Wildfly and Wildfly Core artifact versions from the new tag) + """ + # Prepare / hold the expected future values of the properties of the + # deprecated Wildfly testing module present in Arquillian testsuite + deprecatedWildflyModuleProperties = {} + + deprecatedWildflyModuleProperties['wildfly.deprecated.version'] = getPomPropertyFromRemoteXml( + _keycloak_github_master_main_pom_base_url, + 'wildfly.version', + errorMessage = "Unable to locate 'wildfly.version' property element in the remote XML file!", + expectedElementsCount = 1 + )[0].text + + deprecatedWildflyModuleProperties['wildfly.deprecated.wildfly.core.version'] = getPomPropertyFromRemoteXml( + _keycloak_github_master_main_pom_base_url, + 'wildfly.core.version', + errorMessage = "Unable to locate 'wildfly.core.version' property element in the remote XML file!", + expectedElementsCount = 1 + )[0].text + + deprecatedWildflyModuleProperties['wildfly.deprecated.arquillian.wildfly.container'] = getPomPropertyFromRemoteXml( + _wildfly_github_tag_main_pom_base_url % deprecatedWildflyModuleProperties['wildfly.deprecated.version'], + 'version.org.wildfly.arquillian', + errorMessage = "Unable to locate 'version.org.wildfly.arquillian' property element in the remote XML file!", + expectedElementsCount = 1 + )[0].text + + taskLogger = getTaskLogger('Update Deprecated Wildfly Testing Module') + taskLogger.info('Updating properties of the deprecated Wildfly testing module...') + stepLogger = getStepLogger() + + # Absolute path to main Keycloak pom.xml within the local repo + mainKeycloakPomPath = getKeycloakGitRepositoryRoot() + "/pom.xml" + mainKeycloakPomXmlRoot = getXmlRoot(mainKeycloakPomPath) + # Absolute path to pom.xml file of the Arquillian testsuite within the local repo + arqTestSuitePomPath = getKeycloakGitRepositoryRoot() + "/testsuite/integration-arquillian/pom.xml" + arqTestSuitePomXmlRoot = getXmlRoot(arqTestSuitePomPath) + + # Determine the current value of the 'wildfly.version' property element + # from the main pom.xml file of the local Keycloak git repository clone + currentLocalWildflyVersionElem = getPomProperty(mainKeycloakPomXmlRoot, 'wildfly.version') + _logErrorAndExitIf( + "Unable to determine the value of the 'wildfly.version' property element in the main Keycloak pom.xml file!", + len(currentLocalWildflyVersionElem) != 1, + logger = stepLogger + ) + currentLocalWildflyVersion = currentLocalWildflyVersionElem[0].text + # Determine the current value of the 'wildfly.core.version' property + # element from the main pom.xml file of the local Keycloak git repository + # clone + currentLocalWildflyCoreVersionElem = getPomProperty(mainKeycloakPomXmlRoot, 'wildfly.core.version') + _logErrorAndExitIf( + "Unable to determine the value of the 'wildfly.core.version' property element in the main Keycloak pom.xml file!", + len(currentLocalWildflyCoreVersionElem) != 1, + logger = stepLogger + ) + currentLocalWildflyCoreVersion = currentLocalWildflyCoreVersionElem[0].text + # Update the properties of the deprecated Wildfly testing module present + # in the Arquillian testsuite if the local Wildfly and Wildfly Core version + # is higher than their counterparts currently present in the master branch + # of the Keycloak GitHub repository (IOW only if main Keycloak's pom.xml + # got previously already updated with artifact versions from the new + # Wildfly tag) + if ( + forceUpdates or + compareMavenVersions(currentLocalWildflyVersion, deprecatedWildflyModuleProperties['wildfly.deprecated.version']) > 0 and + compareMavenVersions(currentLocalWildflyCoreVersion, deprecatedWildflyModuleProperties['wildfly.deprecated.wildfly.core.version']) > 0 + ): + + for deprecatedProperty in deprecatedWildflyModuleProperties.keys(): + arqTestSuitePropertyElem = getPomProperty(arqTestSuitePomXmlRoot, deprecatedProperty) + _logErrorAndExitIf( + "Unable to locate the '%s' element in the pom.xml file of the Arquillian testsuite!", + len(arqTestSuitePropertyElem) != 1, + logger = stepLogger + ) + stepLogger.debug( + "Updating value of the '%s' property of the deprecated Wildfly module to '%s'" % + (deprecatedProperty, deprecatedWildflyModuleProperties[deprecatedProperty]) + ) + arqTestSuitePropertyElem[0].text = deprecatedWildflyModuleProperties[deprecatedProperty] + else: + updateNotNecessaryMessage = ( + "Not updating the values of the properties of the deprecated Wildfly testing module!" + "\n\t\t Versions of Wildfly and Wildfly Core artifacts found in the main pom.xml file" + "\n\t\t of this repository are equal, or lower when compared to their respective versions" + "\n\t\t currently present in the 'master' branch of the upstream GitHub Keycloak repository." + "\n\t\t Update is not needed." + ) + stepLogger.debug(_empty_string.join(updateNotNecessaryMessage)) + + lxml.etree.ElementTree(arqTestSuitePomXmlRoot).write(arqTestSuitePomPath, encoding = "UTF-8", pretty_print = True, xml_declaration = True) + stepLogger.info('Done syncing necessary changes to the deprecated Wildfly testing module!') + +def performJbossParentVersionUpdateTask(wildflyTag, wildflyPomFile, wildflyCorePomFile, forceUpdates = False): + taskLogger = getTaskLogger('Update Version of jboss-parent') + taskLogger.info("Checking if the 'jboss-parent' version needs to be updated...") + stepLogger = getStepLogger() + + # Absolute path to main Keycloak pom.xml within the local repo + mainKeycloakPomPath = getKeycloakGitRepositoryRoot() + "/pom.xml" + mainKeycloakPomXmlRoot = getXmlRoot(mainKeycloakPomPath) + # Absolute path to the Keycloak's 'boms/pom.xml' location within the local repo + keycloakBomsPomPath = getKeycloakGitRepositoryRoot() + "/boms/pom.xml" + keycloakBomsPomXmlRoot = getXmlRoot(keycloakBomsPomPath) + + # Retrieve the current versions of the 'jboss-parent' from the: + jbossParentVersionElems = {} + # Main pom.xml file of the Wildfly repository + jbossParentVersionElems['from.main.wildfly.pom'] = getElementsByXPath(getXmlRoot(wildflyPomFile), '/pom:project/pom:parent/pom:version') + # Main pom.xml file of the Wildfly Core repository + jbossParentVersionElems['from.main.wildfly.core.pom'] = getElementsByXPath(getXmlRoot(wildflyCorePomFile), '/pom:project/pom:parent/pom:version') + # Main pom.xml file of the local Keycloak repository + jbossParentVersionElems['from.main.keycloak.pom'] = getElementsByXPath(mainKeycloakPomXmlRoot, '/pom:project/pom:parent/pom:version') + # The boms/pom.xml file of the local Keycloak repository + jbossParentVersionElems['from.keycloak.boms.pom'] = getElementsByXPath(keycloakBomsPomXmlRoot, '/pom:project/pom:parent/pom:version') + + # Sanity check if jboss-parent elements were retrieved correctly from all of the four files + # (in each case there should be exactly one 'jboss-parent' element present in the pom.xml) + for key, value in jbossParentVersionElems.items(): + location = ( + key + .replace('boms.pom', "'boms/pom'") + .replace('keycloak', 'Keycloak') + .replace('wildfly', 'Wildfly') + .replace('core', 'Core') + .replace('from', 'from the') + .replace('pom', 'pom.xml') + .replace('.', ' ') + ) + + _logErrorAndExitIf( + "Unable to determine the version of the 'jboss-parent' element %s file!" % location, + len(value) != 1, + logger = stepLogger + ) + # Turn list containing one XML element into just the XML element itself + jbossParentVersionElems[key] = value[0] + + # Synchronize the jboss-parent version in both the main Keycloak pom.xml file and in the 'boms/pom.xml' file + # (if their version differs from the current versions used by Wildfly / Wildfly Core) + if ( + forceUpdates or + jbossParentVersionElems['from.main.wildfly.pom'].text == jbossParentVersionElems['from.main.wildfly.core.pom'].text and + jbossParentVersionElems['from.main.wildfly.pom'].text != jbossParentVersionElems['from.main.keycloak.pom'].text or + jbossParentVersionElems['from.main.wildfly.pom'].text != jbossParentVersionElems['from.keycloak.boms.pom'].text + ): + + updatedJBossParentVersion = jbossParentVersionElems['from.main.wildfly.pom'].text + stepLogger.info("Updating version of 'jboss-parent' in the main Keycloak pom.xml file...") + jbossParentVersionElems['from.main.keycloak.pom'].text = updatedJBossParentVersion + lxml.etree.ElementTree(mainKeycloakPomXmlRoot).write(mainKeycloakPomPath, encoding = "UTF-8", pretty_print = True, xml_declaration = True) + stepLogger.info("'jboss-parent' version updated to '%s' to match the version used by Wildfly '%s'!" % (updatedJBossParentVersion, wildflyTag)) + + stepLogger.info("Updating version of 'jboss-parent' in the Keycloak's 'boms/pom.xml' file...") + jbossParentVersionElems['from.keycloak.boms.pom'].text = updatedJBossParentVersion + lxml.etree.ElementTree(keycloakBomsPomXmlRoot).write(keycloakBomsPomPath, encoding = "UTF-8", pretty_print = True, xml_declaration = True) + stepLogger.info("'jboss-parent' version updated to '%s' to match the version used by Wildfly '%s'!" % (updatedJBossParentVersion, wildflyTag)) + + # No update necessary ('jboss-parent' versions are already equal) + else: + jbossParentVersionUpdateNotNecessaryMsg = ( + "Update of the 'jboss-parent' version is not necessary!", + "\n\t\tCurrent '%s' version used by Keycloak already matches the current '%s' version used by Wildfly '%s'." % + (jbossParentVersionElems['from.main.keycloak.pom'].text, jbossParentVersionElems['from.main.wildfly.pom'].text, wildflyTag) + ) + stepLogger.info(_empty_string.join(jbossParentVersionUpdateNotNecessaryMsg)) + +def synchronizeInfinispanSubsystemXmlNamespaceWithWildfly(wildflyTag): + """ + Update the XML namespace of the 'subsystem' element of the Keycloak + Infinispan subsystem template with its current value as used by Wildfly. + """ + taskLogger = getTaskLogger("Update 'urn:jboss:domain:infinispan:*' version") + taskLogger.info('Synchronizing XML namespace of Infinispan subsystem from Wildfly to Keycloak...') + stepLogger = getStepLogger() + + wildflyInfinispanSubsystemElementErrorMessage = ( + "Unable to locate 'subsystem' XML element in the remote XML file!" + ) + # Retrieve 'subsystem' element from the Wildfly's Infinispan + # subsystem-template XML file + wildflyInfinispanSubsystemElement = list(filter( + lambda elem: 'subsystem' in elem.tag, + getElementsByXPathFromRemoteXml( + _wildfly_github_tag_ispn_subtempl_base_url % wildflyTag, + xPath = '/config/*', + nameSpace = {} + ) + )) + # Sanity check + _logErrorAndExitIf( + wildflyInfinispanSubsystemElementErrorMessage, + len(wildflyInfinispanSubsystemElement) != 1 + ) + # Retrieve namespace value of that 'subsystem' element + wildflyInfinispanSubsystemElementNamespace = ( + lxml.etree.QName(wildflyInfinispanSubsystemElement[0]).namespace + ) + # Absolute path to the Infinispan subsystem template XML file + # within local Keycloak git repository + keycloakInfinispanSubsystemTemplateXmlPath = ( + getKeycloakGitRepositoryRoot() + + '/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml' + ) + # XML root of the Keycloak Infinispan subsystem template XML file + keycloakInfinispanSubsystemTemplateXmlRoot = getXmlRoot( + keycloakInfinispanSubsystemTemplateXmlPath + ) + keycloakInfinispanSubsystemElementErrorMessage = ( + "Unable to locate 'subsystem' XML element in the local XML file!", + ) + # Retrieve 'subsystem' element from the Keycloak's Infinispan + # subsystem-template XML file + keycloakInfinispanSubsystemElement = list(filter( + lambda elem: 'subsystem' in elem.tag, + getElementsByXPath( + keycloakInfinispanSubsystemTemplateXmlRoot, + xPath = '/config/*', + nameSpace = {} + ) + )) + # Sanity check + _logErrorAndExitIf( + keycloakInfinispanSubsystemElementErrorMessage, + len(keycloakInfinispanSubsystemElement) != 1 + ) + # Update namespace of Keycloak's Infinispan 'subsystem' element in + # subsystem-template XML file to match namespace value from Wildfly + keycloakInfinispanSubsystemElementParent = keycloakInfinispanSubsystemElement[0].getparent() + keycloakInfinispanSubsystemElementIndex = keycloakInfinispanSubsystemElementParent.index( + keycloakInfinispanSubsystemElement[0] + ) + keycloakInfinispanSubsystemElementParent.remove(keycloakInfinispanSubsystemElement[0]) + keycloakInfinispanSubsystemElementParent.insert( + keycloakInfinispanSubsystemElementIndex, + wildflyInfinispanSubsystemElement[0] + ) + # Write the changes back to Keycloak Infinispan subsystem-template XML file + lxml.etree.ElementTree(keycloakInfinispanSubsystemTemplateXmlRoot).write( + keycloakInfinispanSubsystemTemplateXmlPath, + encoding = "UTF-8", + pretty_print = True, + xml_declaration = True + ) + stepLogger.info( + "Updated XML namespace of the Keycloak's Infinispan subsystem to '%s'" % + lxml.etree.QName(wildflyInfinispanSubsystemElement[0]).namespace + ) diff --git a/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/README.md b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/README.md new file mode 100644 index 0000000000..316683231d --- /dev/null +++ b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/README.md @@ -0,0 +1,10 @@ +# fabric8-analytics-version-comparator/f8a\_version\_comparator + +Python module/library implementing generic Maven version comparison: + + https://github.com/apache/maven/blob/master/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java + +The "f8a\_version\_comparator" Python module implementation was taken +from "fabric8-analytics/fabric8-analytics-version-comparator" repository: + + https://github.com/fabric8-analytics/fabric8-analytics-version-comparator diff --git a/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/__init__.py b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/__init__.py new file mode 100644 index 0000000000..99a9b0e902 --- /dev/null +++ b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/__init__.py @@ -0,0 +1,28 @@ +# Copyright © 2018 Red Hat Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Geetika Batra +# + +"""Initialize Module.""" + +__all__ = [ + "base", + "comparable_version", + "item_object", +] + +from . import base +from . import comparable_version +from . import item_object diff --git a/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/base.py b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/base.py new file mode 100644 index 0000000000..cd92b23216 --- /dev/null +++ b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/base.py @@ -0,0 +1,28 @@ +# Copyright © 2018 Red Hat Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Geetika Batra +# + +"""Item class acting as base class for various item types.""" + +from abc import ABCMeta, abstractmethod + + +class Item(metaclass=ABCMeta): + """Base class for maven version comparator tasks.""" + + @abstractmethod + def compare_to(self, _item): + """Compare two maven versions.""" diff --git a/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/comparable_version.py b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/comparable_version.py new file mode 100644 index 0000000000..79489e2465 --- /dev/null +++ b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/comparable_version.py @@ -0,0 +1,213 @@ +# Copyright © 2018 Red Hat Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Geetika Batra +# + +"""Module to implement Comparable Version class.""" + +import typing + +from .item_object import IntegerItem +from .item_object import StringItem +from .item_object import ListItem + + +class ComparableVersion: + """Class for Comparable Version.""" + + def __init__(self, version: str): + """Initialize comparable version class. + + :version: Version supplied as a string + """ + if not isinstance(version, str): + raise TypeError( + "Invalid type {got!r} of argument `version`, expected {expected!r}".format( + got=type(version), + expected=str + )) + + self.version = version + self.items = self.parse_version() + + def __repr__(self): + """Return representation of ComparableVersion object.""" + return "{cls!s}(version={version!r})".format( + cls=self.__class__.__name__, + version=self.version + ) + + def __str__(self): + """Return version string held by ComparableVersion object.""" + return "{version!s}".format( + version=self.version + ) + + def __eq__(self, other): + """Compare ComparableVersion objects for equality. + + This rich comparison implies whether self == other + """ + # don't call compare_to(None) + if other is None: + return False + + return self.compare_to(other) == 0 + + def __ne__(self, other): + """Compare ComparableVersion objects for equality. + + This rich comparison implies whether self != other + """ + # don't call compare_to(None) + if other is None: + return True + + return self.compare_to(other) != 0 + + def __lt__(self, other): + """Compare ComparableVersion objects. + + This rich comparison implies whether self < other + """ + # don't call compare_to(None) + if other is None: + return False + + return self.compare_to(other) == -1 + + def __le__(self, other): + """Compare ComparableVersion objects. + + This rich comparison implies whether self <= other + """ + # don't call compare_to(None) + if other is None: + return False + + return self.compare_to(other) <= 0 + + def __gt__(self, other): + """Compare ComparableVersion objects. + + This rich comparison implies whether self > other + """ + # don't call compare_to(None) + if other is None: + return True + + return self.compare_to(other) == 1 + + def __ge__(self, other): + """Compare ComparableVersion objects. + + This rich comparison implies whether self >= other + """ + # don't call compare_to(None) + if other is None: + return True + + return self.compare_to(other) >= 0 + + def parse_version(self): + """Parse version.""" + # TODO: reduce cyclomatic complexity + ref_list = ListItem() + items = ref_list + parse_stack = list() + version = self.version.lower() + parse_stack.append(ref_list) + _is_digit = False + + _start_index = 0 + + for _ch in range(0, len(version)): + + ver_char = version[_ch] + + if ver_char == ".": + + if _ch == _start_index: + ref_list.add_item(IntegerItem(0)) + else: + ref_list.add_item(self.parse_item(_is_digit, version[_start_index: _ch])) + + _start_index = _ch + 1 + + elif ver_char == "-": + if _ch == _start_index: + ref_list.add_item(IntegerItem(0)) + else: + ref_list.add_item(self.parse_item(_is_digit, version[_start_index: _ch])) + _start_index = _ch + 1 + + temp = ListItem() + ref_list.add_item(temp) + ref_list = temp + parse_stack.append(ref_list) + elif ver_char.isdigit(): + if not _is_digit and _ch > _start_index: + ref_list.add_item(StringItem(version[_start_index: _ch], True)) + _start_index = _ch + + temp = ListItem() + ref_list.add_item(temp) + ref_list = temp + parse_stack.append(ref_list) + _is_digit = True + else: + if _is_digit and _ch > _start_index: + ref_list.add_item(self.parse_item(True, version[_start_index:_ch])) + _start_index = _ch + temp = ListItem() + ref_list.add_item(temp) + ref_list = temp + parse_stack.append(ref_list) + _is_digit = False + + if len(version) > _start_index: + ref_list.add_item(self.parse_item(_is_digit, version[_start_index:])) + + while parse_stack: + ref_list = parse_stack.pop() + ref_list.normalize() + + return items + + @staticmethod + def parse_item(_is_digit, buf): + """Wrap items in version in respective object class.""" + # TODO: make this function static (it does not need 'self') + if _is_digit: + return IntegerItem(buf) + + return StringItem(buf, False) + + def compare_to(self, obj: typing.Union["ComparableVersion", str]): + """Compare two ComparableVersion objects.""" + if isinstance(obj, ComparableVersion): + # compare two objects of the same type + cmp_result = self.items.compare_to(obj.items) + elif isinstance(obj, str): + # compare against string + cmp_result = self.items.compare_to(ComparableVersion(obj).items) + else: + raise TypeError( + "Invalid type {got!r} of argument `obj`, expected <{expected}>".format( + got=type(obj), + expected=typing.Union["ComparableVersion", str] + )) + + return cmp_result diff --git a/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/item_object.py b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/item_object.py new file mode 100644 index 0000000000..14a9e18ad1 --- /dev/null +++ b/misc/scripts/upgrade-wildfly/lib/wildfly/upgrade/dependencies/3rd_party/fabric8-analytics-version-comparator/f8a_version_comparator/item_object.py @@ -0,0 +1,204 @@ +# Copyright © 2018 Red Hat Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Geetika Batra +# + +"""Module to implement methods item types.""" + +from .base import Item +# TODO: setup logging + + +class IntegerItem(Item): + """Integer Item class for maven version comparator tasks.""" + + def __init__(self, str_version): + """Initialize integer from string value of version. + + :str_version: part of version supplied as string + """ + self.value = int(str_version) + + def int_cmp(self, cmp_value): + """Compare two integers.""" + if self.value.__lt__(cmp_value): + return -1 + if self.value.__gt__(cmp_value): + return 1 + return 0 + + def compare_to(self, item): + """Compare two maven versions.""" + if item is None: + return 0 if self.value == 0 else 1 + + if isinstance(item, IntegerItem): + return self.int_cmp(item.value) # check if this value thing works + if isinstance(item, StringItem): + return 1 + if isinstance(item, ListItem): + return 1 + else: + raise ValueError("invalid item" + str(type(item))) + + def to_string(self): + """Return string value of version.""" + return str(self.value) + + def __str__(self): + """Return string value of version - Pythonish variant.""" + return str(self.value) + + +class StringItem(Item): + """String Item class for maven version comparator tasks.""" + + def __init__(self, str_version, followed_by_digit): + """Initialize string value of version. + + :str_value: part of version supplied as string + :followed_by_digit: True if str_version is followed by digit + """ + self.qualifiers = ["alpha", "beta", "milestone", "rc", "snapshot", "", "sp"] + + self.aliases = { + "ga": "", + "final": "", + "cr": "rc" + } + + self.release_version_index = str(self.qualifiers.index("")) + self._decode_char_versions(str_version, followed_by_digit) + + def _decode_char_versions(self, value, followed_by_digit): + """Decode short forms of versions.""" + if followed_by_digit and len(value) == 1: + if value.startswith("a"): + value = "alpha" + elif value.startswith("b"): + value = "beta" + elif value.startswith("m"): + value = "milestone" + + self.value = self.aliases.get(value, value) + + def comparable_qualifier(self, qualifier): + """Get qualifier that is comparable.""" + q_index = None + if qualifier in self.qualifiers: + q_index = self.qualifiers.index(qualifier) + q_index_not_found = str(len(self.qualifiers)) + "-" + qualifier + + return str(q_index) if q_index is not None else q_index_not_found + + def str_cmp(self, val1, val2): + """Compare two strings.""" + if val1.__lt__(val2): + return -1 + if val1.__gt__(val2): + return 1 + return 0 + + def compare_to(self, item): + """Compare two maven versions.""" + if item is None: + temp = self.str_cmp(self.comparable_qualifier(self.value), self.release_version_index) + return temp + if isinstance(item, IntegerItem): + return -1 + if isinstance(item, StringItem): + return self.str_cmp( + self.comparable_qualifier( + self.value), self.comparable_qualifier( + item.value)) + if isinstance(item, ListItem): + return -1 + else: + raise ValueError("invalid item" + str(type(item))) + + def to_string(self): + """Return value in string form.""" + return str(self.value) + + def __str__(self): + """Return string value of version - Pythonish variant.""" + return str(self.value) + + +class ListItem(Item): + """List Item class for maven version comparator tasks.""" + + def __init__(self): + """Initialize string value of version.""" + self.array_list = list() + + def add_item(self, item): + """Add item to array list.""" + self.array_list.append(item) + + def get_list(self): + """Get object list items.""" + return self.array_list + + def normalize(self): + """Remove trailing items: 0, "", empty list.""" + red_list = [0, None, ""] + i = len(self.array_list) - 1 + while i >= 0: + last_item = self.array_list[i] + + if not isinstance(last_item, ListItem): + + if last_item.value in red_list: + self.array_list.pop(i) + else: + break + + i = i - 1 + + def compare_to(self, item): + """Compare two maven versions.""" + # TODO: reduce cyclomatic complexity + if item is None: + if len(self.array_list) == 0: + return 0 + first = self.array_list[0] + return first.compare_to(None) + + if isinstance(item, IntegerItem): + return -1 + if isinstance(item, StringItem): + return 1 + if isinstance(item, ListItem): + left_iter = iter(self.array_list) + right_iter = iter(item.get_list()) + + while True: + l_obj = next(left_iter, None) + r_obj = next(right_iter, None) + if l_obj is None and r_obj is None: + break + result = 0 + if l_obj is None: + if r_obj is not None: + result = -1 * r_obj.compare_to(l_obj) + else: + result = l_obj.compare_to(r_obj) + if result != 0: + return result + + return 0 + else: + raise ValueError("invalid item" + str(type(item))) diff --git a/misc/scripts/upgrade-wildfly/upgrade-keycloak-to-wildfly-tag.py b/misc/scripts/upgrade-wildfly/upgrade-keycloak-to-wildfly-tag.py index 94fcfaed95..cb45ab8da7 100755 --- a/misc/scripts/upgrade-wildfly/upgrade-keycloak-to-wildfly-tag.py +++ b/misc/scripts/upgrade-wildfly/upgrade-keycloak-to-wildfly-tag.py @@ -17,52 +17,121 @@ # * limitations under the License. # * # * -# -# Purpose: Update various necessary bits of Keycloak to align with the specified Wildfly tag. Perform this by: -# -# * Incrementing the jboss-parent element version if necessary, -# * Updating versions of artifacts shared with Wildfly and Wildfly Core in main Keycloak pom.xml file, -# * Updating versions of artifacts shared with Wildfly and Wildfly Core utilized by Keycloak adapters -# -# Usage: Run as, e.g.: -# ./upgrade-keycloak-to-wildfly-tag.py 20.0.0.Final -# -# Or call the script without arguments to get the further help -import os, sys +import click, logging, os, sys -import wildfly.upgrade as wu +import lib.wildfly.upgrade as wu -def usage(): - print("Run as: \n\t%s Wildfly.Tag.To.Upgrade.To \ne.g.:\n\t%s 20.0.0.Final\n" % (sys.argv[0], sys.argv[0])) +CONTEXT_SETTINGS = dict(help_option_names = ['-h', '--help']) +FORCE_OPTION_HELP = """ + Force elements / files updates. -if __name__ == '__main__': + In common mode of operation (without the "-f" or "--force" options) the + script upgrades the version of the Keycloak characteristic in question + (POM property, dependency, or some other XML element shared with Wildfly + application server) ONLY if the new version is HIGHER than the version of + the corresponding element currently present in the local copy of the + Keycloak repository, the script is operating on. - if len(sys.argv) != 2: - usage() - sys.exit(1) + The -f, --force options instruct the script to allow an upgrade to replace + newer version of a particular Keycloak characteristic with an older one. + Useful to perform e.g. Keycloak downgrades to previous Wildfly versions. +""" - wildflyTag = wu.isWellFormedWildflyTag(sys.argv[1]) +RHSSO_ADAPTERS_OPTION_HELP = """ + Update artifacts versions of selected dependencies utilized by various + RH-SSO adapter license XML files. Also update the location of the + corresponding license text files within the repository so their names + reflect the updated artifacts versions. +""" + +@click.command(context_settings=CONTEXT_SETTINGS) +@click.argument('tag', required = True, type=click.STRING) +@click.option('-f', '--force', help=FORCE_OPTION_HELP, is_flag=True) +@click.option('-r', '--update-rh-sso-adapters', help=RHSSO_ADAPTERS_OPTION_HELP, is_flag=True) +@click.option('-v', '--verbose', help='Enable verbose output.', is_flag=True) +@click.version_option(prog_name=sys.argv[0], version=wu.__version__) +def processParameters(tag, verbose, force, update_rh_sso_adapters): + """ + NAME + + upgrade-keycloak-to-wildfly-tag.py - Rebase Keycloak on top of the + specified Wildfly tag (release) + + DESCRIPTION + + Update the versions of various Keycloak characteristics (versions of + POM properties, adapter dependencies, and other attributes actually + binding the Keycloak POM build configuration to the particular Wildfly + tag) to their corresponding values as used by the Wildfly application + server of version matching the tag / release, passed to the script as + argument. + + EXAMPLES + + Upgrade Keycloak to Wildfly 20 (using "20.0.1.Final" Wildfly tag): + + $ python upgrade-keycloak-to-wildfly-tag.py 20.0.1.Final + + Downgrade Keycloak to Wildfly 16 (using "16.0.0.Final" Wildfly tag, + script verbose mode to display the details about elements being + updated, and force option to perform the actual downgrade): + + $ python upgrade-keycloak-to-wildfly-tag.py -v -f 16.0.0.Final + """ + + # Set loglevel to debug if '-v' or '--verbose' option was specified + wu.__loglevel__ = logging.DEBUG if verbose else logging.INFO + + upgradeKeycloakToWildflyTag(tag, forceUpdates = force, ssoAdapters = update_rh_sso_adapters) + +def upgradeKeycloakToWildflyTag(tag, forceUpdates = False, ssoAdapters = False): + wildflyTag = wu.isWellFormedWildflyTag(tag) wildflyPomBaseUrl = "https://github.com/wildfly/wildfly/raw/%s/pom.xml" % wildflyTag - wu.getModuleLogger().info("Retrieving Wildfly's pom.xml for tag: %s" % wildflyTag) + taskLogger = wu.getTaskLogger("Rebase Keycloak on top of Wildfly '%s'" % wildflyTag) + taskLogger.info("Retrieving Wildfly's pom.xml for tag: %s" % wildflyTag) wildflyPomFile = wu.saveUrlToNamedTemporaryFile(wildflyPomBaseUrl) - wildflyPomXmlRoot = wu.getXmlRoot(wildflyPomFile) + wildflyCoreTag = wu.isWellFormedWildflyTag( wu.getPomProperty(wildflyPomXmlRoot, "version.org.wildfly.core")[0].text ) wildflyCorePomBaseUrl = "https://github.com/wildfly/wildfly-core/raw/%s/pom.xml" % wildflyCoreTag - - wu.getModuleLogger().info("Retrieving Wildfly-Core pom.xml for tag: %s" % wildflyCoreTag) + taskLogger.info("Retrieving Wildfly-Core pom.xml for tag: %s" % wildflyCoreTag) wildflyCorePomFile = wu.saveUrlToNamedTemporaryFile(wildflyCorePomBaseUrl) if wildflyPomFile != None and wildflyCorePomFile != None: # Subtask - Update main Keycloak pom.xml file - wu.updateMainKeycloakPomFile(wildflyPomFile, wildflyCorePomFile) + wu.performMainKeycloakPomFileUpdateTask(wildflyPomFile, wildflyCorePomFile, forceUpdates) # Subtask - Update Keycloak adapters - wu.performKeycloakAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile) - # Subtask - Update RH-SSO adapters - wu.performRhssoAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile) + wu.performKeycloakAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile, forceUpdates) + + if ssoAdapters: + # Subtask - Update RH-SSO adapters + wu.performRhssoAdapterLicenseFilesUpdateTask(wildflyPomFile, wildflyCorePomFile, forceUpdates) + else: + skipRhSsoAdapterUpdatesMessage = ( + "Skipping RH-SSO adapters updates since their changes weren't requested!", + "\n\tRerun the script with '-r' or '--update-rh-sso-adapters' option to request them." + ) + taskLogger.warning(wu._empty_string.join(skipRhSsoAdapterUpdatesMessage)) + + # Subtask - Update properties of the deprecated Wildfly testing module if necessary + wu.performDeprecatedWildflyTestingModuleUpdateTask(forceUpdates) + # Subtask - Update version of jboss-parent if necessary + wu.performJbossParentVersionUpdateTask(wildflyTag, wildflyPomFile, wildflyCorePomFile, forceUpdates) + # Subtask - Synchronize the XML namespace of the 'subsystem' element of the Keycloak + # Infinispan subsystem template with its current value as used by Wildfly + wu.synchronizeInfinispanSubsystemXmlNamespaceWithWildfly(wildflyTag) for filename in [wildflyPomFile, wildflyCorePomFile]: os.remove(filename) + + rebaseDoneMessage = ( + "Done rebasing Keycloak to Wildfly '%s' release!" % wildflyTag, + "\n\tRun 'git status' to list the changed files and 'git diff ' to inspect changes done to a specific file." + ) + taskLogger.info(wu._empty_string.join(rebaseDoneMessage)) + +if __name__ == '__main__': + processParameters() diff --git a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java index f1546a5c6a..79d835f05d 100644 --- a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java @@ -22,7 +22,6 @@ import org.infinispan.client.hotrod.exceptions.HotRodClientException; import org.infinispan.commons.marshall.Externalizer; import org.infinispan.commons.marshall.MarshallUtil; import org.infinispan.commons.marshall.SerializeWith; -import org.infinispan.commons.util.concurrent.ConcurrentHashSet; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.notifications.Listener; import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged; diff --git a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/LockEntryPredicate.java b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/LockEntryPredicate.java index f5893a2d25..ce18ae9673 100644 --- a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/LockEntryPredicate.java +++ b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/LockEntryPredicate.java @@ -18,20 +18,20 @@ package org.keycloak.cluster.infinispan; +import org.infinispan.commons.marshall.Externalizer; +import org.infinispan.commons.marshall.MarshallUtil; +import org.infinispan.commons.marshall.SerializeWith; +import org.keycloak.models.sessions.infinispan.util.KeycloakMarshallUtil; + import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; -import org.infinispan.commons.marshall.Externalizer; -import org.infinispan.commons.marshall.MarshallUtil; -import org.infinispan.commons.marshall.SerializeWith; -import org.infinispan.commons.util.concurrent.ConcurrentHashSet; -import org.keycloak.models.sessions.infinispan.util.KeycloakMarshallUtil; - /** * @author Marek Posolda */ @@ -77,7 +77,7 @@ public class LockEntryPredicate implements Predicate new ConcurrentHashSet<>()) + KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, ConcurrentHashMap::newKeySet) ); } } diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java index 76fae5989b..00ff73b09e 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java @@ -178,20 +178,17 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon boolean clustered = config.getBoolean("clustered", false); boolean async = config.getBoolean("async", false); - boolean allowDuplicateJMXDomains = config.getBoolean("allowDuplicateJMXDomains", true); this.topologyInfo = new TopologyInfo(cacheManager, config, true); if (clustered) { String jgroupsUdpMcastAddr = config.get("jgroupsUdpMcastAddr", System.getProperty(InfinispanConnectionProvider.JGROUPS_UDP_MCAST_ADDR)); configureTransport(gcb, topologyInfo.getMyNodeName(), topologyInfo.getMySiteName(), jgroupsUdpMcastAddr); - gcb.globalJmxStatistics() + gcb.jmx() .jmxDomain(InfinispanConnectionProvider.JMX_DOMAIN + "-" + topologyInfo.getMyNodeName()); } - gcb.globalJmxStatistics() - .allowDuplicateDomains(allowDuplicateJMXDomains) - .enable(); + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); // For Infinispan 10, we go with the JBoss marshalling. // TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream. @@ -484,7 +481,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon } - transportBuilder.globalJmxStatistics() + transportBuilder.jmx() .jmxDomain(InfinispanConnectionProvider.JMX_DOMAIN + "-" + nodeName) .enable(); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java index dfaa3aa799..4fe17fda0f 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java @@ -17,22 +17,21 @@ package org.keycloak.models.sessions.infinispan.entities; +import org.infinispan.commons.marshall.Externalizer; +import org.infinispan.commons.marshall.MarshallUtil; +import org.infinispan.commons.marshall.SerializeWith; import org.keycloak.models.sessions.infinispan.util.KeycloakMarshallUtil; +import org.keycloak.sessions.AuthenticationSessionModel; +import org.keycloak.sessions.CommonClientSessionModel.ExecutionStatus; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; import java.io.Serializable; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.infinispan.commons.util.concurrent.ConcurrentHashSet; -import org.keycloak.sessions.AuthenticationSessionModel; -import org.keycloak.sessions.CommonClientSessionModel.ExecutionStatus; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import org.infinispan.commons.marshall.Externalizer; -import org.infinispan.commons.marshall.MarshallUtil; -import org.infinispan.commons.marshall.SerializeWith; - /** * @author Marek Posolda */ @@ -52,7 +51,7 @@ public class AuthenticationSessionEntity implements Serializable { private Map clientNotes; private Map authNotes; - private Set requiredActions = new ConcurrentHashSet<>(); + private Set requiredActions = ConcurrentHashMap.newKeySet(); private Map userSessionNotes; public AuthenticationSessionEntity() { @@ -234,14 +233,14 @@ public class AuthenticationSessionEntity implements Serializable { MarshallUtil.unmarshallString(input), // redirectUri MarshallUtil.unmarshallString(input), // action - KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashSet<>()), // clientScopes + KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, ConcurrentHashMap::newKeySet), // clientScopes KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, EXECUTION_STATUS_EXT, size -> new ConcurrentHashMap<>(size)), // executionStatus MarshallUtil.unmarshallString(input), // protocol KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashMap<>(size)), // clientNotes KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashMap<>(size)), // authNotes - KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashSet<>()), // requiredActions + KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, ConcurrentHashMap::newKeySet), // requiredActions KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashMap<>(size)) // userSessionNotes ); } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java index 7766a18685..f232186750 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java @@ -18,6 +18,7 @@ package org.keycloak.models.sessions.infinispan.remotestore; import java.io.Serializable; +import java.net.SocketAddress; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -74,21 +75,28 @@ public class RemoteCacheSessionsLoader implements SessionLoader> segmentsByAddress = operationsFactory.getPrimarySegmentsByAddress(); - // Same like RemoteCloseableIterator.startInternal - IterationStartOperation iterationStartOperation = operationsFactory.newIterationStartOperation(null, null, null, sessionsPerSegment, false, null); - IterationStartResponse startResponse = await(iterationStartOperation.execute()); + for (Map.Entry> entry : segmentsByAddress.entrySet()) { + SocketAddress targetAddress = entry.getKey(); - try { - // Could happen for non-clustered caches - if (startResponse.getSegmentConsistentHash() == null) { - return -1; - } else { - return startResponse.getSegmentConsistentHash().getNumSegments(); + // Same like RemoteCloseableIterator.startInternal + IterationStartOperation iterationStartOperation = operationsFactory.newIterationStartOperation(null, null, null, sessionsPerSegment, false, null, targetAddress); + IterationStartResponse startResponse = await(iterationStartOperation.execute()); + + try { + // Could happen for non-clustered caches + if (startResponse.getSegmentConsistentHash() == null) { + return -1; + } else { + return startResponse.getSegmentConsistentHash().getNumSegments(); + } + } finally { + startResponse.getChannel().close(); } - } finally { - startResponse.getChannel().close(); } + // Handle the case when primary segments owned by the address are not known + return -1; } diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/JDGPutTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/JDGPutTest.java index dbac897c58..977dfd1b91 100644 --- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/JDGPutTest.java +++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/JDGPutTest.java @@ -19,6 +19,7 @@ package org.keycloak.cluster.infinispan; import java.awt.print.Book; +import java.net.SocketAddress; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -131,21 +132,28 @@ public class JDGPutTest { protected static int getIspnSegmentsCount(RemoteCache remoteCache) { OperationsFactory operationsFactory = ((RemoteCacheImpl) remoteCache).getOperationsFactory(); + Map> segmentsByAddress = operationsFactory.getPrimarySegmentsByAddress(); - // Same like RemoteCloseableIterator.startInternal - IterationStartOperation iterationStartOperation = operationsFactory.newIterationStartOperation(null, null, null, 64, false, null); - IterationStartResponse startResponse = await(iterationStartOperation.execute()); + for (Map.Entry> entry : segmentsByAddress.entrySet()) { + SocketAddress targetAddress = entry.getKey(); - try { - // Could happen for non-clustered caches - if (startResponse.getSegmentConsistentHash() == null) { - return -1; - } else { - return startResponse.getSegmentConsistentHash().getNumSegments(); + // Same like RemoteCloseableIterator.startInternal + IterationStartOperation iterationStartOperation = operationsFactory.newIterationStartOperation(null, null, null, 64, false, null, targetAddress); + IterationStartResponse startResponse = await(iterationStartOperation.execute()); + + try { + // Could happen for non-clustered caches + if (startResponse.getSegmentConsistentHash() == null) { + return -1; + } else { + return startResponse.getSegmentConsistentHash().getNumSegments(); + } + } finally { + startResponse.getChannel().close(); } - } finally { - startResponse.getChannel().close(); } + // Handle the case when primary segments owned by the address are not known + return -1; } // Compute set of ISPN segments into 1 "worker" segment diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java index 4e170c69a7..9ba7f7762a 100644 --- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java +++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java @@ -38,31 +38,21 @@ class TestCacheManagerFactory { & RemoteStoreConfigurationChildBuilder> EmbeddedCacheManager createManager(int threadId, String cacheName, Class builderClass) { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); - GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); + gcb = gcb.clusteredDefault(); + gcb.transport().clusterName("test-clustering-" + threadId); // For Infinispan 10, we go with the JBoss marshalling. // TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream. // See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details gcb.serialization().marshaller(new JBossUserMarshaller()); - - boolean clustered = true; - boolean async = false; - boolean allowDuplicateJMXDomains = true; - - if (clustered) { - gcb = gcb.clusteredDefault(); - gcb.transport().clusterName("test-clustering-" + threadId); - } - - gcb.jmx() - .domain(InfinispanConnectionProvider.JMX_DOMAIN + "-" + threadId).enable(); - + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN + "-" + threadId).enable(); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); Configuration invalidationCacheConfiguration = getCacheBackedByRemoteStore(threadId, cacheName, builderClass); - cacheManager.defineConfiguration(cacheName, invalidationCacheConfiguration); cacheManager.defineConfiguration("local", new ConfigurationBuilder().build()); + return cacheManager; } @@ -71,8 +61,6 @@ class TestCacheManagerFactory { private & RemoteStoreConfigurationChildBuilder> Configuration getCacheBackedByRemoteStore(int threadId, String cacheName, Class builderClass) { ConfigurationBuilder cacheConfigBuilder = new ConfigurationBuilder(); - //String host = "localhost"; - //int port = threadId==1 ? 12232 : 13232; String host = threadId==1 ? "jdg1" : "jdg2"; int port = 11222; @@ -88,7 +76,6 @@ class TestCacheManagerFactory { .forceReturnValues(false) .marshaller(KeycloakHotRodMarshallerFactory.class.getName()) .protocolVersion(ProtocolVersion.PROTOCOL_VERSION_29) - //.maxBatchSize(5) .addServer() .host(host) .port(port) diff --git a/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java b/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java index b68d6537ee..4c0bca3b81 100644 --- a/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java +++ b/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java @@ -156,8 +156,7 @@ public class InfinispanKeyStorageProviderTest { protected Cache getKeysCache() { GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - gcb.globalJmxStatistics().allowDuplicateDomains(true).enabled(true); - + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); final DefaultCacheManager cacheManager = new DefaultCacheManager(gcb.build()); ConfigurationBuilder cb = new ConfigurationBuilder(); @@ -167,8 +166,8 @@ public class InfinispanKeyStorageProviderTest { .size(InfinispanConnectionProvider.KEYS_CACHE_DEFAULT_MAX); cb.jmxStatistics().enabled(true); Configuration cfg = cb.build(); - cacheManager.defineConfiguration(InfinispanConnectionProvider.KEYS_CACHE_NAME, cfg); + return cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME); } } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java index 4c5ccc47d0..517b48289a 100755 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java @@ -31,28 +31,18 @@ public class ClusteredCacheBehaviorTest { public EmbeddedCacheManager createManager() { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - boolean clustered = true; - boolean async = false; - boolean allowDuplicateJMXDomains = true; - - if (clustered) { - gcb = gcb.clusteredDefault(); - gcb.transport().clusterName("test-clustering"); - } - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - + gcb = gcb.clusteredDefault(); + gcb.transport().clusterName("test-clustering"); + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); - ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); - } + invalidationConfigBuilder.clustering().cacheMode(CacheMode.INVALIDATION_SYNC); Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build(); - cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration); + return cacheManager; } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyLockingTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyLockingTest.java index dd0cf32091..c4cfc9b840 100755 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyLockingTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyLockingTest.java @@ -13,8 +13,8 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; /** * @author Bill Burke @@ -65,13 +65,9 @@ public class ConcurrencyLockingTest { protected DefaultCacheManager getVersionedCacheManager() { GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - - boolean allowDuplicateJMXDomains = true; - - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); final DefaultCacheManager cacheManager = new DefaultCacheManager(gcb.build()); + ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration); @@ -81,8 +77,8 @@ public class ConcurrencyLockingTest { counterConfigBuilder.transaction().transactionManagerLookup(new EmbeddedTransactionManagerLookup()); counterConfigBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC); Configuration counterCacheConfiguration = counterConfigBuilder.build(); - cacheManager.defineConfiguration("COUNTER_CACHE", counterCacheConfiguration); + return cacheManager; } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyVersioningTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyVersioningTest.java index 28f435de27..d107de5c67 100755 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyVersioningTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ConcurrencyVersioningTest.java @@ -235,18 +235,9 @@ public class ConcurrencyVersioningTest { protected DefaultCacheManager getVersionedCacheManager() { GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - - boolean clustered = false; - boolean async = false; - boolean allowDuplicateJMXDomains = true; - - if (clustered) { - gcb.transport().defaultTransport(); - } - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); final DefaultCacheManager cacheManager = new DefaultCacheManager(gcb.build()); + ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); invalidationConfigBuilder //.invocationBatching().enable() @@ -259,14 +250,9 @@ public class ConcurrencyVersioningTest { //.writeSkewCheck(true).versioning() //.enable().scheme(VersioningScheme.SIMPLE); - - //invalidationConfigBuilder.locking().isolationLevel(IsolationLevel.REPEATABLE_READ).writeSkewCheck(true).versioning().enable().scheme(VersioningScheme.SIMPLE); - - if (clustered) { - invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); - } Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration); + return cacheManager; } } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java index 719a39a562..cd3f06c9aa 100644 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java @@ -189,35 +189,24 @@ public class DistributedCacheConcurrentWritesTest { public static EmbeddedCacheManager createManager(String nodeName) { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - boolean clustered = true; - boolean async = false; - boolean allowDuplicateJMXDomains = true; - - if (clustered) { - gcb = gcb.clusteredDefault(); - gcb.transport().clusterName("test-clustering"); - gcb.transport().nodeName(nodeName); - } - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - + gcb = gcb.clusteredDefault(); + gcb.transport().clusterName("test-clustering"); + gcb.transport().nodeName(nodeName); + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); - ConfigurationBuilder distConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - distConfigBuilder.clustering().cacheMode(async ? CacheMode.DIST_ASYNC : CacheMode.DIST_SYNC); - distConfigBuilder.clustering().hash().numOwners(1); + distConfigBuilder.clustering().cacheMode(CacheMode.DIST_SYNC); + distConfigBuilder.clustering().hash().numOwners(1); - // Disable L1 cache - distConfigBuilder.clustering().hash().l1().enabled(false); - } + // Disable L1 cache + distConfigBuilder.clustering().hash().l1().enabled(false); Configuration distConfig = distConfigBuilder.build(); - cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, distConfig); - return cacheManager; + return cacheManager; } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java index 01dcf059b1..590ab26031 100644 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java @@ -162,58 +162,43 @@ public class DistributedCacheWriteSkewTest { public static EmbeddedCacheManager createManager(String nodeName) { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - boolean clustered = true; - boolean async = false; - boolean allowDuplicateJMXDomains = true; - - if (clustered) { - gcb = gcb.clusteredDefault(); - gcb.transport().clusterName("test-clustering"); - gcb.transport().nodeName(nodeName); - } - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - + gcb = gcb.clusteredDefault(); + gcb.transport().clusterName("test-clustering"); + gcb.transport().nodeName(nodeName); + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); - ConfigurationBuilder distConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - distConfigBuilder.clustering().cacheMode(async ? CacheMode.DIST_ASYNC : CacheMode.DIST_SYNC); - distConfigBuilder.clustering().hash().numOwners(1); + distConfigBuilder.clustering().cacheMode(CacheMode.DIST_SYNC); + distConfigBuilder.clustering().hash().numOwners(1); - // Disable L1 cache - distConfigBuilder.clustering().hash().l1().enabled(false); + // Disable L1 cache + distConfigBuilder.clustering().hash().l1().enabled(false); - //distConfigBuilder.storeAsBinary().enable().storeKeysAsBinary(false).storeValuesAsBinary(true); + //distConfigBuilder.storeAsBinary().enable().storeKeysAsBinary(false).storeValuesAsBinary(true); - // KEYCLOAK-13692 - Per ISPN-7613 Infinispan: - // * Automatically enables versioning when needed, - // * writeSkewCheck automatically enabled for OPTIMISTIC and REPEATABLE_READ transactions - // distConfigBuilder.versioning().enabled(true); - // distConfigBuilder.versioning().scheme(VersioningScheme.SIMPLE); - // distConfigBuilder.locking().writeSkewCheck(true); + // KEYCLOAK-13692 - Per ISPN-7613 Infinispan: + // * Automatically enables versioning when needed, + // * writeSkewCheck automatically enabled for OPTIMISTIC and REPEATABLE_READ transactions + // so the following explicit settings of these are not needed anymore + // distConfigBuilder.versioning().enabled(true); + // distConfigBuilder.versioning().scheme(VersioningScheme.SIMPLE); + // distConfigBuilder.locking().writeSkewCheck(true); - distConfigBuilder.locking().isolationLevel(IsolationLevel.REPEATABLE_READ); - distConfigBuilder.locking().concurrencyLevel(32); - distConfigBuilder.locking().lockAcquisitionTimeout(1000, TimeUnit.SECONDS); + distConfigBuilder.locking().isolationLevel(IsolationLevel.REPEATABLE_READ); + distConfigBuilder.locking().concurrencyLevel(32); + distConfigBuilder.locking().lockAcquisitionTimeout(1000, TimeUnit.SECONDS); - // KEYCLOAK-13692 - Per ISPN-7613 Infinispan: - // * Automatically enables versioning when needed, - // * writeSkewCheck automatically enabled for OPTIMISTIC and REPEATABLE_READ transactions - // distConfigBuilder.versioning().enabled(true); - // distConfigBuilder.versioning().scheme(VersioningScheme.SIMPLE); + // distConfigBuilder.invocationBatching().enable(); + //distConfigBuilder.transaction().transactionMode(TransactionMode.TRANSACTIONAL); + distConfigBuilder.transaction().transactionManagerLookup(new EmbeddedTransactionManagerLookup()); + distConfigBuilder.transaction().lockingMode(LockingMode.OPTIMISTIC); - // distConfigBuilder.invocationBatching().enable(); - //distConfigBuilder.transaction().transactionMode(TransactionMode.TRANSACTIONAL); - distConfigBuilder.transaction().transactionManagerLookup(new EmbeddedTransactionManagerLookup()); - distConfigBuilder.transaction().lockingMode(LockingMode.OPTIMISTIC); - } Configuration distConfig = distConfigBuilder.build(); - cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, distConfig); - return cacheManager; + return cacheManager; } } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/L1SerializationIssueTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/L1SerializationIssueTest.java index a512a2893e..e5952d14c4 100644 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/L1SerializationIssueTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/L1SerializationIssueTest.java @@ -30,6 +30,7 @@ import org.infinispan.manager.EmbeddedCacheManager; import org.jboss.logging.Logger; import org.junit.Ignore; import org.junit.Test; +import org.keycloak.connections.infinispan.InfinispanConnectionProvider; /** * Reproducer for RHSSO-377 / ISPN-6806 @@ -132,16 +133,13 @@ public class L1SerializationIssueTest { private EmbeddedCacheManager createManager() { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); - GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); gcb = gcb.clusteredDefault(); gcb.transport().clusterName("test-clustering"); - - gcb.globalJmxStatistics().allowDuplicateDomains(true); - + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); - ConfigurationBuilder distConfigBuilder = new ConfigurationBuilder(); ClusteringConfigurationBuilder clusterConfBuilder = distConfigBuilder.clustering(); clusterConfBuilder.cacheMode(CacheMode.DIST_SYNC); @@ -149,10 +147,9 @@ public class L1SerializationIssueTest { clusterConfBuilder.l1().enabled(L1_ENABLED) .lifespan(L1_LIFESPAN_MS) .cleanupTaskFrequency(L1_CLEANER_MS); - Configuration distCacheConfiguration = distConfigBuilder.build(); - cacheManager.defineConfiguration(CACHE_NAME, distCacheConfiguration); + return cacheManager; } diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java index b862b95ab5..b647cdda2b 100644 --- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java @@ -96,26 +96,15 @@ public class OutdatedTopologyExceptionReproducerTest { private EmbeddedCacheManager createManager() { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - boolean clustered = true; - boolean async = false; - boolean allowDuplicateJMXDomains = true; - - if (clustered) { - gcb = gcb.clusteredDefault(); - gcb.transport().clusterName("test-clustering"); - } - - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - + gcb = gcb.clusteredDefault(); + gcb.transport().clusterName("test-clustering"); + gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); - ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); - } + invalidationConfigBuilder.clustering().cacheMode(CacheMode.INVALIDATION_SYNC); // Uncomment this to have test fixed!!! // invalidationConfigBuilder.customInterceptors() @@ -124,10 +113,9 @@ public class OutdatedTopologyExceptionReproducerTest { // .interceptorClass(StateTransferInterceptor.class); Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build(); - cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration); - return cacheManager; + return cacheManager; } private class CacheOperations extends Thread { diff --git a/pom.xml b/pom.xml index ddd79bd475..78103cae54 100755 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.jboss jboss-parent - 35 + 37 Keycloak @@ -52,10 +52,10 @@ 7.4.0.GA ${timestamp} - 20.0.1.Final + 21.0.1.Final 1.2.13.Final 7.4.0.CD20-redhat-00001 - 12.0.3.Final + 13.0.3.Final 7.2.0.Final 7.5.22.Final-redhat-1 @@ -63,22 +63,22 @@ 0.66.19 2.4 - 4.5.12 + 4.5.13 4.4.13 0.6 1.5.1.Final 1.65 - 3.3.6 - 3.3.6 - 3.3.6 - 3.3.6 + 3.3.7 + 3.3.7 + 3.3.7 + 3.3.7 2.1.3 1.4.197 2.2.3 - 5.3.17.Final - 5.3.17.Final - 10.1.8.Final - 2.10.4 + 5.3.20.Final + 5.3.20.Final + 11.0.4.Final + 2.10.5 ${jackson.version} ${jackson.databind.version} 1.6.5 @@ -89,7 +89,7 @@ 2.0.0.Final 2.0.0.Final 1.2.17 - 3.12.1.Final + 3.13.2.Final ${resteasy.version} 20191001.1 1.7.30 @@ -100,9 +100,9 @@ 2.3.1 1.2.1 2.3.3-b02 - 2.1.3.Final - 1.12.1.Final - 1.7.1.Final + 2.2.2.Final + 1.13.1.Final + 1.8.0.Final 9.2.4.v20141103 9.3.9.v20160517 9.4.29.v20200521 @@ -111,7 +111,7 @@ 1.1.6 1.5.2.Final 1.4.3 - 5.0.3.Final-redhat-00005 + 5.0.3.Final-redhat-00006 25.0-jre @@ -119,7 +119,7 @@ 2.6 - 3.9 + 3.10 2.6 2.0.0.AM26 2.0.0 @@ -157,7 +157,7 @@ 1.0.4 1.7.6 2.3.7 - 2.0.0.Final + 2.0.1.Final 1.6.5 1.8.0 0.28.0 diff --git a/quarkus/runtime/src/main/resources/cluster-default.xml b/quarkus/runtime/src/main/resources/cluster-default.xml index 1a2b5ee213..44c7987867 100644 --- a/quarkus/runtime/src/main/resources/cluster-default.xml +++ b/quarkus/runtime/src/main/resources/cluster-default.xml @@ -18,20 +18,24 @@ + xsi:schemaLocation="urn:infinispan:config:11.0 http://www.infinispan.org/schemas/infinispan-config-11.0.xsd" + xmlns="urn:infinispan:config:11.0"> - - - + + + + + - - - + + + + + @@ -40,22 +44,28 @@ - - - + + + + + + + + + - - - + + + + + - - - + - \ No newline at end of file + diff --git a/quarkus/runtime/src/main/resources/cluster-local.xml b/quarkus/runtime/src/main/resources/cluster-local.xml index 282ef83591..e6e3e85015 100644 --- a/quarkus/runtime/src/main/resources/cluster-local.xml +++ b/quarkus/runtime/src/main/resources/cluster-local.xml @@ -18,22 +18,26 @@ + xsi:schemaLocation="urn:infinispan:config:11.0 http://www.infinispan.org/schemas/infinispan-config-11.0.xsd" + xmlns="urn:infinispan:config:11.0"> - - - + + + + + - - - + + + + + @@ -43,21 +47,27 @@ - - - + + + + + + + + + - - - + + + + + - - - + - \ No newline at end of file + diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index 5e952e8c85..57438913e3 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -1,4 +1,4 @@ - + - - + 4.0.0 @@ -42,9 +40,9 @@ undertow - 19.1.0.Final - 11.1.1.Final - 2.1.1.Final + 20.0.1.Final + 12.0.3.Final + 2.2.0.Final 1.6.0.Final @@ -442,7 +440,7 @@ org.h2.Driver keycloak sa - + jdbc:h2:mem:test;MVCC=TRUE;DB_CLOSE_DELAY=-1 diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-ha.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-ha.xml index 5492638ed1..b6b8c13365 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-ha.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-ha.xml @@ -18,21 +18,25 @@ + xsi:schemaLocation="urn:infinispan:config:11.0 http://www.infinispan.org/schemas/infinispan-config-11.0.xsd" + xmlns="urn:infinispan:config:11.0"> - - - + + + + + - - - + + + + + @@ -41,22 +45,28 @@ - - - + + + + + + + + + - - - + + + + + - - - + - \ No newline at end of file + diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-local.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-local.xml index 987a954653..acbceb0dbd 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-local.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/cluster-local.xml @@ -18,8 +18,8 @@ + xsi:schemaLocation="urn:infinispan:config:11.0 http://www.infinispan.org/schemas/infinispan-config-11.0.xsd" + xmlns="urn:infinispan:config:11.0"> @@ -27,14 +27,18 @@ - - - + + + + + - - - + + + + + @@ -44,21 +48,27 @@ - - - + + + + + + + + + - - - + + + + + - - - + - \ No newline at end of file + diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index ac806932d5..003ca2d0d4 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -218,6 +218,7 @@ src/test/resources arquillian.xml + wildfly-config.xml password-blacklists/** log4j.properties vault/** diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java index 3d794b1657..2146aadf9c 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java @@ -71,6 +71,8 @@ import java.io.FileFilter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.security.Provider; +import java.security.Security; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -81,12 +83,19 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.ws.rs.NotFoundException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.jboss.arquillian.test.spi.event.suite.After; import org.jboss.arquillian.test.spi.event.suite.Before; import org.jboss.shrinkwrap.api.importer.ZipImporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.junit.Assert; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot; import static org.keycloak.testsuite.util.ServerURLs.removeDefaultPorts; @@ -402,6 +411,8 @@ public class AuthServerTestEnricher { //frontend-only (either load-balancer or auth-server) log.debug("Starting auth server before suite"); + setJsseSecurityProviderForOutboundSslConnectionsOfElytronClient(); + try { startContainerEvent.fire(new StartContainer(suiteContext.getAuthServerInfo().getArquillianContainer())); } catch (Exception e) { @@ -551,6 +562,194 @@ public class AuthServerTestEnricher { } } + /** KEYCLOAK-15692 Work-around the OpenJSSE TlsMasterSecretGenerator error: + * + * https://github.com/openjsse/openjsse/issues/11 + * + * To prevent above TLS handshake error when initiating a TLS connection + * ensure: + * * Either both server and client endpoints of the future TLS connection + * simultaneously utilize a JSSE security provider using the OpenJSSE + * extension, + * + * * Or both server and client endpoints simultaneously use a JSSE + * security provider, which doesn't depend on the OpenJSSE extension. + * + * Do this by performing the following: + * * On platforms where implementation of the SunJSSE provider depends on + * OpenJSSE extension ensure only SunJSSE provider is used to define the + * SSL context of the Elytron client used for outbound SSL connections. + * + * * On other platforms, use any suitable JSSE provider by querying all + * the platform providers for respective property. + * + */ + public static void setJsseSecurityProviderForOutboundSslConnectionsOfElytronClient() { + log.info( + "Determining the JSSE security provider to use for outbound " + + "SSL/TLS connections of the Elytron client..." + ); + /** First locate the wildfly-config.xml to use. Per: + * https://docs.wildfly.org/21/Client_Guide.html#wildfly-config-xml-discovery + * + * 1) try to load it from the 'wildfly.config.url' property + */ + String wildflyConfigXmlPath = System.getProperty("wildfly.config.url"); + + // 2) If not set, scan the classpath + if (wildflyConfigXmlPath == null) { + log.debug("Scanning classpath to locate wildfly-config.xml..."); + final String javaClassPath = System.getProperty("java.class.path"); + for (String dir : javaClassPath.split(":")) { + if (!dir.isEmpty()) { + String candidatePath = dir + File.separator + + "wildfly-config.xml"; + if (new File(candidatePath).exists()) { + wildflyConfigXmlPath = candidatePath; + log.debugf( + "Using wildfly-config.xml at '%s' location!", + wildflyConfigXmlPath + ); + break; + } + } + } + } else { + log.debugf( + "Using wildfly-config.xml from 'wildfly.config.url' " + + "property at '%s' location", + wildflyConfigXmlPath + ); + } + // If still not found, that's an error + if (wildflyConfigXmlPath == null) { + throw new RuntimeException( + "Failed to locate the wildfly-config.xml to use for " + + "the configuration of Elytron client!" + ); + } + /** Determine the name of the system property from wildfly-config.xml + * holding the name of the security provider which is used by Elytron + * client to define its SSL context for outbound SSL connections. + */ + final File wildflyConfigXml = new File(wildflyConfigXmlPath); + + String jsseSecurityProviderSystemProperty = null; + try { + DocumentBuilder documentBuilder = DocumentBuilderFactory + .newInstance().newDocumentBuilder(); + + Document xmlDoc = documentBuilder.parse(wildflyConfigXml); + NodeList nodeList = xmlDoc.getElementsByTagName("provider-name"); + // Sanity check + if (nodeList.getLength() != 1) { + throw new RuntimeException( + "Failed to locate the 'provider-name' element " + + "in wildfly-config.xml XML file!" + ); + } + String providerNameElement = nodeList.item(0).getAttributes() + .getNamedItem("name").getNodeValue(); + + // Drop Wildfly's expressions notation from the attribute's value + jsseSecurityProviderSystemProperty = providerNameElement + .replaceAll("(\\$|\\{|\\}|(:.*$))", new String()); + + } catch (IOException e) { + throw new RuntimeException(String.format( + "Error reading the '%s' file. Please make sure the provided " + + "path is correct and retry!", + wildflyConfigXml.getAbsolutePath() + )); + } catch (ParserConfigurationException|SAXException e) { + throw new RuntimeException(String.format( + "Failed to parse the '%s' XML file!", + wildflyConfigXml.getAbsolutePath() + )); + } + + boolean determineJsseSecurityProviderName = false; + if (jsseSecurityProviderSystemProperty != null) { + // Does JSSE security provider system property already exist? + if ( + System.getProperty(jsseSecurityProviderSystemProperty) == null + ) { + // If not, determine it + determineJsseSecurityProviderName = true; + } + } else { + throw new RuntimeException( + "Failed to determine the name of system property " + + "holding JSSE security provider's name for Elytron client!" + ); + } + + if (determineJsseSecurityProviderName) { + + /** Detect if OpenJSSE extension is present on the platform + * + * Since internal 'com.sun.net.ssl.*' classes of the SunJSSE + * provider have identical names regardless if the OpenJSSE + * extension is used or not: + * + * https://github.com/openjsse/openjsse/blob/master/pom.xml#L125 + * + * detect the presence of the OpenJSSE extension by checking the + * presence of the 'openjsse.jar' file within the JRE extensions + * directory. + * + */ + final String jreExtensionsDir = System.getProperty("java.home") + + File.separator + "lib" + File.separator + "ext" + + File.separator + "openjsse.jar"; + + boolean openJsseExtensionPresent = new File( + jreExtensionsDir).exists(); + + Provider platformJsseProvider = Security + .getProviders("SSLContext.TLSv1.2")[0]; + + if (platformJsseProvider != null) { + // If OpenJSSE extension is present + if (openJsseExtensionPresent) { + // Sanity check - confirm SunJSSE provider is present on + // the platform (if OpenJSSE extension is present, it + // shouldn't ever happen SunJSSE won't be, but double-check + // for any case) + Provider sunJsseProvider = Stream.of( + Security.getProviders() + ).filter(p -> p.getName().equals("SunJSSE")) + .collect(Collectors.toList()) + .get(0); + + // Use it or throw an error if absent + if (sunJsseProvider != null) { + platformJsseProvider = sunJsseProvider; + } else { + throw new RuntimeException( + "The SunJSSE provider is not present " + + "on the platform!" + ); + } + } + // Propagate the final provider name to system property used by + // wildfly-config.xml to configure the JSSE provider name + System.setProperty( + jsseSecurityProviderSystemProperty, + platformJsseProvider.getName() + ); + } else { + throw new RuntimeException( + "Cannot identify a security provider for Elytron client " + + "offering the TLSv1.2 capability!" + ); + } + log.infof( + "Using the '%s' JSSE provider!", platformJsseProvider.getName() + ); + } + } + private static OnlineManagementClient getManagementClient(ContainerInfo containerInfo) { try { return ManagementClient.online(OnlineOptions diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/metrics/MetricsRestServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/metrics/MetricsRestServiceTest.java index 0d586ab924..31e93a4406 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/metrics/MetricsRestServiceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/metrics/MetricsRestServiceTest.java @@ -55,7 +55,7 @@ public class MetricsRestServiceTest extends AbstractKeycloakTest { try (Response response = client.target("http://" + MGMT_HOST + ":" + MGMT_PORT + "/health").request().get()) { Assert.assertThat(response, statusCodeIs(Status.OK)); - Assert.assertThat(response, body(containsString("{\"status\":\"UP\",\"checks\":[]}"))); + Assert.assertThat(response, body(containsString("{\"status\":\"UP\",\"checks\":[{"))); } finally { client.close(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-config.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-config.xml new file mode 100644 index 0000000000..05f2352000 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-config.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index f140c76967..1385ee479a 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -60,7 +60,7 @@ integration-arquillian-tests-base ${project.version} tests - arquillian.xml,keycloak-add-user.json,test-constants.properties,kerberos/*,keystore/*,password-blacklists/*,log4j.properties,vault/* + arquillian.xml,wildfly-config.xml,keycloak-add-user.json,test-constants.properties,kerberos/*,keystore/*,password-blacklists/*,log4j.properties,vault/* diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 8fadfdc93a..724b3ca96a 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -214,14 +214,14 @@ false true true - ${auth.server.config.dir}/client.jks - secret - ${auth.server.config.dir}/keycloak.truststore - secret ${auth.server.config.dir}/ca.crt ${auth.server.config.dir}/client.crt + ${auth.server.config.dir}/client.jks + secret ${auth.server.config.dir}/client.key secret + ${auth.server.config.dir}/keycloak.truststore + secret ${auth.server.config.dir}/other_client.jks @@ -601,13 +601,13 @@ ${project.version} ${client.certificate.ca.path} + ${client.certificate.file} ${client.certificate.keystore} ${client.certificate.keystore.passphrase} - ${client.truststore} - ${client.truststore.passphrase} - ${client.certificate.file} ${client.key.file} ${client.key.passphrase} + ${client.truststore} + ${client.truststore.passphrase} ${hok.client.certificate.keystore} @@ -653,6 +653,12 @@ ${keycloak.connectionsJpa.database} ${keycloak.connectionsJpa.user} ${keycloak.connectionsJpa.password} + + + ${project.build.directory}/dependency/wildfly-config.xml diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml index 5fb32ea190..30d8a8ea26 100755 --- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml +++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml @@ -19,7 +19,7 @@ org.jboss.as.connector - + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml index 41697cac28..cdc2a7e0ee 100755 --- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml +++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml @@ -15,21 +15,20 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - org.jboss.as.clustering.infinispan - + - + - + @@ -39,14 +38,14 @@ - + - - + + - + @@ -76,11 +75,11 @@ - + - + @@ -92,10 +91,10 @@ - + - + @@ -104,15 +103,15 @@ - + - + - - + + - + @@ -146,12 +145,12 @@ - + - + diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-undertow.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-undertow.xml index faebb13f4e..e4a9fa11d2 100644 --- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-undertow.xml +++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-undertow.xml @@ -24,7 +24,7 @@ org.wildfly.extension.undertow - +