From f44315d7daf06a14f438f5497deacf52b87abf45 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 23 Jul 2024 00:51:25 +0200 Subject: [PATCH 01/17] increase version for next increment --- .../bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml | 2 +- .../sapcxtools/sapcommercetoolkit/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxbackoffice/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxenvconfig/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxreporting/external-dependencies.xml | 2 +- .../hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml | 2 +- .../bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxtemplate/external-dependencies.xml | 2 +- sonar-project.properties | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml index f1be362..4a8e49d 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcommercetoolkit" version="4.3.1-SNAPSHOT" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml index ae78547..5785f13 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcommercetoolkit - 4.3.0 + 4.3.1-SNAPSHOT jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml index 4a641c8..3101048 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxbackoffice" version="4.3.1-SNAPSHOT" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml index e188a78..846a147 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxbackoffice - 4.3.0 + 4.3.1-SNAPSHOT jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml index 589e5da..23b7007 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxenvconfig" version="4.3.1-SNAPSHOT" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml index 1cf6dd2..ed862c4 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxenvconfig - 4.3.0 + 4.3.1-SNAPSHOT jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml index cd484d9..50aedc2 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxreporting" version="4.3.1-SNAPSHOT" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml index bed3335..1c123aa 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxreporting - 4.3.0 + 4.3.1-SNAPSHOT jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml index f87d8ac..326fc52 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxsearch" version="4.3.1-SNAPSHOT" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml index a8b72aa..5957502 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxsearch - 4.3.0 + 4.3.1-SNAPSHOT jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml index 006f293..06569fd 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxtemplate" version="4.3.1-SNAPSHOT" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml index 665739b..1e8162d 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxtemplate - 4.3.0 + 4.3.1-SNAPSHOT jar diff --git a/sonar-project.properties b/sonar-project.properties index 3ed7e1f..52ea013 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.organization=sapcxtools # This is the name and version displayed in the SonarCloud UI. sonar.projectName=sapcxtools-workspace -sonar.projectVersion=4.3.0 +sonar.projectVersion=4.3.1-SNAPSHOT # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=core-customize/hybris/bin/custom/sapcxtools From a66b810f5f0de129cfc4201b311480a722ec2929 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 23 Jul 2024 12:33:07 +0200 Subject: [PATCH 02/17] improve handling of user specific configuration in user's home directory --- .gitignore | 2 +- core-customize/build.gradle.kts | 104 +++++++++--------- .../hybris/config/cloud/local-dev.properties | 13 ++- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/.gitignore b/.gitignore index 26c25ad..6badfdc 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ core-customize/hybris/roles/ core-customize/hybris/log/ core-customize/hybris/temp/ core-customize/hybris/data/ -core-customize/hybris/config/local-config/99-local.properties +core-customize/hybris/config/local-config/9[0-9]-local.properties # only allow sapcxtools extension core-customize/hybris/bin/modules/ diff --git a/core-customize/build.gradle.kts b/core-customize/build.gradle.kts index 239f007..3ca7efc 100644 --- a/core-customize/build.gradle.kts +++ b/core-customize/build.gradle.kts @@ -67,65 +67,61 @@ tasks.register("generateLocalProperties") { } val symlinkConfigTask = tasks.register("symlinkConfig") +val hybrisConfig = file("hybris/config") val localConfig = file("hybris/config/local-config") +val homeDirectory = file(project.gradle.gradleUserHomeDir.parent) mapOf( - "10-local.properties" to file("hybris/config/cloud/common.properties"), - "20-local.properties" to file("hybris/config/cloud/persona/development.properties"), - "50-local.properties" to file("hybris/config/cloud/local-dev.properties") + "10-local.properties" to "cloud/common.properties", + "20-local.properties" to "cloud/persona/development.properties", + "50-local.properties" to "cloud/local-dev.properties", + "90-local.properties" to "local/90-local.properties", + "91-local.properties" to "local/91-local.properties", + "92-local.properties" to "local/92-local.properties", + "93-local.properties" to "local/93-local.properties", + "94-local.properties" to "local/94-local.properties", + "95-local.properties" to "local/95-local.properties", + "96-local.properties" to "local/96-local.properties", + "97-local.properties" to "local/97-local.properties", + "98-local.properties" to "local/98-local.properties" ).forEach{ - val symlinkTask = tasks.register("symlink${it.key}") { - val path = it.value.relativeTo(localConfig) - if (Os.isFamily(Os.FAMILY_UNIX)) { - commandLine("sh", "-c", "ln -sfn ${path} ${it.key}") - } else { - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - val windowsPath = path.toString().replace("[/]".toRegex(), "\\") - commandLine("cmd", "/c", """mklink /d "${it.key}" "${windowsPath}" """) - } - workingDir(localConfig) - dependsOn("generateLocalProperties") - } - symlinkConfigTask.configure { - dependsOn(symlinkTask) + val link = it.key + var path = file(hybrisConfig.absolutePath + "/" + it.value) + if (!path.exists()) { + path = file(homeDirectory.absolutePath + "/.sap-commerce/local-config/" + it.key) } -} - -if (project.file("../certificates/local.cxdev.me.p12").exists() && - project.file("../certificates/cxdev_eu_auth0_com-metadata.xml").exists()) { - - val symlinkTask = tasks.register("symlinkLocalSSOConfiguration") { - val link = "95-local.properties" - val path = file("hybris/config/cloud/local-sso.properties").relativeTo(localConfig) - - if (Os.isFamily(Os.FAMILY_UNIX)) { - commandLine("sh", "-c", "ln -sfn ${path} ${link}") - } else { - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - val windowsPath = path.toString().replace("[/]".toRegex(), "\\") - commandLine("cmd", "/c", """mklink /d "${link}" "${windowsPath}" """) + + if (path.exists()) { + val symlinkTask = tasks.register("symlink-${link}") { + val relPath = path.relativeTo(localConfig) + println("rel path: " + relPath) + + if (Os.isFamily(Os.FAMILY_UNIX)) { + commandLine("sh", "-c", "ln -sfn ${relPath} ${link}") + } else { + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + val windowsPath = relPath.toString().replace("[/]".toRegex(), "\\") + commandLine("cmd", "/c", """mklink "${link}" "${windowsPath}" """) + } + workingDir(localConfig) + dependsOn("generateLocalProperties") } - - workingDir(localConfig) - dependsOn("generateLocalProperties") - } - symlinkConfigTask.configure { - dependsOn(symlinkTask) - } -} else if (file("95-local.properties").relativeTo(localConfig).exists()) { - val unlinkTask = tasks.register("unlinkLocalSSOConfiguration") { - val link = "95-local.properties" - - if (Os.isFamily(Os.FAMILY_UNIX)) { - commandLine("sh", "-c", "unlink ${link}") - } else { - commandLine("cmd", "/c", """rmdir "${link}" """) + symlinkConfigTask.configure { + dependsOn(symlinkTask) + } + } else { + // Unlink if no longer existing + val unlinkTask = tasks.register("unlink-${link}") { + if (Os.isFamily(Os.FAMILY_UNIX)) { + commandLine("sh", "-c", "rm -f ${link}") + } else { + commandLine("cmd", "/c", "del /q ${link}") + } + workingDir(localConfig) + dependsOn("generateLocalProperties") + } + symlinkConfigTask.configure { + dependsOn(unlinkTask) } - - workingDir(localConfig) - dependsOn("generateLocalProperties") - } - symlinkConfigTask.configure { - dependsOn(unlinkTask) } } @@ -146,4 +142,4 @@ tasks.register("setupLocalDevelopment") { group = "SAP Commerce" description = "Setup local development" dependsOn("bootstrapPlatform", "generateLocalDeveloperProperties", "installManifestAddons") -} \ No newline at end of file +} diff --git a/core-customize/hybris/config/cloud/local-dev.properties b/core-customize/hybris/config/cloud/local-dev.properties index 66584e0..5f7c415 100644 --- a/core-customize/hybris/config/cloud/local-dev.properties +++ b/core-customize/hybris/config/cloud/local-dev.properties @@ -66,7 +66,7 @@ cronjob.timertask.loadonstartup=false mail.from=no-reply@cxdev.me mail.replyto=no-reply@cxdev.me -mail.smtp.server=mail.cxdev.me +mail.smtp.server=localhost mail.smtp.port=25 mail.use.tls=false @@ -101,6 +101,9 @@ flexible.search.exception.show.query.details=true # Backoffice Dev Settings backoffice.session.timeout=3600 +# faster startup / update system etc +backoffice.fill.typefacade.cache.on.startup=false +backoffice.solr.search.index.autoinit=false # https://help.sap.com/viewer/5c9ea0c629214e42b727bf08800d8dfa/latest/en-US/8b48115b86691014991ad2131153834f.html backoffice.cockpitng.development.mode=true # disable all caching for development @@ -115,11 +118,15 @@ backoffice.sass.source.map.enabled=true # backoffice hot deployment backoffice.cockpitng.hotDeployment.enabled=true -#reset backoffice config every login +# reset backoffice config every start, change to "login" for active backoffice development # https://help.sap.com/viewer/5c9ea0c629214e42b727bf08800d8dfa/latest/en-US/8b7db2c286691014af65a6a21e6d5933.html -backoffice.cockpitng.reset.triggers=login +backoffice.cockpitng.reset.triggers=start backoffice.cockpitng.reset.scope=widgets,cockpitConfig +# supress spurious update-backofficeIndex-CronJob message if index not available +log4j2.logger.indexer.name=de.hybris.platform.solrfacetsearch.indexer.strategies.impl.AbstractIndexerStrategy +log4j2.logger.indexer.level=WARN + # Always import Sample & Test data locally sapcommercetoolkit.impeximport.environment.isdevelopment=true From a500f5d8b41968b610b6f2289fb66fa5a6103bdb Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Wed, 24 Jul 2024 22:53:40 +0200 Subject: [PATCH 03/17] remove unnecessary print statement in gradle build --- core-customize/build.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/core-customize/build.gradle.kts b/core-customize/build.gradle.kts index 3ca7efc..d65b803 100644 --- a/core-customize/build.gradle.kts +++ b/core-customize/build.gradle.kts @@ -93,8 +93,6 @@ mapOf( if (path.exists()) { val symlinkTask = tasks.register("symlink-${link}") { val relPath = path.relativeTo(localConfig) - println("rel path: " + relPath) - if (Os.isFamily(Os.FAMILY_UNIX)) { commandLine("sh", "-c", "ln -sfn ${relPath} ${link}") } else { From bc4aa10a4f12eb3ef62202856ea271162dee9aeb Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Wed, 24 Sep 2025 21:43:11 +0200 Subject: [PATCH 04/17] improve handling and configuration possibilities of JwtAccessTokenVerificationFilter --- .../sapcxtools/sapcxsinglesignon/README.md | 43 ++++----- .../sapcxsinglesignon/project.properties | 6 ++ .../spring/sapcxsinglesignon-web-spring.xml | 15 ---- .../resources/sapcxsinglesignon-spring.xml | 23 +++++ .../JwtAccessTokenVerificationFilter.java | 88 ++++++++++++++----- .../user/NoUpdateUserFromTokenStrategy.java | 9 ++ .../sso/user/UpdateUserFromTokenStrategy.java | 7 ++ 7 files changed, 134 insertions(+), 57 deletions(-) create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/NoUpdateUserFromTokenStrategy.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/UpdateUserFromTokenStrategy.java diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md index 0a3df71..1172c18 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md @@ -155,26 +155,29 @@ This can be easily done using the `modifyPopulatorList` bean notation: ### Configuration parameters -| Parameter | Type | Description | -|-----------|------|-------------| -| sapcxsinglesignon.filter.enabled | Boolean | specifies whether the filter is active or not (default: false) | -| sapcxsinglesignon.filter.login.userClientId | String | the SAP Commerce client ID for your single page application (required) | -| sapcxsinglesignon.filter.idp.issuer | String | the registered issuer, eg. https://dev-1234.eu.auth0.com/ (required) | -| sapcxsinglesignon.filter.idp.audience | String | the registered API, eg. https://localhost:9002/occ/v2/ (required) | -| sapcxsinglesignon.filter.idp.clientid | String | the client ID of the application (required) | -| sapcxsinglesignon.filter.idp.claim.id | String | claim name used for user ID mapping (default: email) | -| sapcxsinglesignon.replicate.enabled | Boolean | specifies whether the replication is active or not (default: false) | -| sapcxsinglesignon.replicate.creation.enabled | Boolean | specifies whether the user creation is enabled or not (default: false) | -| sapcxsinglesignon.replicate.removal.enabled | Boolean | specifies whether the user removal is enabled or not (default: false) | -| sapcxsinglesignon.auth0.management.api.audience | String | the audience for your machine-to-machine application (required) | -| sapcxsinglesignon.auth0.management.api.clientid | String | the auth0 client ID for your machine-to-machine application (required) | -| sapcxsinglesignon.auth0.management.api.clientsecret | String | the auth0 client secret for your machine-to-machine application (required) | -| sapcxsinglesignon.auth0.customer.connection | String | the authentication connection for customers (default: "Username-Password-Authentication") | -| customer.metadata.prefix | String | the prefix for application metadata for customers (required, default: commerce) | -| sapcxsinglesignon.auth0.customer.role | String | the role to assign to newly created customer accounts | -| sapcxsinglesignon.auth0.customer.requireemailverification | String | specifies if the user needs to verify their email (default: false) | -| sapcxsinglesignon.auth0.customer.requirepasswordverification | String | specifies if the user needs to verify their password (default: false) | -| sapcxsinglesignon.auth0.customer.useblockedstatus | Boolean | specifies if the user shall be blocked when disabled in SAP Commerce (default: false) | +| Parameter | Type | Description | +|--------------------------------------------------------------|------|------------------------------------------------------------------------------------------| +| sapcxsinglesignon.filter.enabled | Boolean | specifies whether the filter is active or not (default: false) | +| sapcxsinglesignon.filter.login.userClientId | String | the SAP Commerce client ID for your single page application (required) | +| sapcxsinglesignon.filter.idp.issuer | String | the registered issuer, eg. https://dev-1234.eu.auth0.com/ (required) | +| sapcxsinglesignon.filter.idp.jwksUrl | String | if issuer is non-OIDC conform, use this URL for JWKS (optional) | +| sapcxsinglesignon.filter.idp.audience | String | the registered API, eg. https://localhost:9002/occ/v2/ (required) | +| sapcxsinglesignon.filter.idp.scope | String | the required scopeof the API, if any, eg. shop (optional) | +| sapcxsinglesignon.filter.idp.requiredClaims | String | comma-separated list of required claims for a valid token (optional) | +| sapcxsinglesignon.filter.idp.clientid | String | the client ID of the application (required) | +| sapcxsinglesignon.filter.idp.claim.id | String | claim name used for user ID mapping (default: email) | +| sapcxsinglesignon.replicate.enabled | Boolean | specifies whether the replication is active or not (default: false) | +| sapcxsinglesignon.replicate.creation.enabled | Boolean | specifies whether the user creation is enabled or not (default: false) | +| sapcxsinglesignon.replicate.removal.enabled | Boolean | specifies whether the user removal is enabled or not (default: false) | +| sapcxsinglesignon.auth0.management.api.audience | String | the audience for your machine-to-machine application (required) | +| sapcxsinglesignon.auth0.management.api.clientid | String | the auth0 client ID for your machine-to-machine application (required) | +| sapcxsinglesignon.auth0.management.api.clientsecret | String | the auth0 client secret for your machine-to-machine application (required) | +| sapcxsinglesignon.auth0.customer.connection | String | the authentication connection for customers (default: "Username-Password-Authentication") | +| customer.metadata.prefix | String | the prefix for application metadata for customers (required, default: commerce) | +| sapcxsinglesignon.auth0.customer.role | String | the role to assign to newly created customer accounts | +| sapcxsinglesignon.auth0.customer.requireemailverification | String | specifies if the user needs to verify their email (default: false) | +| sapcxsinglesignon.auth0.customer.requirepasswordverification | String | specifies if the user needs to verify their password (default: false) | +| sapcxsinglesignon.auth0.customer.useblockedstatus | Boolean | specifies if the user shall be blocked when disabled in SAP Commerce (default: false) | ## License diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties index f41add9..4d42e49 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties @@ -13,7 +13,10 @@ samlsinglesignon.additionalWebSpringConfigs.sapcxsinglesignon=classpath:/sapcxsi # login.userClientId - the SAP Commerce client ID for your single page application (required) # # filter.idp.issuer - the issuer, eg. https://dev-1234.eu.auth0.com/ (required) +# filter.idp.jwksUrl - the URL pointing to the JWKS, eg. https://dev-1234.eu.auth0.com/ (optional) # filter.idp.audience - the audience of the API, eg. https://localhost:9002/occ/v2/ (required) +# filter.idp.scope - the required scope of the API, if any, eg. shop (optional) +# filter.idp.requiredClaims - comma-separated list of required claims for a valid token (optional) # filter.idp.clientid - the client ID of the application (required) # filter.idp.claim.id - claim name used for user ID mapping (default: email) # @@ -25,7 +28,10 @@ sapcxsinglesignon.filter.enabled=false sapcxsinglesignon.filter.login.userClientId= sapcxsinglesignon.filter.idp.issuer= +sapcxsinglesignon.filter.idp.jwksUrl= sapcxsinglesignon.filter.idp.audience= +sapcxsinglesignon.filter.idp.scope= +sapcxsinglesignon.filter.idp.requiredClaims= sapcxsinglesignon.filter.idp.clientid= sapcxsinglesignon.filter.idp.claim.id=email diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/occ/v2/sapcxsinglesignonocc/web/spring/sapcxsinglesignon-web-spring.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/occ/v2/sapcxsinglesignonocc/web/spring/sapcxsinglesignon-web-spring.xml index c493a9f..378a28b 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/occ/v2/sapcxsinglesignonocc/web/spring/sapcxsinglesignon-web-spring.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/occ/v2/sapcxsinglesignonocc/web/spring/sapcxsinglesignon-web-spring.xml @@ -13,19 +13,4 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml index 0f04fc4..a5ff15f 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml @@ -6,6 +6,29 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java index dbd3701..a2bb060 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java @@ -3,8 +3,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; -import java.util.Collections; -import java.util.Date; +import java.util.*; +import java.util.stream.Stream; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -17,6 +17,7 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -24,12 +25,7 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.JwtDecoders; -import org.springframework.security.oauth2.jwt.JwtException; -import org.springframework.security.oauth2.jwt.JwtValidators; -import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.jwt.*; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.OAuth2Authentication; @@ -42,6 +38,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import tools.sapcx.commerce.sso.replication.CustomerReplicationStrategy; +import tools.sapcx.commerce.sso.user.UpdateUserFromTokenStrategy; /** * This filter performs verification of an external access token. @@ -58,16 +55,21 @@ public class JwtAccessTokenVerificationFilter extends OncePerRequestFilter { private static final Logger LOG = LoggerFactory.getLogger(JwtAccessTokenVerificationFilter.class); public static final String AUTHORIZED_PARTY_CLAIM = "azp"; + public static final String SCOPE_CLAIM = "scope"; private OAuth2RequestFactory oAuth2RequestFactory; private ClientDetailsService clientDetailsService; private UserDetailsService userDetailsService; + private UpdateUserFromTokenStrategy updateUserFromTokenStrategy; private CustomerReplicationStrategy customerReplicationStrategy; private TokenStore tokenStore; private String occClientId; private boolean enabled; + private String jwksUrl; private String issuer; private String audience; + private String scope; + private List requiredClaims; private String clientId; private String customerIdField; private TokenExtractor tokenExtractor; @@ -77,23 +79,37 @@ public JwtAccessTokenVerificationFilter( OAuth2RequestFactory oAuth2RequestFactory, ClientDetailsService clientDetailsService, UserDetailsService userDetailsService, + UpdateUserFromTokenStrategy updateUserFromTokenStrategy, CustomerReplicationStrategy customerReplicationStrategy, TokenStore tokenStore, String occClientId, boolean enabled, + String jwksUrl, String issuer, String audience, + String scope, + String requiredClaims, String clientId, String customerIdField) { this.oAuth2RequestFactory = oAuth2RequestFactory; this.clientDetailsService = clientDetailsService; this.userDetailsService = userDetailsService; + this.updateUserFromTokenStrategy = updateUserFromTokenStrategy; this.customerReplicationStrategy = customerReplicationStrategy; this.tokenStore = tokenStore; this.occClientId = occClientId; this.enabled = enabled; + this.jwksUrl = jwksUrl; this.issuer = issuer; this.audience = audience; + this.scope = scope; + this.requiredClaims = new ArrayList<>(); + if (isNotBlank(requiredClaims)) { + Stream.of(StringUtils.split(requiredClaims, ',')) + .map(StringUtils::trimToEmpty) + .filter(StringUtils::isNotBlank) + .forEach(this.requiredClaims::add); + } this.clientId = clientId; this.customerIdField = customerIdField; } @@ -139,10 +155,10 @@ private OAuth2AccessToken fetchFromTokenStore(String accessTokenValue, boolean c if (createIfMissing) { try { Jwt decodedToken = decodeAccessToken(accessTokenValue); - String authorizedParty = decodedToken.getClaimAsString(AUTHORIZED_PARTY_CLAIM); String userId = decodedToken.getClaimAsString(customerIdField); - if (StringUtils.equals(this.clientId, authorizedParty) && userId != null) { + if (userId != null) { LOG.debug("Mapped user ID using field '{}': '{}'", customerIdField, userId); + updateUserFromTokenStrategy.updateUserFromToken(userId, decodedToken); oAuth2AccessToken = storeAuthenticationForUser(userId, occClientId, decodedToken); } else { LOG.warn("No user ID found in access token for field: '{}' and the configured authorized party. Make sure your IDP configuration is correct!", customerIdField); @@ -172,6 +188,7 @@ protected Jwt decodeAccessToken(String accessTokenValue) throws JwtException { private OAuth2AccessToken storeAuthenticationForUser(String userId, String oAuth2ClientId, Jwt decodedToken) { assert isNotBlank(oAuth2ClientId); assert isNotBlank(userId); + assert Objects.nonNull(decodedToken); // OAuth2 Request ClientDetails clientDetails = clientDetailsService.loadClientByClientId(oAuth2ClientId); @@ -208,26 +225,53 @@ private OAuth2AccessToken storeAuthenticationForUser(String userId, String oAuth private UsernamePasswordAuthenticationToken createUsernamePasswordAuthenticationToken(String userId) { try { UserDetails userDetails = userDetailsService.loadUserByUsername(userId); - return new UsernamePasswordAuthenticationToken(userId, null, userDetails.getAuthorities()); + Collection authorities = userDetails.getAuthorities(); + return new UsernamePasswordAuthenticationToken(userId, null, authorities); } catch (UsernameNotFoundException e) { LOG.warn("Login attempt for unknown user '{}'!", userId); throw new BadCredentialsException("Invalid credentials!"); } } - protected void configureValidationForJwtDecoder(NimbusJwtDecoder decoder) { - OAuth2TokenValidator audienceValidator = new AudienceValidator(audience); - OAuth2TokenValidator withIssuer = JwtValidators.createDefaultWithIssuer(issuer); - OAuth2TokenValidator withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator); - decoder.setJwtValidator(withAudience); - } - private synchronized void initJwtDecoder() { - if (this.jwtDecoder == null) { + if (isNotBlank(jwksUrl)) { + LOG.info("Initializing JWT decoder with JwkSetUri '{}'", jwksUrl); + this.jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwksUrl).build(); + } else { + LOG.info("Initializing JWT decoder with OIDC issuer location '{}'", issuer); this.jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuer); - if (this.jwtDecoder instanceof NimbusJwtDecoder) { - configureValidationForJwtDecoder((NimbusJwtDecoder) this.jwtDecoder); - } } + + if (this.jwtDecoder instanceof NimbusJwtDecoder) { + configureValidatorsForJwtDecoder((NimbusJwtDecoder) this.jwtDecoder); + } + } + + private void configureValidatorsForJwtDecoder(NimbusJwtDecoder decoder) { + List> validators = new ArrayList<>(); + + validators.add(new JwtTimestampValidator()); + + if (isNotBlank(this.issuer)) { + validators.add(new JwtIssuerValidator(this.issuer)); + } + + if (isNotBlank(this.audience)) { + validators.add(new AudienceValidator(this.audience)); + } + + if (isNotBlank(this.clientId)) { + validators.add(new JwtClaimValidator<>(AUTHORIZED_PARTY_CLAIM, this.clientId::equalsIgnoreCase)); + } + + if (isNotBlank(this.scope)) { + validators.add(new JwtClaimValidator<>(SCOPE_CLAIM, this.scope::equalsIgnoreCase)); + } + + for (String requiredClaim : this.requiredClaims) { + validators.add(new JwtClaimValidator<>(requiredClaim, Objects::nonNull)); + } + + decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators)); } } diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/NoUpdateUserFromTokenStrategy.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/NoUpdateUserFromTokenStrategy.java new file mode 100644 index 0000000..372868b --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/NoUpdateUserFromTokenStrategy.java @@ -0,0 +1,9 @@ +package tools.sapcx.commerce.sso.user; + +import org.springframework.security.oauth2.jwt.Jwt; + +public class NoUpdateUserFromTokenStrategy implements UpdateUserFromTokenStrategy { + @Override + public void updateUserFromToken(String userId, Jwt token) { + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/UpdateUserFromTokenStrategy.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/UpdateUserFromTokenStrategy.java new file mode 100644 index 0000000..ed7d08c --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/user/UpdateUserFromTokenStrategy.java @@ -0,0 +1,7 @@ +package tools.sapcx.commerce.sso.user; + +import org.springframework.security.oauth2.jwt.Jwt; + +public interface UpdateUserFromTokenStrategy { + void updateUserFromToken(String userId, Jwt token); +} From 6b3bd80488735f04b2a502ad9a0779b55445f338 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Wed, 24 Sep 2025 21:44:08 +0200 Subject: [PATCH 05/17] add rebel.xml files to gitignore list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6badfdc..1b39e82 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ consolestorage/ .idea/ idea-module-files/ *.iml +rebel.xml com.springsource.sts.config.flow.prefs org.springframework.ide.eclipse.beans.core.prefs org.springframework.ide.eclipse.core.prefs From df8a5e65af780571c3e4bcff1a021202dd3af035 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Wed, 24 Sep 2025 21:45:05 +0200 Subject: [PATCH 06/17] add blueprint for code formatting on storefront --- build.gradle.kts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b3aa001..6a574fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.diffplug.spotless") version("6.8.0") + id("com.diffplug.spotless") version("6.25.0") } repositories { @@ -9,7 +9,6 @@ repositories { spotless { val importOrderConfigFile = project.file("core-customize/conventions/eclipse.importorder") val javaFormatterConfigFile = project.file("core-customize/conventions/eclipse-formatter-settings.xml") - java { target("core-customize/hybris/bin/custom/sapcxtools/**/*.java") targetExclude("core-customize/hybris/bin/custom/sapcxtools/**/gensrc/**") @@ -19,4 +18,14 @@ spotless { trimTrailingWhitespace() endWithNewline() } + + //val frontendFormatterConfigFile = project.file("js-storefront/*/.prettierrc") + //format("frontend") { + // target( + // "js-storefront/*/src/**/*.scss", + // "js-storefront/*/src/**/*.ts", + // "js-storefront/*/src/**/*.html" + // ) + // prettier("2.5.1").configFile(frontendFormatterConfigFile) + //} } \ No newline at end of file From 0b13957b851d889815b12ed58c29612e6d0bd489 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Wed, 24 Sep 2025 21:46:45 +0200 Subject: [PATCH 07/17] update SAP commerce version to 2211.43 --- core-customize/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-customize/manifest.json b/core-customize/manifest.json index b2b870d..1317c25 100644 --- a/core-customize/manifest.json +++ b/core-customize/manifest.json @@ -50,7 +50,7 @@ ] } ], - "commerceSuiteVersion": "2211.24", + "commerceSuiteVersion": "2211.43", "extensionPacks": [], "extensions": [], "properties": [], From f6c19d075ef74dbbfef755da5a8803e531a91b8a Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Thu, 25 Sep 2025 11:20:34 +0200 Subject: [PATCH 08/17] increase gradle version to 9.1.0 --- core-customize/build.gradle.kts | 12 ++++++------ gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core-customize/build.gradle.kts b/core-customize/build.gradle.kts index d65b803..5c04b16 100644 --- a/core-customize/build.gradle.kts +++ b/core-customize/build.gradle.kts @@ -6,9 +6,9 @@ import java.time.Instant import java.util.Base64 plugins { - id("sap.commerce.build") version("3.7.1") - id("sap.commerce.build.ccv2") version("3.7.1") - id("de.undercouch.download") version("4.1.2") + id("sap.commerce.build") version("4.0.0") + id("sap.commerce.build.ccv2") version("4.0.0") + id("de.undercouch.download") version("5.5.0") } val DEPENDENCY_FOLDER = "../dependencies" @@ -58,8 +58,8 @@ if (project.hasProperty("SAPCX_ARTEFACT_BASEURL") && project.hasProperty("SAPCX_ } tasks.register("generateLocalProperties") { - comment = "FILE WAS GENERATED AT " + Instant.now() - outputFile = project.file("hybris/config/local.properties") + comment = "GENERATED AT " + Instant.now() + destinationFile = project.file("hybris/config/local.properties") property("hybris.optional.config.dir", project.file("hybris/config/local-config").absolutePath) doLast { mkdir(project.file("hybris/config/local-config/")) @@ -126,7 +126,7 @@ mapOf( tasks.register("generateLocalDeveloperProperties") { dependsOn(symlinkConfigTask) comment = "my.properties - add your own local development configuration parameters here" - outputFile = project.file("hybris/config/local-config/99-local.properties") + destinationFile = project.file("hybris/config/local-config/99-local.properties") onlyIf { !project.file("hybris/config/local-config/99-local.properties").exists() } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..d706aba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 518924bf398ae0cde9420be751343bdef60b5f03 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Thu, 25 Sep 2025 11:43:00 +0200 Subject: [PATCH 09/17] fix build errors with software download --- .../backwards-compatibility-java11.yml | 78 ------------------- .../backwards-compatibility-java17.yml | 2 +- core-customize/build.gradle.kts | 4 +- 3 files changed, 3 insertions(+), 81 deletions(-) delete mode 100644 .github/workflows/backwards-compatibility-java11.yml diff --git a/.github/workflows/backwards-compatibility-java11.yml b/.github/workflows/backwards-compatibility-java11.yml deleted file mode 100644 index b12bd84..0000000 --- a/.github/workflows/backwards-compatibility-java11.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Backwards Compatibility (JDK 11) - -on: - push: - branches: [develop] - paths: - - "core-customize/hybris/bin/custom/sapcxtools/**/*.java" - - "core-customize/hybris/bin/custom/sapcxtools/**/*-beans.xml" - - "core-customize/hybris/bin/custom/sapcxtools/**/*-items.xml" - - "core-customize/hybris/bin/custom/sapcxtools/**/extensioninfo.xml" - - "core-customize/hybris/bin/custom/sapcxtools/**/external-dependencies.xml" - pull_request: - branches: [main, develop] - schedule: - - cron: "0 15 * * 0,3" - workflow_dispatch: - -jobs: - compatibility: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - version: ["2105.26"] - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 - with: - distribution: "adopt" - java-version: "11" - cache: "gradle" - - name: Validate Gradle wrapper - uses: gradle/actions/wrapper-validation@v3 - - name: Set up cache for SAP artifacts - uses: actions/cache@v4 - with: - key: sap-artifacts-cache-${{ hashFiles('core-customize/manifest.json') }}-${{ matrix.version }} - path: dependencies - - name: Configure SAP version - run: | - sed -i 's/\"commerceSuiteVersion\": \"[^\"]*\"/\"commerceSuiteVersion\": \"${{ matrix.version }}\"/' core-customize/manifest.json - - name: SAP download config - env: - SAPCX_ARTEFACT_BASEURL: ${{ secrets.SAPCX_ARTEFACT_BASEURL }} - SAPCX_ARTEFACT_USER: ${{ secrets.SAPCX_ARTEFACT_USER }} - SAPCX_ARTEFACT_PASSWORD: ${{ secrets.SAPCX_ARTEFACT_PASSWORD }} - shell: bash - run: | - mkdir -p ${HOME}/.gradle - echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV - echo "SAPCX_ARTEFACT_BASEURL=${SAPCX_ARTEFACT_BASEURL}" >> ${HOME}/.gradle/gradle.properties - echo "SAPCX_ARTEFACT_USER=${SAPCX_ARTEFACT_USER}" >> ${HOME}/.gradle/gradle.properties - echo "SAPCX_ARTEFACT_PASSWORD=${SAPCX_ARTEFACT_PASSWORD}" >> ${HOME}/.gradle/gradle.properties - - name: SAP Commerce environment - run: | - echo "HYBRIS_OPT_CONFIG_DIR=$GITHUB_WORKSPACE/core-customize/hybris/config/local-config" >> $GITHUB_ENV - echo "HYBRIS_BIN_DIR=$GITHUB_WORKSPACE/core-customize/hybris/bin" >> $GITHUB_ENV - echo "HYBRIS_CONF_DIR=$GITHUB_WORKSPACE/core-customize/hybris/config" >> $GITHUB_ENV - echo "HYBRIS_LOG_DIR=$GITHUB_WORKSPACE/core-customize/hybris/log" >> $GITHUB_ENV - - name: Bootstrap platform - run: ./gradlew setupLocalDevelopment - - name: Build platform - run: ./gradlew yall - - name: Run unit tests - run: | - cat $GITHUB_WORKSPACE/ci/config/testing-unit.properties > $HYBRIS_CONF_DIR/local-config/99-local.properties - ./gradlew yunittests - - # Target never fails, therefore, check for test errors and fail if errors were found - for file in $HYBRIS_LOG_DIR/junit/test-results/unit/*.xml; do if [ "$(grep -cE '<(error|failure)' "${file}")" -gt 0 ]; then exit 1; fi; done - - name: Run integration tests - run: | - cat $GITHUB_WORKSPACE/ci/config/testing-integration.properties > $HYBRIS_CONF_DIR/local-config/99-local.properties - ./gradlew yintegrationtests - - # Target never fails, therefore, check for test errors and fail if errors were found - for file in $HYBRIS_LOG_DIR/junit/test-results/integration/*.xml; do if [ "$(grep -cE '<(error|failure)' "${file}")" -gt 0 ]; then exit 1; fi; done diff --git a/.github/workflows/backwards-compatibility-java17.yml b/.github/workflows/backwards-compatibility-java17.yml index 683edaf..3bd2046 100644 --- a/.github/workflows/backwards-compatibility-java17.yml +++ b/.github/workflows/backwards-compatibility-java17.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - version: ["2205.25"] + version: ["2211.43"] steps: - uses: actions/checkout@v4 - name: Set up JDK 17 diff --git a/core-customize/build.gradle.kts b/core-customize/build.gradle.kts index 5c04b16..6aa0e6e 100644 --- a/core-customize/build.gradle.kts +++ b/core-customize/build.gradle.kts @@ -25,7 +25,7 @@ if (project.hasProperty("SAPCX_ARTEFACT_BASEURL") && project.hasProperty("SAPCX_ val COMMERCE_VERSION = CCV2.manifest.commerceSuiteVersion tasks.register("downloadPlatform") { - src(BASEURL + "/hybris-commerce-suite/${COMMERCE_VERSION}.zip") + src(BASEURL + "/commerce/hybris-commerce-suite-${COMMERCE_VERSION}.zip") dest(file("${DEPENDENCY_FOLDER}/hybris-commerce-suite-${COMMERCE_VERSION}.zip")) header("Authorization", "Basic ${AUTHORIZATION}") overwrite(false) @@ -42,7 +42,7 @@ if (project.hasProperty("SAPCX_ARTEFACT_BASEURL") && project.hasProperty("SAPCX_ if (CCV2.manifest.extensionPacks.any{"hybris-commerce-integrations".equals(it.name)}) { val INTEXTPACK_VERSION = CCV2.manifest.extensionPacks.first{"hybris-commerce-integrations".equals(it.name)}.version tasks.register("downloadIntExtPack") { - src(BASEURL + "/hybris-commerce-integrations/${INTEXTPACK_VERSION}.zip") + src(BASEURL + "/integration/hybris-commerce-integrations-${INTEXTPACK_VERSION}.zip") dest(file("${DEPENDENCY_FOLDER}/hybris-commerce-integrations-${INTEXTPACK_VERSION}.zip")) header("Authorization", "Basic ${AUTHORIZATION}") overwrite(false) From f173ae35c8de8ef164a5a8c4244128937113e1e5 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Thu, 25 Sep 2025 11:47:53 +0200 Subject: [PATCH 10/17] trigger build and tests with changes to manifest.json --- .../backwards-compatibility-java17.yml | 1 + .github/workflows/buildandtest.yml | 1 + .github/workflows/dependency-check.yml | 1 + core-customize/manifest.json | 84 +++++++++---------- 4 files changed, 45 insertions(+), 42 deletions(-) diff --git a/.github/workflows/backwards-compatibility-java17.yml b/.github/workflows/backwards-compatibility-java17.yml index 3bd2046..bb626d6 100644 --- a/.github/workflows/backwards-compatibility-java17.yml +++ b/.github/workflows/backwards-compatibility-java17.yml @@ -4,6 +4,7 @@ on: push: branches: [develop] paths: + - "core-customize/manifest.json" - "core-customize/hybris/bin/custom/sapcxtools/**/*.java" - "core-customize/hybris/bin/custom/sapcxtools/**/*-beans.xml" - "core-customize/hybris/bin/custom/sapcxtools/**/*-items.xml" diff --git a/.github/workflows/buildandtest.yml b/.github/workflows/buildandtest.yml index dd263bc..d6a25cd 100644 --- a/.github/workflows/buildandtest.yml +++ b/.github/workflows/buildandtest.yml @@ -4,6 +4,7 @@ on: push: branches: [develop] paths: + - "core-customize/manifest.json" - "core-customize/hybris/bin/custom/sapcxtools/**/*.java" - "core-customize/hybris/bin/custom/sapcxtools/**/*-beans.xml" - "core-customize/hybris/bin/custom/sapcxtools/**/*-items.xml" diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index b72baa4..30360ab 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -4,6 +4,7 @@ on: push: branches: [develop] paths: + - "core-customize/manifest.json" - "core-customize/hybris/bin/custom/sapcxtools/**/*.java" - "core-customize/hybris/bin/custom/sapcxtools/**/*-beans.xml" - "core-customize/hybris/bin/custom/sapcxtools/**/*-items.xml" diff --git a/core-customize/manifest.json b/core-customize/manifest.json index 1317c25..026d23f 100644 --- a/core-customize/manifest.json +++ b/core-customize/manifest.json @@ -1,4 +1,45 @@ { + "commerceSuiteVersion": "2211.43", + "extensionPacks": [], + "extensions": [], + "properties": [], + "storefrontAddons": [], + "useCloudExtensionPack": false, + "useConfig": { + "extensions": { + "exclude": [], + "location": "hybris/config/localextensions.xml" + }, + "properties": [{ + "location": "hybris/config/cloud/common.properties" + }, + { + "aspect": "api", + "location": "hybris/config/cloud/aspect/api.properties" + }, + { + "aspect": "backoffice", + "location": "hybris/config/cloud/aspect/backoffice.properties" + }, + { + "aspect": "backgroundProcessing", + "location": "hybris/config/cloud/aspect/backgroundprocessing.properties" + }, + { + "aspect": "admin", + "location": "hybris/config/cloud/aspect/admin.properties" + }, + { + "location": "hybris/config/cloud/persona/development.properties", + "persona": "development" + } + ], + "tests": { + "extensions": ["sapcommercetoolkit", "sapcxbackoffice", "sapcxreporting", "sapcxsearch", "sapcxsinglesignon"], + "annotations": ["UnitTests", "IntegrationTests"], + "packages": ["tools.sapcx.commerce.*"] + } + }, "aspects": [{ "name": "backoffice", "webapps": [{ @@ -49,46 +90,5 @@ } ] } - ], - "commerceSuiteVersion": "2211.43", - "extensionPacks": [], - "extensions": [], - "properties": [], - "storefrontAddons": [], - "useCloudExtensionPack": false, - "useConfig": { - "extensions": { - "exclude": [], - "location": "hybris/config/localextensions.xml" - }, - "properties": [{ - "location": "hybris/config/cloud/common.properties" - }, - { - "aspect": "api", - "location": "hybris/config/cloud/aspect/api.properties" - }, - { - "aspect": "backoffice", - "location": "hybris/config/cloud/aspect/backoffice.properties" - }, - { - "aspect": "backgroundProcessing", - "location": "hybris/config/cloud/aspect/backgroundprocessing.properties" - }, - { - "aspect": "admin", - "location": "hybris/config/cloud/aspect/admin.properties" - }, - { - "location": "hybris/config/cloud/persona/development.properties", - "persona": "development" - } - ], - "tests": { - "extensions": ["sapcommercetoolkit", "sapcxbackoffice", "sapcxreporting", "sapcxsearch", "sapcxsinglesignon"], - "annotations": ["UnitTests", "IntegrationTests"], - "packages": ["tools.sapcx.commerce.*"] - } - } + ] } From 35bea864b9b4e381a73fc07d13916cf1939e4fc7 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 9 Dec 2025 22:24:17 +0100 Subject: [PATCH 11/17] optimize configuration --- .../config/cloud/aspect/admin.properties | 8 +- .../hybris/config/cloud/aspect/api.properties | 5 +- .../aspect/backgroundprocessing.properties | 2 +- .../config/cloud/aspect/backoffice.properties | 1 + .../hybris/config/cloud/common.properties | 52 ++----- .../hybris/config/cloud/local-dev.properties | 138 ------------------ .../hybris/config/cloud/local-sso.properties | 54 ------- .../cloud/persona/development.properties | 18 ++- .../cloud/persona/production.properties | 8 + .../config/cloud/persona/staging.properties | 12 ++ 10 files changed, 52 insertions(+), 246 deletions(-) delete mode 100644 core-customize/hybris/config/cloud/local-dev.properties delete mode 100644 core-customize/hybris/config/cloud/local-sso.properties create mode 100644 core-customize/hybris/config/cloud/persona/production.properties create mode 100644 core-customize/hybris/config/cloud/persona/staging.properties diff --git a/core-customize/hybris/config/cloud/aspect/admin.properties b/core-customize/hybris/config/cloud/aspect/admin.properties index be4c7d1..6177578 100644 --- a/core-customize/hybris/config/cloud/aspect/admin.properties +++ b/core-customize/hybris/config/cloud/aspect/admin.properties @@ -1,7 +1 @@ -# cloud/aspect/admin.properties - -system.unlocking.disabled=false - -backoffice.fill.typefacade.cache.on.startup=false -backoffice.solr.search.index.autoinit=false -commerceservices.org.unit.path.generation.enabled=false +# cloud/aspect/admin.properties \ No newline at end of file diff --git a/core-customize/hybris/config/cloud/aspect/api.properties b/core-customize/hybris/config/cloud/aspect/api.properties index 411f94a..01da958 100644 --- a/core-customize/hybris/config/cloud/aspect/api.properties +++ b/core-customize/hybris/config/cloud/aspect/api.properties @@ -1,4 +1 @@ -# cloud/aspect/api.properties - -xss.filter.enabled=false -xss.filter.header.X-Frame-Options= +# cloud/aspect/api.properties \ No newline at end of file diff --git a/core-customize/hybris/config/cloud/aspect/backgroundprocessing.properties b/core-customize/hybris/config/cloud/aspect/backgroundprocessing.properties index e1337e7..3e1d66b 100644 --- a/core-customize/hybris/config/cloud/aspect/backgroundprocessing.properties +++ b/core-customize/hybris/config/cloud/aspect/backgroundprocessing.properties @@ -1,4 +1,4 @@ # cloud/aspect/backgroundprocessing.properties -cluster.node.groups=integration,yHotfolderCandidate,backgroundProcessing # make sure to set the nodeGroup property of your jobs/cronjobs to backgroundProcessing! +cluster.node.groups=integration,yHotfolderCandidate,backgroundProcessing \ No newline at end of file diff --git a/core-customize/hybris/config/cloud/aspect/backoffice.properties b/core-customize/hybris/config/cloud/aspect/backoffice.properties index eac7d5b..11b763d 100644 --- a/core-customize/hybris/config/cloud/aspect/backoffice.properties +++ b/core-customize/hybris/config/cloud/aspect/backoffice.properties @@ -1 +1,2 @@ # cloud/aspect/backoffice.properties + diff --git a/core-customize/hybris/config/cloud/common.properties b/core-customize/hybris/config/cloud/common.properties index 9bbdfc7..70de6a8 100644 --- a/core-customize/hybris/config/cloud/common.properties +++ b/core-customize/hybris/config/cloud/common.properties @@ -6,44 +6,21 @@ promotions.legacy.mode=true #*****************************************************************************# # Sane Defaults - lang.packs=en,de -sqlserver.enableLimitSupportForSQLServer2012=true - -# Suppress spurious logs -log4j2.logger.nosolr.name=de.hybris.platform.solrfacetsearch.indexer.cron.SolrIndexerJob -log4j2.logger.nosolr.level=WARN -log4j2.logger.nglogin.name=com.hybris.cockpitng.composer.LoginFormComposer -log4j2.logger.nglogin.level=WARN -log4j2.logger.cleanup.name=de.hybris.platform.retention.job.AfterRetentionCleanupJobPerformable -log4j2.logger.cleanup.level=WARN -log4j2.logger.cleanuplogs.name=de.hybris.platform.jobs.maintenance.impl.CleanUpLogsStrategy -log4j2.logger.cleanuplogs.level=WARN +installed.tenants= +spring.profiles.active= + +# Enable ANSI colors in logs (does not work on Windows) +ansi.colors=true # Disable audit, decrease session timeout -# https://help.sap.com/viewer/1be46286b36a4aa48205be5a96240672/SHIP/en-US/d6794b766aea4783b829988dc587f978.html auditing.enabled=false default.session.timeout=360 +# Deactivate deprecated addonfilter addonfilter.active=false - -csrf.allowed.url.patterns=/[^/]+(/[^?]*)+(sop/response)$,/[^/]+(/[^?]*)+(merchant_callback)$,/[^/]+(/[^?]*)+(hop/response)$,/[^/]+(/[^?]*)+(language)$,/[^/]+(/[^?]*)+(currency)$,/(events)$ occ.rewrite.overlapping.paths.enabled=true -# junit tenant is only necessary if you execute tests in the Commerce Cloud build pipeline -# (i.e. when you configure `tests` / `webTests` in `manifest.json`) -installed.tenants= - -# spring profiles to be activated -spring.profiles.active= - -# disable system update during runtime -system.unlocking.disabled=true - -# prevent tasks / cronjobs from starting until server is fully up -# -> faster startup -task.polling.startup.delay.enabled=true - # PaymentInfo validates that the card number is plausible. # Setting paymentinfo.creditcard.checknumber=false will prevent PaymentInfo.createItem() # from throwing an exception if the card number is invalid. @@ -62,15 +39,20 @@ solr.server.mode=cloud # Change the limit on the number of facet values returned per facet. # This needs to be greater than the number of categories in the system. -facet.limit.default=500 +facet.limit.default=1000 #*****************************************************************************# -# Spartacus B2B - +# Reset public URLs based on cloud variables webroot.commercewebservices.http=${ccv2.services.api.url.0}/occ webroot.commercewebservices.https=${ccv2.services.api.url.0}/occ commercewebservices.oauth2.tokenUrl=${ccv2.services.api.url.0}/authorizationserver/oauth/token +corsfilter.default.allowedOrigins=${ccv2.services.api.url.0},${ccv2.services.jsapps.url.0} + +# Deactivate broken XSS Filter (also breaks CORS filter) +xss.filter.enabled=false +xss.filter.header.X-Frame-Options= + #*****************************************************************************# # Update System Configuration @@ -84,11 +66,5 @@ update.importEssentialData.enabled=true update.localizeTypes.enabled=true update.rebuildLucenesearchIndexes.enabled=false -# Import project data of these extensions (comma-separated list) -update.executeProjectData.extensionName.list=sapcommercetoolkit - # Import localized impex files automatically sapcommercetoolkit.impeximport.environment.supportlocalizedfiles=true - -# Limit impex workers to 1 in order to ensure correct order of impex import statements into database -impex.import.workers=1 diff --git a/core-customize/hybris/config/cloud/local-dev.properties b/core-customize/hybris/config/cloud/local-dev.properties deleted file mode 100644 index 5f7c415..0000000 --- a/core-customize/hybris/config/cloud/local-dev.properties +++ /dev/null @@ -1,138 +0,0 @@ -# local-dev.properties - -#*****************************************************************************# -# Sane Defaults - -initialpassword.admin=nimda -spring.profiles.active=sapcommercetools-fake-localmails,sapcommercetools-modelservice-failurelogging -installed.tenants= - -testclasses.extensions=sapcommercetoolkit,sapcxbackoffice,sapcxreporting,sapcxenvconfig,sapcxsearch -testclasses.packages=tools.sapcx.commerce.* -testclasses.suppress.junit.tenant=true -testclasses.reportdir=${HYBRIS_LOG_DIR}/junit/test-results - -#*****************************************************************************# -# Endpoints for local development -# Note: Host services must always end with a slash! - -host.service.backend=https://localhost:${tomcat.ssl.port}/ -host.service.frontend=https://localhost:4200/ - -# Web root mapping -hac.webroot=/hac - -# Fake managed properties for CCv2 services -ccv2.services.api.url.0=${host.service.backend}occ -ccv2.services.backoffice.url.0=${host.service.backend}backoffice -ccv2.services.jsapps.url.0=${host.service.frontend} - -# Java heap size -tomcat.mem=-Xms4G -Xmx4G - -#ref. hybris/bin/platform/project.properties -tomcat.generaloptions=${tomcat.jdkmodules.config} \ - -Djava.locale.providers=COMPAT,CLDR \ - ${tomcat.mem} \ - -XX:+UseStringDeduplication \ - -ea \ - -Dcatalina.base=%CATALINA_BASE% \ - -Dcatalina.home=%CATALINA_HOME% \ - -Dfile.encoding=UTF-8 \ - -Djava.util.logging.config.file=jdk_logging.properties \ - -Djava.io.tmpdir="${HYBRIS_TEMP_DIR}" - -#ref. hybris/bin/platform/resources/advanced.properties -standalone.javaoptions=${tomcat.mem} -Djava.locale.providers=COMPAT,CLDR - -# faster startup / update system etc -backoffice.fill.typefacade.cache.on.startup=false -backoffice.solr.search.index.autoinit=false - -# disable audit for faster init/update -auditing.enabled=false - -# supress spurious update-backofficeIndex-CronJob message if index not available -log4j2.logger.indexer.name=de.hybris.platform.solrfacetsearch.indexer.strategies.impl.AbstractIndexerStrategy -log4j2.logger.indexer.level=WARN - -system.unlocking.disabled=false - -# don't start cronjobs automatically -cronjob.timertask.loadonstartup=false - -#*****************************************************************************# -# Mail settings - -mail.from=no-reply@cxdev.me -mail.replyto=no-reply@cxdev.me -mail.smtp.server=localhost -mail.smtp.port=25 -mail.use.tls=false - -#*****************************************************************************# -# Local Cloud Hotfolder - -cluster.node.groups=integration,yHotfolderCandidate,backgroundProcessing -azure.hotfolder.storage.account.connection-string=UseDevelopmentStorage=true -azure.hotfolder.storage.account.name=devstoreaccount1 -cloud.hotfolder.default.images.root.url=http://127.0.0.1:10000/${azure.hotfolder.storage.account.name}/${azure.hotfolder.storage.container.name}/${tenantId}/images -cloud.hotfolder.default.images.media.folder.qualifier=azureimages - -#*****************************************************************************# -# Local Solr Cloud - -# Start local solr server for development -solrserver.instances.cloud.autostart=true - -# Force the Default configuration to use cloud mode + default zookeeper endpoint -solr.config.Default.mode=CLOUD -solr.config.Default.urls=localhost:9983 - -#*****************************************************************************# -# Development Mode - -addonfilter.active=true -ansi.colors=true -build.development.mode=true -development.mode=true -tomcat.development.mode=true -flexible.search.exception.show.query.details=true - -# Backoffice Dev Settings -backoffice.session.timeout=3600 -# faster startup / update system etc -backoffice.fill.typefacade.cache.on.startup=false -backoffice.solr.search.index.autoinit=false -# https://help.sap.com/viewer/5c9ea0c629214e42b727bf08800d8dfa/latest/en-US/8b48115b86691014991ad2131153834f.html -backoffice.cockpitng.development.mode=true -# disable all caching for development -backoffice.cockpitng.additionalResourceLoader.enabled=true -backoffice.cockpitng.uifactory.cache.enabled=false -backoffice.cockpitng.widgetclassloader.resourcecache.enabled=false -backoffice.cockpitng.resourceloader.resourcecache.enabled=false -cockpitng.widgetclassloader.resourcecache.enabled=false - -backoffice.sass.source.map.enabled=true - -# backoffice hot deployment -backoffice.cockpitng.hotDeployment.enabled=true - -# reset backoffice config every start, change to "login" for active backoffice development -# https://help.sap.com/viewer/5c9ea0c629214e42b727bf08800d8dfa/latest/en-US/8b7db2c286691014af65a6a21e6d5933.html -backoffice.cockpitng.reset.triggers=start -backoffice.cockpitng.reset.scope=widgets,cockpitConfig - -# supress spurious update-backofficeIndex-CronJob message if index not available -log4j2.logger.indexer.name=de.hybris.platform.solrfacetsearch.indexer.strategies.impl.AbstractIndexerStrategy -log4j2.logger.indexer.level=WARN - -# Always import Sample & Test data locally -sapcommercetoolkit.impeximport.environment.isdevelopment=true - -# Deactivate DisableLoginForImportedUserInterceptor for development purposes -disable.login.for.imported.user.interceptor.enabled=false - -# Logging configuration for local development -log4j2.logger.sso.name=tools.sapcx.commerce.sso -log4j2.logger.sso.level=DEBUG diff --git a/core-customize/hybris/config/cloud/local-sso.properties b/core-customize/hybris/config/cloud/local-sso.properties deleted file mode 100644 index 5a2c595..0000000 --- a/core-customize/hybris/config/cloud/local-sso.properties +++ /dev/null @@ -1,54 +0,0 @@ -# local-sso.properties - -# Keystore setup to avoid SSL errors -tomcat.ssl.keystore.path=${HYBRIS_CONFIG_DIR}/../../../certificates/local.cxdev.me.p12 -tomcat.ssl.keystore.password=123456 -tomcat.ssl.keystore.alias=local.cxdev.me - -#*****************************************************************************# -# Endpoints for local development -# Note: Host services must always end with a slash! - -host.service.backend=https://customer.local.cxdev.me:${tomcat.ssl.port}/ -host.service.frontend=https://customer.local.cxdev.me:4200/ - -# Web root mapping -hac.login.singlesignon.redirect=/samlsinglesignon/saml${hac.webroot} - -# Fake managed properties for CCv2 services -ccv2.services.api.url.0=${host.service.backend}occ -ccv2.services.backoffice.url.0=${host.service.backend}backoffice -ccv2.services.jsapps.url.0=${host.service.frontend} - -#*****************************************************************************# -# Single sign on for local development - -# Sample SSO Integration for Backoffice/SmartEdit -sso.metadata.location=file:${HYBRIS_CONFIG_DIR}/../../../certificates/cxdev_eu_auth0_com-metadata.xml -sso.keystore.location=file:${HYBRIS_CONFIG_DIR}/../../../certificates/local.cxdev.me.p12 -sso.keystore.password=123456 -sso.keystore.default.certificate.alias=local.cxdev.me -sso.keystore.privatekey.alias=local.cxdev.me -sso.keystore.privatekey.password=123456 -sso.entity.id=urn:sapcxtools.eu.auth0.com -sso.relyingPartyRegistration.registrationId=cxdev -sso.redirect.url=${host.service.backend} - -# Sample User Attribute mapping -sso.userid.attribute.key=http://schemas.auth0.com/username -sso.firstname.attribute.key=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname -sso.lastname.attribute.key=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname -sso.usergroup.attribute.key=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups - -# Sample User Group mapping -sso.database.usergroup.mapping=false -sso.mapping.CXDEV_BACKOFFICE_ADMIN.usertype=Employee -sso.mapping.CXDEV_BACKOFFICE_ADMIN.groups=admingroup -sso.mapping.CXDEV_BACKOFFICE_CMSEDITOR.usertype=Employee -sso.mapping.CXDEV_BACKOFFICE_CMSEDITOR.groups=cmseditorgroup -sso.mapping.CXDEV_BACKOFFICE_CMSMANAGER.usertype=Employee -sso.mapping.CXDEV_BACKOFFICE_CMSMANAGER.groups=cmsmanagergroup -sso.mapping.CXDEV_BACKOFFICE_PIMEDITOR.usertype=Employee -sso.mapping.CXDEV_BACKOFFICE_PIMEDITOR.groups=producteditorgroup -sso.mapping.CXDEV_BACKOFFICE_PIMMANAGER.usertype=Employee -sso.mapping.CXDEV_BACKOFFICE_PIMMANAGER.groups=productmanagergroup diff --git a/core-customize/hybris/config/cloud/persona/development.properties b/core-customize/hybris/config/cloud/persona/development.properties index 44e130d..3304a3f 100644 --- a/core-customize/hybris/config/cloud/persona/development.properties +++ b/core-customize/hybris/config/cloud/persona/development.properties @@ -1,13 +1,23 @@ # cloud/persona/development.properties +# Avoid sending outbound emails by storing them in the database +spring.profiles.active=sapcommercetools-fake-localmails,sapcommercetools-modelservice-failurelogging +sapcommercetoolkit.fakes.htmlEmailService.localstorage.method=database + # Mock Payment provider sop.post.url=${ccv2.services.api.url.0}/acceleratorservices/sop-mock/process -# Anybody should be able to access mock payment provider -corsfilter.acceleratorservices.allowedOrigins=* + +# Deactivate DisableLoginForImportedUserInterceptor for development purposes +disable.login.for.imported.user.interceptor.enabled=false # Sample & Test data sapcommercetoolkit.impeximport.environment.importsampledata=true sapcommercetoolkit.impeximport.environment.importtestdata=true -# Deactivate DisableLoginForImportedUserInterceptor for development purposes -disable.login.for.imported.user.interceptor.enabled=false +#*****************************************************************************# +# Testing + +testclasses.extensions=sapcommercetoolkit,sapcxbackoffice,sapcxreporting,sapcxenvconfig,sapcxsearch,sapcxsinglesignon,sapcxssoauth0 +testclasses.packages=tools.sapcx.commerce.* +testclasses.suppress.junit.tenant=true +testclasses.reportdir=${HYBRIS_LOG_DIR}/junit/test-results diff --git a/core-customize/hybris/config/cloud/persona/production.properties b/core-customize/hybris/config/cloud/persona/production.properties new file mode 100644 index 0000000..322d1e7 --- /dev/null +++ b/core-customize/hybris/config/cloud/persona/production.properties @@ -0,0 +1,8 @@ +# cloud/persona/production.properties + +# Activate DisableLoginForImportedUserInterceptor for development purposes +disable.login.for.imported.user.interceptor.enabled=true + +# Sample & Test data +sapcommercetoolkit.impeximport.environment.importsampledata=false +sapcommercetoolkit.impeximport.environment.importtestdata=false diff --git a/core-customize/hybris/config/cloud/persona/staging.properties b/core-customize/hybris/config/cloud/persona/staging.properties new file mode 100644 index 0000000..39e1491 --- /dev/null +++ b/core-customize/hybris/config/cloud/persona/staging.properties @@ -0,0 +1,12 @@ +# cloud/persona/staging.properties + +# Avoid sending outbound emails by storing them in the database +spring.profiles.active=sapcommercetools-fake-localmails,sapcommercetools-modelservice-failurelogging +sapcommercetoolkit.fakes.htmlEmailService.localstorage.method=database + +# Deactivate DisableLoginForImportedUserInterceptor for development purposes +disable.login.for.imported.user.interceptor.enabled=false + +# Sample & Test data +sapcommercetoolkit.impeximport.environment.importsampledata=true +sapcommercetoolkit.impeximport.environment.importtestdata=false From 3d5340f560181623d80a3615024ab6c01673a56c Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 9 Dec 2025 23:24:12 +0100 Subject: [PATCH 12/17] extract Auth0 implementation from sapcxsinglesignon extension --- .../sapcxtools/sapcxsinglesignon/.project | 2 +- .../sapcxtools/sapcxsinglesignon/LICENSE.md | 2 +- .../sapcxtools/sapcxsinglesignon/README.md | 31 +--- .../sapcxsinglesignon/extensioninfo.xml | 3 +- .../external-dependencies.xml | 61 +------- .../sapcxsinglesignon/project.properties | 42 +----- .../web/spring/idp-initiated-login-spring.xml | 0 .../resources/sapcxsinglesignon-spring.xml | 48 ------- .../commerce/sso/auth0/actions/Actions.java | 45 ------ .../sso/auth0/actions/AssignRoleAction.java | 38 ----- .../sso/auth0/actions/CreateUserAction.java | 82 ----------- .../sso/auth0/actions/FetchRoleAction.java | 38 ----- .../sso/auth0/actions/FetchUserAction.java | 38 ----- .../auth0/actions/PasswordResetUrlAction.java | 38 ----- .../sso/auth0/actions/RemoveRoleAction.java | 39 ------ .../sso/auth0/actions/RemoveUserAction.java | 35 ----- .../commerce/sso/auth0/actions/SdkAction.java | 76 ---------- .../actions/SdkConfigurationService.java | 90 ------------ .../sso/auth0/actions/UpdateUserAction.java | 54 ------- .../Auth0CustomerMetadataPopulator.java | 111 --------------- .../replication/Auth0CustomerPopulator.java | 35 ----- .../Auth0CustomerReplicationStrategy.java | 132 ------------------ .../JwtAccessTokenVerificationFilter.java | 5 - .../AllCustomerReplicationFilter.java | 12 -- .../CustomerReplicationException.java | 22 --- .../replication/CustomerReplicationHook.java | 16 --- .../CustomerReplicationInterceptor.java | 55 -------- .../CustomerReplicationStrategy.java | 11 -- .../config/ExtensionConfigurationTests.java | 1 + 29 files changed, 16 insertions(+), 1146 deletions(-) rename core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/{sapcxsinglesignon => samlsinglesignon}/web/spring/idp-initiated-login-spring.xml (100%) delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java delete mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/.project b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/.project index 6708223..c94914c 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/.project +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/.project @@ -1,6 +1,6 @@ - sapcxtemplate + sapcxsinglesignon diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md index 1172c18..3524aee 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/README.md @@ -9,14 +9,10 @@ The functionality covers a filter for the OCC layer, that handles the validation The filter will be executed before the actual spring security chain of the OCC extension, creating a valid token in the local token storage for the authenticated user. -The extension also ships a customer replication strategy, that can be activated to create users within -Auth0, whenever a customer object is created or changed within SAP commerce cloud. - ### How to activate and use To activate the functionality, one needs to set the configuration parameters accordingly for each environment, -especially the flags `sapcxsinglesignon.filter.enabled`, `sapcxsinglesignon.replicate.creation.enabled`, and -`sapcxsinglesignon.replicate.removal.enabled` which are set to `false` by default. +especially the flag `sapcxsinglesignon.filter.enabled`, which is set to `false` by default. Also, the IDP should be configured to use the SAP Commerce OCC endpoint as audience, and provide the following information within the access token, as they are required by the filter: @@ -142,17 +138,6 @@ export class CustomLogoutGuard extends LogoutGuard { } ``` -For the customer replication, one can add additional populators to the `auth0CustomerConverter` converter bean. -This can be easily done using the `modifyPopulatorList` bean notation: - -```xml - - - - - -``` - ### Configuration parameters | Parameter | Type | Description | @@ -166,21 +151,9 @@ This can be easily done using the `modifyPopulatorList` bean notation: | sapcxsinglesignon.filter.idp.requiredClaims | String | comma-separated list of required claims for a valid token (optional) | | sapcxsinglesignon.filter.idp.clientid | String | the client ID of the application (required) | | sapcxsinglesignon.filter.idp.claim.id | String | claim name used for user ID mapping (default: email) | -| sapcxsinglesignon.replicate.enabled | Boolean | specifies whether the replication is active or not (default: false) | -| sapcxsinglesignon.replicate.creation.enabled | Boolean | specifies whether the user creation is enabled or not (default: false) | -| sapcxsinglesignon.replicate.removal.enabled | Boolean | specifies whether the user removal is enabled or not (default: false) | -| sapcxsinglesignon.auth0.management.api.audience | String | the audience for your machine-to-machine application (required) | -| sapcxsinglesignon.auth0.management.api.clientid | String | the auth0 client ID for your machine-to-machine application (required) | -| sapcxsinglesignon.auth0.management.api.clientsecret | String | the auth0 client secret for your machine-to-machine application (required) | -| sapcxsinglesignon.auth0.customer.connection | String | the authentication connection for customers (default: "Username-Password-Authentication") | -| customer.metadata.prefix | String | the prefix for application metadata for customers (required, default: commerce) | -| sapcxsinglesignon.auth0.customer.role | String | the role to assign to newly created customer accounts | -| sapcxsinglesignon.auth0.customer.requireemailverification | String | specifies if the user needs to verify their email (default: false) | -| sapcxsinglesignon.auth0.customer.requirepasswordverification | String | specifies if the user needs to verify their password (default: false) | -| sapcxsinglesignon.auth0.customer.useblockedstatus | Boolean | specifies if the user shall be blocked when disabled in SAP Commerce (default: false) | ## License _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/extensioninfo.xml index 0adb7df..0d95515 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/extensioninfo.xml @@ -1,8 +1,9 @@ - + + diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/external-dependencies.xml index ce433a2..942dc9b 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/external-dependencies.xml @@ -3,79 +3,28 @@ 4.0.0 tools.sapcx sapcxsinglesignon - 4.1.1 + 4.4.0 jar org.springframework.security spring-security-saml2-service-provider - 5.6.1 + 5.8.16 org.springframework.security spring-security-oauth2-core - 5.6.9 + 5.8.16 org.springframework.security spring-security-oauth2-jose - 5.6.9 + 5.8.16 com.nimbusds nimbus-jose-jwt - 9.37.3 - - - com.auth0 - auth0 - 2.9.0 - - - com.auth0 - java-jwt - 4.4.0 - - - com.squareup.okhttp3 - okhttp - 4.12.0 - - - com.squareup.okhttp3 - logging-interceptor - 4.12.0 - - - com.squareup.okio - okio - 3.7.0 - - - com.squareup.okio - okio-jvm - 3.7.0 - runtime - - - org.jetbrains.kotlin - kotlin-stdlib - 1.9.21 - - - org.jetbrains.kotlin - kotlin-stdlib-common - 1.9.21 - - - org.jetbrains - annotations - 24.1.0 - - - net.jodah - failsafe - 2.4.4 + 10.6 diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties index 4d42e49..cd10239 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/project.properties @@ -1,10 +1,12 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2023 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context sapcxsinglesignon.application-context=sapcxsinglesignon-spring.xml -samlsinglesignon.additionalWebSpringConfigs.sapcxsinglesignon=classpath:/sapcxsinglesignon/web/spring/idp-initiated-login-spring.xml + +# Enhance samlsinglesignon web context +samlsinglesignon.additionalWebSpringConfigs.sapcxsinglesignon=classpath:/samlsinglesignon/web/spring/idp-initiated-login-spring.xml ############################################################################## # General settings @@ -19,10 +21,6 @@ samlsinglesignon.additionalWebSpringConfigs.sapcxsinglesignon=classpath:/sapcxsi # filter.idp.requiredClaims - comma-separated list of required claims for a valid token (optional) # filter.idp.clientid - the client ID of the application (required) # filter.idp.claim.id - claim name used for user ID mapping (default: email) -# -# replicate.enabled - specifies whether the user replication is enabled or not (default: false) -# replicate.creation.enabled - specifies whether the user creation is enabled or not (default: false) -# replicate.removal.enabled - specifies whether the user removal is enabled or not (default: false) ############################################################################## sapcxsinglesignon.filter.enabled=false sapcxsinglesignon.filter.login.userClientId= @@ -36,35 +34,3 @@ sapcxsinglesignon.filter.idp.clientid= sapcxsinglesignon.filter.idp.claim.id=email sapcxsinglesignon.backoffice.defaulturl=/ - -sapcxsinglesignon.replicate.enabled=false -sapcxsinglesignon.replicate.creation.enabled=false -sapcxsinglesignon.replicate.removal.enabled=false - -############################################################################## -# Auth0 settings -# -# management.api.domain - the domain for your machine-to-machine application (required) -# management.api.audience - the audience for your machine-to-machine application (required) -# management.api.clientid - the auth0 client ID for your machine-to-machine application (required) -# management.api.clientsecret - the auth0 client secret for your machine-to-machine application (required) -# -# customer.connection - the authentication connection for customers (required) -# (default: "Username-Password-Authentication") -# customer.metadata.prefix - the prefix for application metadata for customers (required, default: commerce) -# customer.role - the role to assign to newly created customer accounts (optional) -# customer.requireemailverification - specifies if the user needs to verify their email (default: false) -# customer.requirepasswordverification - specifies if the user needs to verify their password (default: false) -# customer.useblockedstatus - specifies if the user shall be blocked when disabled in SAP Commerce (default: false) -############################################################################## -sapcxsinglesignon.auth0.management.api.domain= -sapcxsinglesignon.auth0.management.api.audience= -sapcxsinglesignon.auth0.management.api.clientid= -sapcxsinglesignon.auth0.management.api.clientsecret= - -sapcxsinglesignon.auth0.customer.connection=Username-Password-Authentication -sapcxsinglesignon.auth0.customer.metadata.prefix=commerce -sapcxsinglesignon.auth0.customer.role= -sapcxsinglesignon.auth0.customer.requireemailverification=false -sapcxsinglesignon.auth0.customer.requirepasswordverification=false -sapcxsinglesignon.auth0.customer.useblockedstatus=false diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon/web/spring/idp-initiated-login-spring.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/samlsinglesignon/web/spring/idp-initiated-login-spring.xml similarity index 100% rename from core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon/web/spring/idp-initiated-login-spring.xml rename to core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/samlsinglesignon/web/spring/idp-initiated-login-spring.xml diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml index a5ff15f..f40f3ed 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/resources/sapcxsinglesignon-spring.xml @@ -13,7 +13,6 @@ - @@ -28,51 +27,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java deleted file mode 100644 index 335b73b..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java +++ /dev/null @@ -1,45 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.roles.Role; -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.core.model.user.CustomerModel; - -public interface Actions { - static Role getRole(String roleName) throws Auth0Exception { - return FetchRoleAction.getRole(roleName); - } - - static void assignRole(Role role, User user) throws Auth0Exception { - AssignRoleAction.assignRole(role, user); - } - - static void removeRole(Role role, User user) throws Auth0Exception { - RemoveRoleAction.removeRole(role, user); - } - - static User getUser(String email) throws Auth0Exception { - return FetchUserAction.getUser(email); - } - - static User createUser(CustomerModel customer) throws Auth0Exception { - return CreateUserAction.createUser(customer); - } - - static User updateUser(User user, CustomerModel customer) throws Auth0Exception { - return UpdateUserAction.updateUser(user, customer); - } - - static void removeUser(User user, String customerId) throws Auth0Exception { - RemoveUserAction.removeUser(user, customerId); - } - - static String getPasswordResetUrl(User user) throws Auth0Exception { - return getPasswordResetUrl(user, false); - } - - static String getPasswordResetUrl(User user, boolean markEmailAsVerified) throws Auth0Exception { - return PasswordResetUrlAction.getPasswordResetUrl(user, markEmailAsVerified); - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java deleted file mode 100644 index f872871..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java +++ /dev/null @@ -1,38 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.List; -import java.util.Map; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.roles.Role; -import com.auth0.json.mgmt.users.User; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class AssignRoleAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(AssignRoleAction.class); - - static void assignRole(Role role, User user) throws Auth0Exception { - new AssignRoleAction().execute(Map.of("role", role, "user", user)); - } - - private AssignRoleAction() { - // Avoid instantiation - } - - @Override - public Void execute(Map parameter) throws Auth0Exception { - Role role = getWithType(parameter, "role", Role.class); - User user = getWithType(parameter, "user", User.class); - - try { - submit(managementAPI().users().addRoles(user.getId(), List.of(role.getId()))); - LOG.debug("Assigned role with name '{}' to user with email '{}'.", role.getName(), user.getEmail()); - return null; - } catch (Auth0Exception exception) { - LOG.warn(String.format("Could not assign role '%s' to user with email '%s'. ", role.getName(), user.getEmail()), exception); - throw exception; - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java deleted file mode 100644 index 6504a6e..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java +++ /dev/null @@ -1,82 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.dto.converter.Converter; - -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.RandomStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class CreateUserAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(CreateUserAction.class); - - static User createUser(CustomerModel customer) throws Auth0Exception { - return new CreateUserAction().execute(Map.of("customer", customer)); - } - - private CreateUserAction() { - // Avoid instantiation - } - - @Override - public User execute(Map parameter) throws Auth0Exception { - CustomerModel customer = getWithType(parameter, "customer", CustomerModel.class); - String customerId = customer.getUid(); - - User user = null; - try { - Converter customerConverter = getCustomerConverter(); - User userInfo = customerConverter.convert(customer); - - if (requireEmailVerification()) { - userInfo.setEmailVerified(false); - userInfo.setVerifyEmail(true); - } - - if (useBlockedStatusForDisabledCustomers()) { - userInfo.setBlocked(BooleanUtils.isNotFalse(customer.isLoginDisabled())); - } - - // Add one time information for creation process - userInfo.setConnection(getCustomerConnection()); - userInfo.setPassword(getRandomPassword()); - if (requirePasswordVerification()) { - userInfo.setVerifyPassword(true); - } - - user = fetch(managementAPI().users().create(userInfo)); - return user; - } catch (Auth0Exception exception) { - LOG.debug(String.format("Create user with ID '%s' failed!", customerId), exception); - throw exception; - } finally { - LOG.debug("Create user with ID '{}' resulted in: '{}'.", customerId, user != null ? user.getId() : "-error-"); - } - } - - private char[] getRandomPassword() { - List passwordCharacters = new ArrayList<>(32); - - String alphaNumericChars = RandomStringUtils.randomAlphanumeric(26); - for (int i = 0; i < alphaNumericChars.length(); i++) { - passwordCharacters.add(String.valueOf(alphaNumericChars.charAt(i))); - } - - String specialChars = RandomStringUtils.random(6, '!', '@', '#', '$', '%', '^', '&', '*'); - for (int i = 0; i < specialChars.length(); i++) { - passwordCharacters.add(String.valueOf(specialChars.charAt(i))); - } - - Collections.shuffle(passwordCharacters); - return String.join("", passwordCharacters).toCharArray(); - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java deleted file mode 100644 index f787168..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java +++ /dev/null @@ -1,38 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.Map; - -import com.auth0.client.mgmt.filter.RolesFilter; -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.roles.Role; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class FetchRoleAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(FetchRoleAction.class); - - static Role getRole(String roleName) throws Auth0Exception { - return new FetchRoleAction().execute(Map.of("role", roleName)); - } - - private FetchRoleAction() { - // Avoid instantiation - } - - @Override - public Role execute(Map parameter) throws Auth0Exception { - String roleName = getWithType(parameter, "role", String.class); - Role role = null; - try { - RolesFilter roleByName = new RolesFilter().withName(roleName); - role = fetchFirst(managementAPI().roles().list(roleByName)); - return role; - } catch (Auth0Exception exception) { - LOG.debug(String.format("Search for role with name '%s' could not be executed!", roleName), exception); - throw exception; - } finally { - LOG.debug("Lookup for existing role with name '{}' resulted in: '{}'", roleName, role != null ? role.getId() : "-not found-"); - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java deleted file mode 100644 index a40428a..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java +++ /dev/null @@ -1,38 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.Map; - -import com.auth0.client.mgmt.filter.UserFilter; -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.users.User; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class FetchUserAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(FetchUserAction.class); - - static User getUser(String id) throws Auth0Exception { - return new FetchUserAction().execute(Map.of("id", id)); - } - - private FetchUserAction() { - // Avoid instantiation - } - - @Override - public User execute(Map parameter) throws Auth0Exception { - String userId = getWithType(parameter, "id", String.class); - User user = null; - try { - UserFilter userById = new UserFilter().withQuery(userId); - user = fetchFirst(managementAPI().users().list(userById)); - return user; - } catch (Auth0Exception exception) { - LOG.debug(String.format("Search for user with ID '%s' could not be executed!", userId), exception); - throw exception; - } finally { - LOG.debug("Lookup for existing user with ID '{}' resulted in: '{}'", userId, user != null ? user.getId() : "-not found-"); - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java deleted file mode 100644 index 3e58a69..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java +++ /dev/null @@ -1,38 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.Map; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.tickets.PasswordChangeTicket; -import com.auth0.json.mgmt.users.User; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class PasswordResetUrlAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(PasswordResetUrlAction.class); - - static String getPasswordResetUrl(User user, boolean markEmailAsVerified) throws Auth0Exception { - return new PasswordResetUrlAction().execute(Map.of("user", user, "markEmailAsVerified", markEmailAsVerified)); - } - - private PasswordResetUrlAction() { - // Avoid instantiation - } - - @Override - public String execute(Map parameter) throws Auth0Exception { - User user = getWithType(parameter, "user", User.class); - Boolean markEmailAsVerified = getWithType(parameter, "markEmailAsVerified", Boolean.class); - try { - PasswordChangeTicket ticket = new PasswordChangeTicket(user.getId()); - ticket.setMarkEmailAsVerified(markEmailAsVerified); - - PasswordChangeTicket passwordChangeTicket = fetch(managementAPI().tickets().requestPasswordChange(ticket)); - return passwordChangeTicket.getTicket(); - } catch (Auth0Exception exception) { - LOG.debug(String.format("Get password reset token for existing user with ID '{}' failed!", user.getEmail()), exception); - throw exception; - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java deleted file mode 100644 index f461a6a..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java +++ /dev/null @@ -1,39 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.List; -import java.util.Map; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.roles.Role; -import com.auth0.json.mgmt.users.User; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class RemoveRoleAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(RemoveRoleAction.class); - - static void removeRole(Role role, User user) throws Auth0Exception { - new RemoveRoleAction().execute(Map.of("role", role, "user", user)); - } - - private RemoveRoleAction() { - // Avoid instantiation - } - - @Override - public Void execute(Map parameter) throws Auth0Exception { - Role role = getWithType(parameter, "role", Role.class); - User user = getWithType(parameter, "user", User.class); - - try { - submit(managementAPI().users().removeRoles(user.getId(), List.of(role.getId()))); - LOG.debug("Remove role with name '{}' from user with email '{}'.", role.getName(), user.getEmail()); - return null; - } catch (Auth0Exception exception) { - LOG.warn(String.format("Could not remove role '%s' from user with email '%s'. ", role.getName(), user.getEmail()), exception); - throw exception; - } - } - -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java deleted file mode 100644 index d9be1e2..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java +++ /dev/null @@ -1,35 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.Map; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.users.User; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class RemoveUserAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(RemoveUserAction.class); - - static void removeUser(User user, String customerId) throws Auth0Exception { - new RemoveUserAction().execute(Map.of("user", user, "customerId", customerId)); - } - - private RemoveUserAction() { - // Avoid instantiation - } - - @Override - public Void execute(Map parameter) throws Auth0Exception { - User user = getWithType(parameter, "user", User.class); - String customerId = getWithType(parameter, "customerId", String.class); - try { - fetch(managementAPI().users().delete(user.getId())); - LOG.debug("Delete user with ID '{}' was successful.", customerId); - return null; - } catch (Auth0Exception exception) { - LOG.debug(String.format("Delete user with ID '%s' failed!", customerId), exception); - throw exception; - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java deleted file mode 100644 index 147e60d..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java +++ /dev/null @@ -1,76 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.Map; - -import com.auth0.client.mgmt.ManagementAPI; -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.Page; -import com.auth0.json.mgmt.users.User; -import com.auth0.net.Request; - -import de.hybris.platform.core.Registry; -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.dto.converter.Converter; - -/** - * Performs an action on the Auth0 SDK. - * - * @param response type of the action - */ -@FunctionalInterface -interface SdkAction { - R execute(Map requestParameter) throws Auth0Exception; - - default ManagementAPI managementAPI() throws Auth0Exception { - return getConfigurationService().getManagementAPI(); - } - - default void submit(Request request) throws Auth0Exception { - request.execute(); - } - - default T fetch(Request request) throws Auth0Exception { - return request.execute().getBody(); - } - - default T fetchFirst(Request> request) throws Auth0Exception { - return request.execute().getBody().getItems().stream().findFirst().orElse(null); - } - - default T getWithType(Map requestParameter, String parameterName, Class returnType) { - Object value = requestParameter.get(parameterName); - return (returnType.isInstance(value)) ? returnType.cast(value) : null; - } - - default String getCustomerIdField() { - return getConfigurationService().getCustomerIdField(); - } - - default String getCustomerConnection() { - return getConfigurationService().getCustomerConnection(); - } - - default Converter getCustomerConverter() { - return getConfigurationService().getCustomerConverter(); - } - - default boolean requireEmailVerification() { - return getConfigurationService().requireEmailVerification(); - } - - default boolean requirePasswordVerification() { - return getConfigurationService().requirePasswordVerification(); - } - - default boolean useBlockedStatusForDisabledCustomers() { - return getConfigurationService().useBlockedStatusForDisabledCustomers(); - } - - default String getAuthClientId() { - return getConfigurationService().getAuthClientId(); - } - - private SdkConfigurationService getConfigurationService() { - return Registry.getApplicationContext().getBean(SdkConfigurationService.class); - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java deleted file mode 100644 index d4714d2..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java +++ /dev/null @@ -1,90 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import com.auth0.client.auth.AuthAPI; -import com.auth0.client.mgmt.ManagementAPI; -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.config.ConfigurationService; -import de.hybris.platform.servicelayer.dto.converter.Converter; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SdkConfigurationService { - private static final Logger LOG = LoggerFactory.getLogger(SdkConfigurationService.class); - private static final String AUTH0_MANAGEMENT_API_DOMAIN = "sapcxsinglesignon.auth0.management.api.domain"; - private static final String AUTH0_MANAGEMENT_API_AUDIENCE = "sapcxsinglesignon.auth0.management.api.audience"; - private static final String AUTH0_AUTH_API_CLIENTID = "sapcxsinglesignon.auth0.auth.api.clientid"; - private static final String AUTH0_MANAGEMENT_API_CLIENTID = "sapcxsinglesignon.auth0.management.api.clientid"; - private static final String AUTH0_MANAGEMENT_API_CLIENTSECRET = "sapcxsinglesignon.auth0.management.api.clientsecret"; - private static final String AUTH0_CUSTOMER_CONNECTION = "sapcxsinglesignon.auth0.customer.connection"; - private static final String AUTH0_CUSTOMER_ID_FIELD = "sapcxsinglesignon.auth0.customer.idfield"; - private static final String AUTH0_REQUIRE_EMAIL_VERIFICATION = "sapcxsinglesignon.auth0.customer.requireemailverification"; - private static final String AUTH0_REQUIRE_PASSWORD_VERIFICATION = "sapcxsinglesignon.auth0.customer.requirepasswordverification"; - private static final String AUTH0_USE_BLOCKEDSTATUS = "sapcxsinglesignon.auth0.customer.useblockedstatus"; - - private ConfigurationService configurationService; - private Converter customerConverter; - - public SdkConfigurationService(ConfigurationService configurationService, Converter customerConverter) { - this.configurationService = configurationService; - this.customerConverter = customerConverter; - } - - public ManagementAPI getManagementAPI() throws Auth0Exception { - LOG.debug("Create new Auth0 ManagementAPI.", getAudience()); - return ManagementAPI.newBuilder(getDomain(), getManagementAccessToken()).build(); - } - - private String getManagementAccessToken() throws Auth0Exception { - LOG.debug("Fetch access token for management API for audience: {}", getAudience()); - AuthAPI authAPI = AuthAPI.newBuilder(getDomain(), getManagementClientId(), getManagementClientSecret()).build(); - return authAPI.requestToken(getAudience()).execute().getBody().getAccessToken(); - } - - private String getAudience() { - return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_AUDIENCE); - } - - private String getDomain() { - return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_DOMAIN); - } - - public String getManagementClientId() { - return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_CLIENTID); - } - - public String getManagementClientSecret() { - return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_CLIENTSECRET); - } - - public String getCustomerConnection() { - return configurationService.getConfiguration().getString(AUTH0_CUSTOMER_CONNECTION); - } - - public String getCustomerIdField() { - return configurationService.getConfiguration().getString(AUTH0_CUSTOMER_ID_FIELD); - } - - public Converter getCustomerConverter() { - return customerConverter; - } - - public boolean requireEmailVerification() { - return configurationService.getConfiguration().getBoolean(AUTH0_REQUIRE_EMAIL_VERIFICATION, false); - } - - public boolean requirePasswordVerification() { - return configurationService.getConfiguration().getBoolean(AUTH0_REQUIRE_PASSWORD_VERIFICATION, false); - } - - public boolean useBlockedStatusForDisabledCustomers() { - return configurationService.getConfiguration().getBoolean(AUTH0_USE_BLOCKEDSTATUS, false); - } - - public String getAuthClientId() { - return configurationService.getConfiguration().getString(AUTH0_AUTH_API_CLIENTID, null); - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java deleted file mode 100644 index 6a5b7a9..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java +++ /dev/null @@ -1,54 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.actions; - -import java.util.Map; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.dto.converter.Converter; - -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class UpdateUserAction implements SdkAction { - private static final Logger LOG = LoggerFactory.getLogger(UpdateUserAction.class); - - static User updateUser(User user, CustomerModel customer) throws Auth0Exception { - return new UpdateUserAction().execute(Map.of("user", user, "customer", customer)); - } - - private UpdateUserAction() { - // Avoid instantiation - } - - @Override - public User execute(Map parameter) throws Auth0Exception { - User user = getWithType(parameter, "user", User.class); - CustomerModel customer = getWithType(parameter, "customer", CustomerModel.class); - String customerId = customer.getUid(); - try { - Converter customerConverter = getCustomerConverter(); - User userInfo = customerConverter.convert(customer); - - if (requireEmailVerification() && !StringUtils.equals(user.getEmail(), userInfo.getEmail())) { - userInfo.setEmailVerified(false); - userInfo.setVerifyEmail(true); - } - - if (useBlockedStatusForDisabledCustomers()) { - userInfo.setBlocked(BooleanUtils.isNotFalse(customer.isLoginDisabled())); - } - - user = fetch(managementAPI().users().update(user.getId(), userInfo)); - return user; - } catch (Auth0Exception exception) { - LOG.debug(String.format("Search for user with ID '%s' failed!", customer.getUid()), exception); - throw exception; - } finally { - LOG.debug("Update information for existing user with ID '{}' resulted in: '{}'", customerId, user != null ? user.getId() : "-error-"); - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java deleted file mode 100644 index 6692d2b..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java +++ /dev/null @@ -1,111 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.replication; - -import static java.lang.String.format; -import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; -import static org.apache.commons.lang3.BooleanUtils.isTrue; -import static org.apache.commons.lang3.StringUtils.isBlank; - -import java.util.HashMap; -import java.util.Map; - -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.converters.Populator; -import de.hybris.platform.core.model.user.AddressModel; -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.dto.converter.ConversionException; - -public class Auth0CustomerMetadataPopulator implements Populator { - private String metadataPrefix; - private String metadataKeyFormat; - - public Auth0CustomerMetadataPopulator(String metadataPrefix) { - this.metadataPrefix = metadataPrefix; - this.metadataKeyFormat = isBlank(metadataPrefix) ? "%1$s" : "%0$s_%1$s"; - } - - @Override - public void populate(CustomerModel source, User target) throws ConversionException { - emptyIfNull(source.getAddresses()).stream() - .filter(this::isContactAddress) - .findFirst() - .map(this::convertAddressToMetadata) - .ifPresent(target::setAppMetadata); - } - - protected boolean isContactAddress(AddressModel address) { - return isTrue(address.getContactAddress()); - } - - private Map convertAddressToMetadata(AddressModel address) { - Map metadata = new HashMap<>(16); - addStandardContactFields(address, metadata); - addStandardAddressFields(address, metadata); - addStandardTelecommunicationFields(address, metadata); - return metadata; - } - - protected void addStandardContactFields(AddressModel address, Map metadata) { - if (address.getTitle() != null) { - metadata.put(getKey("contact_title"), address.getTitle().getCode()); - } - if (address.getCompany() != null) { - metadata.put(getKey("contact_company"), address.getCompany()); - } - if (address.getDepartment() != null) { - metadata.put(getKey("contact_department"), address.getDepartment()); - } - } - - protected void addStandardAddressFields(AddressModel address, Map metadata) { - if (address.getLine1() != null) { - metadata.put(getKey("contact_streetname"), address.getLine1()); - } - if (address.getLine2() != null) { - metadata.put(getKey("contact_streetnumber"), address.getLine2()); - } - if (address.getBuilding() != null) { - metadata.put(getKey("contact_building"), address.getBuilding()); - } - if (address.getAppartment() != null) { - metadata.put(getKey("contact_appartment"), address.getAppartment()); - } - if (address.getPostalcode() != null) { - metadata.put(getKey("contact_postalcode"), address.getPostalcode()); - } - if (address.getTown() != null) { - metadata.put(getKey("contact_city"), address.getTown()); - } - if (address.getDistrict() != null) { - metadata.put(getKey("contact_district"), address.getDepartment()); - } - if (address.getRegion() != null) { - metadata.put(getKey("contact_region"), address.getRegion().getIsocodeShort()); - } - if (address.getCountry() != null) { - metadata.put(getKey("contact_country"), address.getCountry().getIsocode()); - } - if (address.getPobox() != null) { - metadata.put(getKey("contact_pobox"), address.getPobox()); - } - } - - protected void addStandardTelecommunicationFields(AddressModel address, Map metadata) { - if (address.getCellphone() != null) { - metadata.put(getKey("contact_cellphone"), address.getCellphone()); - } - if (address.getPhone1() != null) { - metadata.put(getKey("contact_phone1"), address.getPhone1()); - } - if (address.getPhone2() != null) { - metadata.put(getKey("contact_phone2"), address.getPhone2()); - } - if (address.getFax() != null) { - metadata.put(getKey("contact_fax"), address.getFax()); - } - } - - private String getKey(String fieldName) { - return format(metadataKeyFormat, metadataPrefix, fieldName); - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java deleted file mode 100644 index 4788027..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java +++ /dev/null @@ -1,35 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.replication; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.commerceservices.strategies.CustomerNameStrategy; -import de.hybris.platform.converters.Populator; -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.dto.converter.ConversionException; - -public class Auth0CustomerPopulator implements Populator { - private CustomerNameStrategy customerNameStrategy; - - public Auth0CustomerPopulator(CustomerNameStrategy customerNameStrategy) { - this.customerNameStrategy = customerNameStrategy; - } - - @Override - public void populate(CustomerModel source, User target) throws ConversionException { - target.setEmail(source.getContactEmail()); - target.setNickname(source.getCustomerID()); - target.setName(source.getName()); - - String[] nameParts = customerNameStrategy.splitName(source.getName()); - if (nameParts.length == 2) { - if (isNotBlank(nameParts[0])) { - target.setGivenName(nameParts[0]); - } - if (isNotBlank(nameParts[1])) { - target.setFamilyName(nameParts[1]); - } - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java deleted file mode 100644 index 539037e..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java +++ /dev/null @@ -1,132 +0,0 @@ -package tools.sapcx.commerce.sso.auth0.replication; - -import static org.apache.commons.collections4.ListUtils.emptyIfNull; - -import java.util.List; - -import com.auth0.exception.Auth0Exception; -import com.auth0.json.mgmt.roles.Role; -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.user.UserService; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import tools.sapcx.commerce.sso.auth0.actions.Actions; -import tools.sapcx.commerce.sso.replication.CustomerReplicationException; -import tools.sapcx.commerce.sso.replication.CustomerReplicationHook; -import tools.sapcx.commerce.sso.replication.CustomerReplicationStrategy; - -public class Auth0CustomerReplicationStrategy implements CustomerReplicationStrategy { - private static final Logger LOG = LoggerFactory.getLogger(Auth0CustomerReplicationStrategy.class); - - private UserService userService; - private List customerReplicationHooks; - private String auth0RoleForCustomers; - private boolean isCreationEnabled; - private boolean isRemovalEnabled; - - public Auth0CustomerReplicationStrategy( - UserService userService, - List customerReplicationHooks, - String auth0RoleForCustomers, - boolean isCreationEnabled, - boolean isRemovalEnabled) { - this.userService = userService; - this.customerReplicationHooks = emptyIfNull(customerReplicationHooks); - this.auth0RoleForCustomers = auth0RoleForCustomers; - this.isCreationEnabled = isCreationEnabled; - this.isRemovalEnabled = isRemovalEnabled; - } - - @Override - public void replicate(CustomerModel customer) { - if (userService.isAnonymousUser(customer)) { - LOG.debug("Anonymous user replication is disabled by convention."); - return; - } - - User user = createOrUpdateUser(customer); - if (user != null) { - updateUserRoles(user, !customer.isLoginDisabled()); - } - } - - private User createOrUpdateUser(CustomerModel customer) { - String customerId = customer.getUid(); - try { - User user = Actions.getUser(customerId); - if (user != null) { - LOG.debug("User for provided customer ID '{}' exists: '{}'.", customerId, user.getId()); - User updatedUser = Actions.updateUser(user, customer); - customerReplicationHooks.forEach(hook -> hook.customerSuccessfullyUpdated(customer, updatedUser)); - return updatedUser; - } else if (!isCreationEnabled) { - LOG.debug("Customer creation is disabled by configuration."); - return null; - } else { - LOG.debug("User for provided customer ID '{}' does not exist.", customerId); - User createdUser = Actions.createUser(customer); - customerReplicationHooks.forEach(hook -> hook.customerSuccessfullyCreated(customer, createdUser)); - return createdUser; - } - } catch (Auth0Exception exception) { - LOG.debug("Could not replicate customer with ID '{}'. Data may no be in sync and needs to be corrected manually!", customerId); - throw new CustomerReplicationException("Could not replicate customer to Auth0!", exception); - } - } - - private Role updateUserRoles(User user, boolean isLoginEnabled) { - try { - if (StringUtils.isBlank(auth0RoleForCustomers)) { - return null; - } - - Role role = Actions.getRole(auth0RoleForCustomers); - if (role == null) { - return null; - } - - if (isLoginEnabled) { - Actions.assignRole(role, user); - } else { - Actions.removeRole(role, user); - } - return role; - } catch (Auth0Exception exception) { - LOG.debug("Could not synchronize roles for customer ID '{}'. Data may no be in sync and needs to be corrected manually!", user.getEmail()); - throw new CustomerReplicationException("Could not synchronize customer roles to Auth0!", exception); - } - } - - @Override - public void remove(String customerId) { - if (userService.isUserExisting(customerId) && userService.isAnonymousUser(userService.getUserForUID(customerId))) { - LOG.debug("Anonymous user removal is disabled by convention."); - return; - } - - try { - User user = Actions.getUser(customerId); - if (!isRemovalEnabled) { - LOG.debug("Customer removal is disabled by configuration."); - updateUserRoles(user, false); - return; - } - - if (user == null) { - LOG.debug("User for provided customer ID '{}' does not exist! Removal not necessary.", customerId); - } else { - LOG.debug("User for provided customer ID '{}' exists: '{}'. Trigger user removal.", customerId, user.getId()); - Actions.removeUser(user, customerId); - customerReplicationHooks.forEach(hook -> hook.customerSuccessfullyRemoved(customerId)); - } - } catch (Auth0Exception exception) { - LOG.debug("Could not remove customer with ID '{}'! Account needs to be removed manually!", customerId); - throw new CustomerReplicationException("Could not remove customer on Auth0 side!", exception); - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java index a2bb060..d90541a 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/filter/JwtAccessTokenVerificationFilter.java @@ -37,7 +37,6 @@ import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.filter.OncePerRequestFilter; -import tools.sapcx.commerce.sso.replication.CustomerReplicationStrategy; import tools.sapcx.commerce.sso.user.UpdateUserFromTokenStrategy; /** @@ -61,7 +60,6 @@ public class JwtAccessTokenVerificationFilter extends OncePerRequestFilter { private ClientDetailsService clientDetailsService; private UserDetailsService userDetailsService; private UpdateUserFromTokenStrategy updateUserFromTokenStrategy; - private CustomerReplicationStrategy customerReplicationStrategy; private TokenStore tokenStore; private String occClientId; private boolean enabled; @@ -80,7 +78,6 @@ public JwtAccessTokenVerificationFilter( ClientDetailsService clientDetailsService, UserDetailsService userDetailsService, UpdateUserFromTokenStrategy updateUserFromTokenStrategy, - CustomerReplicationStrategy customerReplicationStrategy, TokenStore tokenStore, String occClientId, boolean enabled, @@ -95,7 +92,6 @@ public JwtAccessTokenVerificationFilter( this.clientDetailsService = clientDetailsService; this.userDetailsService = userDetailsService; this.updateUserFromTokenStrategy = updateUserFromTokenStrategy; - this.customerReplicationStrategy = customerReplicationStrategy; this.tokenStore = tokenStore; this.occClientId = occClientId; this.enabled = enabled; @@ -217,7 +213,6 @@ private OAuth2AccessToken storeAuthenticationForUser(String userId, String oAuth return oAuth2AccessToken; } catch (BadCredentialsException e) { - customerReplicationStrategy.remove(userId); throw e; } } diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java deleted file mode 100644 index 88f7e51..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java +++ /dev/null @@ -1,12 +0,0 @@ -package tools.sapcx.commerce.sso.replication; - -import java.util.function.Predicate; - -import de.hybris.platform.core.model.user.CustomerModel; - -public class AllCustomerReplicationFilter implements Predicate { - @Override - public boolean test(CustomerModel customerModel) { - return true; - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java deleted file mode 100644 index ded0bd0..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java +++ /dev/null @@ -1,22 +0,0 @@ -package tools.sapcx.commerce.sso.replication; - -public class CustomerReplicationException extends RuntimeException { - public CustomerReplicationException() { - } - - public CustomerReplicationException(String message) { - super(message); - } - - public CustomerReplicationException(String message, Throwable cause) { - super(message, cause); - } - - public CustomerReplicationException(Throwable cause) { - super(cause); - } - - public CustomerReplicationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java deleted file mode 100644 index 58aa6b2..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java +++ /dev/null @@ -1,16 +0,0 @@ -package tools.sapcx.commerce.sso.replication; - -import com.auth0.json.mgmt.users.User; - -import de.hybris.platform.core.model.user.CustomerModel; - -public interface CustomerReplicationHook { - default void customerSuccessfullyCreated(CustomerModel customer, User createdUser) { - } - - default void customerSuccessfullyUpdated(CustomerModel customer, User updatedUser) { - } - - default void customerSuccessfullyRemoved(String customer) { - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java deleted file mode 100644 index 39e8356..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java +++ /dev/null @@ -1,55 +0,0 @@ -package tools.sapcx.commerce.sso.replication; - -import java.util.function.Predicate; - -import de.hybris.platform.core.model.user.CustomerModel; -import de.hybris.platform.servicelayer.interceptor.InterceptorContext; -import de.hybris.platform.servicelayer.interceptor.InterceptorException; -import de.hybris.platform.servicelayer.interceptor.RemoveInterceptor; -import de.hybris.platform.servicelayer.interceptor.ValidateInterceptor; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CustomerReplicationInterceptor implements ValidateInterceptor, RemoveInterceptor { - private static final Logger LOG = LoggerFactory.getLogger(CustomerReplicationInterceptor.class); - - private CustomerReplicationStrategy customerReplicationStrategy; - private Predicate customerReplicationFilter; - private boolean enabled; - - public CustomerReplicationInterceptor( - CustomerReplicationStrategy customerReplicationStrategy, - Predicate customerReplicationFilter, - boolean enabled) { - this.customerReplicationStrategy = customerReplicationStrategy; - this.customerReplicationFilter = customerReplicationFilter; - this.enabled = enabled; - } - - @Override - public void onValidate(CustomerModel customer, InterceptorContext interceptorContext) { - if (enabled && customer != null) { - try { - if (customerReplicationFilter.test(customer)) { - customerReplicationStrategy.replicate(customer); - } - } catch (RuntimeException e) { - LOG.warn(String.format("Could not replicate customer with ID '%s'. Data may no be in sync and needs to be corrected manually!", customer.getUid()), e); - } - } - } - - @Override - public void onRemove(CustomerModel customer, InterceptorContext interceptorContext) throws InterceptorException { - if (enabled && customer != null) { - try { - if (customerReplicationFilter.test(customer)) { - customerReplicationStrategy.remove(customer.getUid()); - } - } catch (RuntimeException e) { - LOG.warn(String.format("Could not remove customer with ID '%s'! Account needs to be removed manually!", customer.getUid()), e); - } - } - } -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java deleted file mode 100644 index 31390bc..0000000 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java +++ /dev/null @@ -1,11 +0,0 @@ -package tools.sapcx.commerce.sso.replication; - -import javax.annotation.Nonnull; - -import de.hybris.platform.core.model.user.CustomerModel; - -public interface CustomerReplicationStrategy { - void replicate(@Nonnull CustomerModel customer); - - void remove(@Nonnull String customerId); -} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java index 161cf50..1526972 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsinglesignon/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java @@ -14,6 +14,7 @@ public void extensionConfiguration() { .requires("sapcxsinglesignon") .requires("sapcommercetoolkit") .requires("commercewebservices") + .requires("samlsinglesignon") .requires("oauth2") .verify(); } From 1f7c77babdd8fc988258d38a29c8f08186c37c07 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 9 Dec 2025 23:27:39 +0100 Subject: [PATCH 13/17] add new sapcxssoauth0 extension --- .../sapcxtools/sapcxssoauth0/.gitignore | 46 ++++ .../custom/sapcxtools/sapcxssoauth0/.project | 34 +++ .../sapcxtools/sapcxssoauth0/CONTRIBUTING.md | 4 + .../sapcxtools/sapcxssoauth0/LICENSE.md | 201 ++++++++++++++++++ .../custom/sapcxtools/sapcxssoauth0/README.md | 49 +++++ .../sapcxssoauth0/extensioninfo.xml | 7 + .../sapcxssoauth0/external-dependencies.xml | 61 ++++++ .../sapcxssoauth0/project.properties | 45 ++++ .../resources/sapcxssoauth0-spring.xml | 55 +++++ .../commerce/sso/auth0/actions/Actions.java | 45 ++++ .../sso/auth0/actions/AssignRoleAction.java | 38 ++++ .../sso/auth0/actions/CreateUserAction.java | 82 +++++++ .../sso/auth0/actions/FetchRoleAction.java | 38 ++++ .../sso/auth0/actions/FetchUserAction.java | 38 ++++ .../auth0/actions/PasswordResetUrlAction.java | 38 ++++ .../sso/auth0/actions/RemoveRoleAction.java | 39 ++++ .../sso/auth0/actions/RemoveUserAction.java | 35 +++ .../commerce/sso/auth0/actions/SdkAction.java | 76 +++++++ .../actions/SdkConfigurationService.java | 90 ++++++++ .../sso/auth0/actions/UpdateUserAction.java | 54 +++++ .../Auth0CustomerMetadataPopulator.java | 111 ++++++++++ .../replication/Auth0CustomerPopulator.java | 35 +++ .../Auth0CustomerReplicationStrategy.java | 132 ++++++++++++ .../sso/constants/CxSsoAuth0Constants.java | 11 + .../AllCustomerReplicationFilter.java | 12 ++ .../CustomerReplicationException.java | 22 ++ .../replication/CustomerReplicationHook.java | 16 ++ .../CustomerReplicationInterceptor.java | 55 +++++ .../CustomerReplicationStrategy.java | 11 + .../config/ExtensionConfigurationTests.java | 18 ++ 30 files changed, 1498 insertions(+) create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.gitignore create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.project create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/CONTRIBUTING.md create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/LICENSE.md create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/README.md create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/extensioninfo.xml create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/external-dependencies.xml create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/project.properties create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/resources/sapcxssoauth0-spring.xml create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/constants/CxSsoAuth0Constants.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java create mode 100644 core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.gitignore b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.gitignore new file mode 100644 index 0000000..bd0966d --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.gitignore @@ -0,0 +1,46 @@ +# Folders to ignore, whatever the place they are +.DS_Store +.metadata +.settings +classes +testclasses +eclipsebin +gensrc + +# .classpath files and lib folder in sapcxtools to ignore +.classpath +lib/ + +# Filename patterns to ignore +*.class +build.xml +Generated*.java +platformhome.properties +*testclasses.xml +extensioninfo.xsd +*hmc.jar +hmc.xsd +items.xsd +beans.xsd +ruleset.xml +*.log +.pmd +*build.number +base.properties + +# Addon specific copy folders +**/_ui/addons +**/views/addons +**/tld/addons +**/tags/addons +**/messages/addons +**/lib/addons +**/web/addonsrc +**/_ui-src/addons +**/web/addontestsrc +**/web/commonwebsrc/*/ +**/web/webroot/WEB-INF/lib/addon-* +wro_addons.xml + +# Backoffice artifacts +*_bof.jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.project b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.project new file mode 100644 index 0000000..0588f6c --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/.project @@ -0,0 +1,34 @@ + + + sapcxssoauth0 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + + + + 1642058460820 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/CONTRIBUTING.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/CONTRIBUTING.md new file mode 100644 index 0000000..572289d --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/CONTRIBUTING.md @@ -0,0 +1,4 @@ +This repository has a special setup for contributing. + +Please read the [CONTRIBUTING.md from the extensions repository](https://github.com/sapcxtools/workspace/blob/main/CONTRIBUTING.md) which +will guide you through the process. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/LICENSE.md new file mode 100644 index 0000000..9776fe0 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2025] [SAP CX Tools] + + 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. \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/README.md new file mode 100644 index 0000000..c8010c2 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/README.md @@ -0,0 +1,49 @@ +# SAP CX Single-Sign-On (Auth0 Replication) + +The `sapcxssoauth0` extension provides core replication mechanism for synchronizing customer data with +external service and identity providers, in this case Auth0 by Okta. + +## FEATURE DESCRIPTION + +The extension ships a customer replication strategy, that can be activated to create users within +Auth0, whenever a customer object is created or changed within SAP commerce cloud. + +### How to activate and use + +To activate the functionality, one needs to set the configuration parameters accordingly for each environment, +especially the flags `sapcxsinglesignon.replicate.creation.enabled`, and +`sapcxsinglesignon.replicate.removal.enabled` which are set to `false` by default. + +For the customer replication, one can add additional populators to the `auth0CustomerConverter` converter bean. +This can be easily done using the `modifyPopulatorList` bean notation: + +```xml + + + + + +``` + +### Configuration parameters + +| Parameter | Type | Description | +|--------------------------------------------------------------|------|------------------------------------------------------------------------------------------| +| sapcxsinglesignon.replicate.enabled | Boolean | specifies whether the replication is active or not (default: false) | +| sapcxsinglesignon.replicate.creation.enabled | Boolean | specifies whether the user creation is enabled or not (default: false) | +| sapcxsinglesignon.replicate.removal.enabled | Boolean | specifies whether the user removal is enabled or not (default: false) | +| sapcxsinglesignon.auth0.management.api.audience | String | the audience for your machine-to-machine application (required) | +| sapcxsinglesignon.auth0.management.api.clientid | String | the auth0 client ID for your machine-to-machine application (required) | +| sapcxsinglesignon.auth0.management.api.clientsecret | String | the auth0 client secret for your machine-to-machine application (required) | +| sapcxsinglesignon.auth0.customer.connection | String | the authentication connection for customers (default: "Username-Password-Authentication") | +| customer.metadata.prefix | String | the prefix for application metadata for customers (required, default: commerce) | +| sapcxsinglesignon.auth0.customer.role | String | the role to assign to newly created customer accounts | +| sapcxsinglesignon.auth0.customer.requireemailverification | String | specifies if the user needs to verify their email (default: false) | +| sapcxsinglesignon.auth0.customer.requirepasswordverification | String | specifies if the user needs to verify their password (default: false) | +| sapcxsinglesignon.auth0.customer.useblockedstatus | Boolean | specifies if the user shall be blocked when disabled in SAP Commerce (default: false) | + +## License + +_Licensed under the Apache License, Version 2.0, January 2004_ + +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/extensioninfo.xml new file mode 100644 index 0000000..a8bc828 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/extensioninfo.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/external-dependencies.xml new file mode 100644 index 0000000..7679f46 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/external-dependencies.xml @@ -0,0 +1,61 @@ + + 4.0.0 + tools.sapcx + sapcxssoauth0 + 4.4.0 + jar + + + com.auth0 + auth0 + 2.9.0 + + + com.auth0 + java-jwt + 4.4.0 + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + com.squareup.okhttp3 + logging-interceptor + 4.12.0 + + + com.squareup.okio + okio + 3.7.0 + + + com.squareup.okio + okio-jvm + 3.7.0 + runtime + + + org.jetbrains.kotlin + kotlin-stdlib + 1.9.21 + + + org.jetbrains.kotlin + kotlin-stdlib-common + 1.9.21 + + + org.jetbrains + annotations + 24.1.0 + + + net.jodah + failsafe + 2.4.4 + + + diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/project.properties new file mode 100644 index 0000000..06ea34f --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/project.properties @@ -0,0 +1,45 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2025 SAP CX Tools +# ---------------------------------------------------------------------------- + +# Location of the file for the global platform spring application context +sapcxssoauth0.application-context=sapcxssoauth0-spring.xml + +############################################################################## +# General settings +# +# replicate.enabled - specifies whether the user replication is enabled or not (default: false) +# replicate.creation.enabled - specifies whether the user creation is enabled or not (default: false) +# replicate.removal.enabled - specifies whether the user removal is enabled or not (default: false) +############################################################################## +sapcxssoauth0.replicate.enabled=false +sapcxssoauth0.replicate.creation.enabled=false +sapcxssoauth0.replicate.removal.enabled=false + +############################################################################## +# Auth0 settings +# +# management.api.domain - the domain for your machine-to-machine application (required) +# management.api.audience - the audience for your machine-to-machine application (required) +# management.api.clientid - the auth0 client ID for your machine-to-machine application (required) +# management.api.clientsecret - the auth0 client secret for your machine-to-machine application (required) +# +# customer.connection - the authentication connection for customers (required) +# (default: "Username-Password-Authentication") +# customer.metadata.prefix - the prefix for application metadata for customers (required, default: commerce) +# customer.role - the role to assign to newly created customer accounts (optional) +# customer.requireemailverification - specifies if the user needs to verify their email (default: false) +# customer.requirepasswordverification - specifies if the user needs to verify their password (default: false) +# customer.useblockedstatus - specifies if the user shall be blocked when disabled in SAP Commerce (default: false) +############################################################################## +sapcxssoauth0.auth0.management.api.domain= +sapcxssoauth0.auth0.management.api.audience= +sapcxssoauth0.auth0.management.api.clientid= +sapcxssoauth0.auth0.management.api.clientsecret= + +sapcxssoauth0.auth0.customer.connection=Username-Password-Authentication +sapcxssoauth0.auth0.customer.metadata.prefix=commerce +sapcxssoauth0.auth0.customer.role= +sapcxssoauth0.auth0.customer.requireemailverification=false +sapcxssoauth0.auth0.customer.requirepasswordverification=false +sapcxssoauth0.auth0.customer.useblockedstatus=false diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/resources/sapcxssoauth0-spring.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/resources/sapcxssoauth0-spring.xml new file mode 100644 index 0000000..26704c7 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/resources/sapcxssoauth0-spring.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java new file mode 100644 index 0000000..335b73b --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/Actions.java @@ -0,0 +1,45 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.roles.Role; +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.core.model.user.CustomerModel; + +public interface Actions { + static Role getRole(String roleName) throws Auth0Exception { + return FetchRoleAction.getRole(roleName); + } + + static void assignRole(Role role, User user) throws Auth0Exception { + AssignRoleAction.assignRole(role, user); + } + + static void removeRole(Role role, User user) throws Auth0Exception { + RemoveRoleAction.removeRole(role, user); + } + + static User getUser(String email) throws Auth0Exception { + return FetchUserAction.getUser(email); + } + + static User createUser(CustomerModel customer) throws Auth0Exception { + return CreateUserAction.createUser(customer); + } + + static User updateUser(User user, CustomerModel customer) throws Auth0Exception { + return UpdateUserAction.updateUser(user, customer); + } + + static void removeUser(User user, String customerId) throws Auth0Exception { + RemoveUserAction.removeUser(user, customerId); + } + + static String getPasswordResetUrl(User user) throws Auth0Exception { + return getPasswordResetUrl(user, false); + } + + static String getPasswordResetUrl(User user, boolean markEmailAsVerified) throws Auth0Exception { + return PasswordResetUrlAction.getPasswordResetUrl(user, markEmailAsVerified); + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java new file mode 100644 index 0000000..f872871 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/AssignRoleAction.java @@ -0,0 +1,38 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.List; +import java.util.Map; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.roles.Role; +import com.auth0.json.mgmt.users.User; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class AssignRoleAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(AssignRoleAction.class); + + static void assignRole(Role role, User user) throws Auth0Exception { + new AssignRoleAction().execute(Map.of("role", role, "user", user)); + } + + private AssignRoleAction() { + // Avoid instantiation + } + + @Override + public Void execute(Map parameter) throws Auth0Exception { + Role role = getWithType(parameter, "role", Role.class); + User user = getWithType(parameter, "user", User.class); + + try { + submit(managementAPI().users().addRoles(user.getId(), List.of(role.getId()))); + LOG.debug("Assigned role with name '{}' to user with email '{}'.", role.getName(), user.getEmail()); + return null; + } catch (Auth0Exception exception) { + LOG.warn(String.format("Could not assign role '%s' to user with email '%s'. ", role.getName(), user.getEmail()), exception); + throw exception; + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java new file mode 100644 index 0000000..6504a6e --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/CreateUserAction.java @@ -0,0 +1,82 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.dto.converter.Converter; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class CreateUserAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(CreateUserAction.class); + + static User createUser(CustomerModel customer) throws Auth0Exception { + return new CreateUserAction().execute(Map.of("customer", customer)); + } + + private CreateUserAction() { + // Avoid instantiation + } + + @Override + public User execute(Map parameter) throws Auth0Exception { + CustomerModel customer = getWithType(parameter, "customer", CustomerModel.class); + String customerId = customer.getUid(); + + User user = null; + try { + Converter customerConverter = getCustomerConverter(); + User userInfo = customerConverter.convert(customer); + + if (requireEmailVerification()) { + userInfo.setEmailVerified(false); + userInfo.setVerifyEmail(true); + } + + if (useBlockedStatusForDisabledCustomers()) { + userInfo.setBlocked(BooleanUtils.isNotFalse(customer.isLoginDisabled())); + } + + // Add one time information for creation process + userInfo.setConnection(getCustomerConnection()); + userInfo.setPassword(getRandomPassword()); + if (requirePasswordVerification()) { + userInfo.setVerifyPassword(true); + } + + user = fetch(managementAPI().users().create(userInfo)); + return user; + } catch (Auth0Exception exception) { + LOG.debug(String.format("Create user with ID '%s' failed!", customerId), exception); + throw exception; + } finally { + LOG.debug("Create user with ID '{}' resulted in: '{}'.", customerId, user != null ? user.getId() : "-error-"); + } + } + + private char[] getRandomPassword() { + List passwordCharacters = new ArrayList<>(32); + + String alphaNumericChars = RandomStringUtils.randomAlphanumeric(26); + for (int i = 0; i < alphaNumericChars.length(); i++) { + passwordCharacters.add(String.valueOf(alphaNumericChars.charAt(i))); + } + + String specialChars = RandomStringUtils.random(6, '!', '@', '#', '$', '%', '^', '&', '*'); + for (int i = 0; i < specialChars.length(); i++) { + passwordCharacters.add(String.valueOf(specialChars.charAt(i))); + } + + Collections.shuffle(passwordCharacters); + return String.join("", passwordCharacters).toCharArray(); + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java new file mode 100644 index 0000000..f787168 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchRoleAction.java @@ -0,0 +1,38 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.Map; + +import com.auth0.client.mgmt.filter.RolesFilter; +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.roles.Role; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class FetchRoleAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(FetchRoleAction.class); + + static Role getRole(String roleName) throws Auth0Exception { + return new FetchRoleAction().execute(Map.of("role", roleName)); + } + + private FetchRoleAction() { + // Avoid instantiation + } + + @Override + public Role execute(Map parameter) throws Auth0Exception { + String roleName = getWithType(parameter, "role", String.class); + Role role = null; + try { + RolesFilter roleByName = new RolesFilter().withName(roleName); + role = fetchFirst(managementAPI().roles().list(roleByName)); + return role; + } catch (Auth0Exception exception) { + LOG.debug(String.format("Search for role with name '%s' could not be executed!", roleName), exception); + throw exception; + } finally { + LOG.debug("Lookup for existing role with name '{}' resulted in: '{}'", roleName, role != null ? role.getId() : "-not found-"); + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java new file mode 100644 index 0000000..a40428a --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/FetchUserAction.java @@ -0,0 +1,38 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.Map; + +import com.auth0.client.mgmt.filter.UserFilter; +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.users.User; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class FetchUserAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(FetchUserAction.class); + + static User getUser(String id) throws Auth0Exception { + return new FetchUserAction().execute(Map.of("id", id)); + } + + private FetchUserAction() { + // Avoid instantiation + } + + @Override + public User execute(Map parameter) throws Auth0Exception { + String userId = getWithType(parameter, "id", String.class); + User user = null; + try { + UserFilter userById = new UserFilter().withQuery(userId); + user = fetchFirst(managementAPI().users().list(userById)); + return user; + } catch (Auth0Exception exception) { + LOG.debug(String.format("Search for user with ID '%s' could not be executed!", userId), exception); + throw exception; + } finally { + LOG.debug("Lookup for existing user with ID '{}' resulted in: '{}'", userId, user != null ? user.getId() : "-not found-"); + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java new file mode 100644 index 0000000..3e58a69 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/PasswordResetUrlAction.java @@ -0,0 +1,38 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.Map; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.tickets.PasswordChangeTicket; +import com.auth0.json.mgmt.users.User; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class PasswordResetUrlAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(PasswordResetUrlAction.class); + + static String getPasswordResetUrl(User user, boolean markEmailAsVerified) throws Auth0Exception { + return new PasswordResetUrlAction().execute(Map.of("user", user, "markEmailAsVerified", markEmailAsVerified)); + } + + private PasswordResetUrlAction() { + // Avoid instantiation + } + + @Override + public String execute(Map parameter) throws Auth0Exception { + User user = getWithType(parameter, "user", User.class); + Boolean markEmailAsVerified = getWithType(parameter, "markEmailAsVerified", Boolean.class); + try { + PasswordChangeTicket ticket = new PasswordChangeTicket(user.getId()); + ticket.setMarkEmailAsVerified(markEmailAsVerified); + + PasswordChangeTicket passwordChangeTicket = fetch(managementAPI().tickets().requestPasswordChange(ticket)); + return passwordChangeTicket.getTicket(); + } catch (Auth0Exception exception) { + LOG.debug(String.format("Get password reset token for existing user with ID '{}' failed!", user.getEmail()), exception); + throw exception; + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java new file mode 100644 index 0000000..f461a6a --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveRoleAction.java @@ -0,0 +1,39 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.List; +import java.util.Map; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.roles.Role; +import com.auth0.json.mgmt.users.User; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class RemoveRoleAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(RemoveRoleAction.class); + + static void removeRole(Role role, User user) throws Auth0Exception { + new RemoveRoleAction().execute(Map.of("role", role, "user", user)); + } + + private RemoveRoleAction() { + // Avoid instantiation + } + + @Override + public Void execute(Map parameter) throws Auth0Exception { + Role role = getWithType(parameter, "role", Role.class); + User user = getWithType(parameter, "user", User.class); + + try { + submit(managementAPI().users().removeRoles(user.getId(), List.of(role.getId()))); + LOG.debug("Remove role with name '{}' from user with email '{}'.", role.getName(), user.getEmail()); + return null; + } catch (Auth0Exception exception) { + LOG.warn(String.format("Could not remove role '%s' from user with email '%s'. ", role.getName(), user.getEmail()), exception); + throw exception; + } + } + +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java new file mode 100644 index 0000000..d9be1e2 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/RemoveUserAction.java @@ -0,0 +1,35 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.Map; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.users.User; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class RemoveUserAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(RemoveUserAction.class); + + static void removeUser(User user, String customerId) throws Auth0Exception { + new RemoveUserAction().execute(Map.of("user", user, "customerId", customerId)); + } + + private RemoveUserAction() { + // Avoid instantiation + } + + @Override + public Void execute(Map parameter) throws Auth0Exception { + User user = getWithType(parameter, "user", User.class); + String customerId = getWithType(parameter, "customerId", String.class); + try { + fetch(managementAPI().users().delete(user.getId())); + LOG.debug("Delete user with ID '{}' was successful.", customerId); + return null; + } catch (Auth0Exception exception) { + LOG.debug(String.format("Delete user with ID '%s' failed!", customerId), exception); + throw exception; + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java new file mode 100644 index 0000000..147e60d --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkAction.java @@ -0,0 +1,76 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.Map; + +import com.auth0.client.mgmt.ManagementAPI; +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.Page; +import com.auth0.json.mgmt.users.User; +import com.auth0.net.Request; + +import de.hybris.platform.core.Registry; +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.dto.converter.Converter; + +/** + * Performs an action on the Auth0 SDK. + * + * @param response type of the action + */ +@FunctionalInterface +interface SdkAction { + R execute(Map requestParameter) throws Auth0Exception; + + default ManagementAPI managementAPI() throws Auth0Exception { + return getConfigurationService().getManagementAPI(); + } + + default void submit(Request request) throws Auth0Exception { + request.execute(); + } + + default T fetch(Request request) throws Auth0Exception { + return request.execute().getBody(); + } + + default T fetchFirst(Request> request) throws Auth0Exception { + return request.execute().getBody().getItems().stream().findFirst().orElse(null); + } + + default T getWithType(Map requestParameter, String parameterName, Class returnType) { + Object value = requestParameter.get(parameterName); + return (returnType.isInstance(value)) ? returnType.cast(value) : null; + } + + default String getCustomerIdField() { + return getConfigurationService().getCustomerIdField(); + } + + default String getCustomerConnection() { + return getConfigurationService().getCustomerConnection(); + } + + default Converter getCustomerConverter() { + return getConfigurationService().getCustomerConverter(); + } + + default boolean requireEmailVerification() { + return getConfigurationService().requireEmailVerification(); + } + + default boolean requirePasswordVerification() { + return getConfigurationService().requirePasswordVerification(); + } + + default boolean useBlockedStatusForDisabledCustomers() { + return getConfigurationService().useBlockedStatusForDisabledCustomers(); + } + + default String getAuthClientId() { + return getConfigurationService().getAuthClientId(); + } + + private SdkConfigurationService getConfigurationService() { + return Registry.getApplicationContext().getBean(SdkConfigurationService.class); + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java new file mode 100644 index 0000000..d4714d2 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/SdkConfigurationService.java @@ -0,0 +1,90 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import com.auth0.client.auth.AuthAPI; +import com.auth0.client.mgmt.ManagementAPI; +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.config.ConfigurationService; +import de.hybris.platform.servicelayer.dto.converter.Converter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SdkConfigurationService { + private static final Logger LOG = LoggerFactory.getLogger(SdkConfigurationService.class); + private static final String AUTH0_MANAGEMENT_API_DOMAIN = "sapcxsinglesignon.auth0.management.api.domain"; + private static final String AUTH0_MANAGEMENT_API_AUDIENCE = "sapcxsinglesignon.auth0.management.api.audience"; + private static final String AUTH0_AUTH_API_CLIENTID = "sapcxsinglesignon.auth0.auth.api.clientid"; + private static final String AUTH0_MANAGEMENT_API_CLIENTID = "sapcxsinglesignon.auth0.management.api.clientid"; + private static final String AUTH0_MANAGEMENT_API_CLIENTSECRET = "sapcxsinglesignon.auth0.management.api.clientsecret"; + private static final String AUTH0_CUSTOMER_CONNECTION = "sapcxsinglesignon.auth0.customer.connection"; + private static final String AUTH0_CUSTOMER_ID_FIELD = "sapcxsinglesignon.auth0.customer.idfield"; + private static final String AUTH0_REQUIRE_EMAIL_VERIFICATION = "sapcxsinglesignon.auth0.customer.requireemailverification"; + private static final String AUTH0_REQUIRE_PASSWORD_VERIFICATION = "sapcxsinglesignon.auth0.customer.requirepasswordverification"; + private static final String AUTH0_USE_BLOCKEDSTATUS = "sapcxsinglesignon.auth0.customer.useblockedstatus"; + + private ConfigurationService configurationService; + private Converter customerConverter; + + public SdkConfigurationService(ConfigurationService configurationService, Converter customerConverter) { + this.configurationService = configurationService; + this.customerConverter = customerConverter; + } + + public ManagementAPI getManagementAPI() throws Auth0Exception { + LOG.debug("Create new Auth0 ManagementAPI.", getAudience()); + return ManagementAPI.newBuilder(getDomain(), getManagementAccessToken()).build(); + } + + private String getManagementAccessToken() throws Auth0Exception { + LOG.debug("Fetch access token for management API for audience: {}", getAudience()); + AuthAPI authAPI = AuthAPI.newBuilder(getDomain(), getManagementClientId(), getManagementClientSecret()).build(); + return authAPI.requestToken(getAudience()).execute().getBody().getAccessToken(); + } + + private String getAudience() { + return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_AUDIENCE); + } + + private String getDomain() { + return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_DOMAIN); + } + + public String getManagementClientId() { + return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_CLIENTID); + } + + public String getManagementClientSecret() { + return configurationService.getConfiguration().getString(AUTH0_MANAGEMENT_API_CLIENTSECRET); + } + + public String getCustomerConnection() { + return configurationService.getConfiguration().getString(AUTH0_CUSTOMER_CONNECTION); + } + + public String getCustomerIdField() { + return configurationService.getConfiguration().getString(AUTH0_CUSTOMER_ID_FIELD); + } + + public Converter getCustomerConverter() { + return customerConverter; + } + + public boolean requireEmailVerification() { + return configurationService.getConfiguration().getBoolean(AUTH0_REQUIRE_EMAIL_VERIFICATION, false); + } + + public boolean requirePasswordVerification() { + return configurationService.getConfiguration().getBoolean(AUTH0_REQUIRE_PASSWORD_VERIFICATION, false); + } + + public boolean useBlockedStatusForDisabledCustomers() { + return configurationService.getConfiguration().getBoolean(AUTH0_USE_BLOCKEDSTATUS, false); + } + + public String getAuthClientId() { + return configurationService.getConfiguration().getString(AUTH0_AUTH_API_CLIENTID, null); + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java new file mode 100644 index 0000000..6a5b7a9 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/actions/UpdateUserAction.java @@ -0,0 +1,54 @@ +package tools.sapcx.commerce.sso.auth0.actions; + +import java.util.Map; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.dto.converter.Converter; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class UpdateUserAction implements SdkAction { + private static final Logger LOG = LoggerFactory.getLogger(UpdateUserAction.class); + + static User updateUser(User user, CustomerModel customer) throws Auth0Exception { + return new UpdateUserAction().execute(Map.of("user", user, "customer", customer)); + } + + private UpdateUserAction() { + // Avoid instantiation + } + + @Override + public User execute(Map parameter) throws Auth0Exception { + User user = getWithType(parameter, "user", User.class); + CustomerModel customer = getWithType(parameter, "customer", CustomerModel.class); + String customerId = customer.getUid(); + try { + Converter customerConverter = getCustomerConverter(); + User userInfo = customerConverter.convert(customer); + + if (requireEmailVerification() && !StringUtils.equals(user.getEmail(), userInfo.getEmail())) { + userInfo.setEmailVerified(false); + userInfo.setVerifyEmail(true); + } + + if (useBlockedStatusForDisabledCustomers()) { + userInfo.setBlocked(BooleanUtils.isNotFalse(customer.isLoginDisabled())); + } + + user = fetch(managementAPI().users().update(user.getId(), userInfo)); + return user; + } catch (Auth0Exception exception) { + LOG.debug(String.format("Search for user with ID '%s' failed!", customer.getUid()), exception); + throw exception; + } finally { + LOG.debug("Update information for existing user with ID '{}' resulted in: '{}'", customerId, user != null ? user.getId() : "-error-"); + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java new file mode 100644 index 0000000..6692d2b --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerMetadataPopulator.java @@ -0,0 +1,111 @@ +package tools.sapcx.commerce.sso.auth0.replication; + +import static java.lang.String.format; +import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; +import static org.apache.commons.lang3.BooleanUtils.isTrue; +import static org.apache.commons.lang3.StringUtils.isBlank; + +import java.util.HashMap; +import java.util.Map; + +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.converters.Populator; +import de.hybris.platform.core.model.user.AddressModel; +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.dto.converter.ConversionException; + +public class Auth0CustomerMetadataPopulator implements Populator { + private String metadataPrefix; + private String metadataKeyFormat; + + public Auth0CustomerMetadataPopulator(String metadataPrefix) { + this.metadataPrefix = metadataPrefix; + this.metadataKeyFormat = isBlank(metadataPrefix) ? "%1$s" : "%0$s_%1$s"; + } + + @Override + public void populate(CustomerModel source, User target) throws ConversionException { + emptyIfNull(source.getAddresses()).stream() + .filter(this::isContactAddress) + .findFirst() + .map(this::convertAddressToMetadata) + .ifPresent(target::setAppMetadata); + } + + protected boolean isContactAddress(AddressModel address) { + return isTrue(address.getContactAddress()); + } + + private Map convertAddressToMetadata(AddressModel address) { + Map metadata = new HashMap<>(16); + addStandardContactFields(address, metadata); + addStandardAddressFields(address, metadata); + addStandardTelecommunicationFields(address, metadata); + return metadata; + } + + protected void addStandardContactFields(AddressModel address, Map metadata) { + if (address.getTitle() != null) { + metadata.put(getKey("contact_title"), address.getTitle().getCode()); + } + if (address.getCompany() != null) { + metadata.put(getKey("contact_company"), address.getCompany()); + } + if (address.getDepartment() != null) { + metadata.put(getKey("contact_department"), address.getDepartment()); + } + } + + protected void addStandardAddressFields(AddressModel address, Map metadata) { + if (address.getLine1() != null) { + metadata.put(getKey("contact_streetname"), address.getLine1()); + } + if (address.getLine2() != null) { + metadata.put(getKey("contact_streetnumber"), address.getLine2()); + } + if (address.getBuilding() != null) { + metadata.put(getKey("contact_building"), address.getBuilding()); + } + if (address.getAppartment() != null) { + metadata.put(getKey("contact_appartment"), address.getAppartment()); + } + if (address.getPostalcode() != null) { + metadata.put(getKey("contact_postalcode"), address.getPostalcode()); + } + if (address.getTown() != null) { + metadata.put(getKey("contact_city"), address.getTown()); + } + if (address.getDistrict() != null) { + metadata.put(getKey("contact_district"), address.getDepartment()); + } + if (address.getRegion() != null) { + metadata.put(getKey("contact_region"), address.getRegion().getIsocodeShort()); + } + if (address.getCountry() != null) { + metadata.put(getKey("contact_country"), address.getCountry().getIsocode()); + } + if (address.getPobox() != null) { + metadata.put(getKey("contact_pobox"), address.getPobox()); + } + } + + protected void addStandardTelecommunicationFields(AddressModel address, Map metadata) { + if (address.getCellphone() != null) { + metadata.put(getKey("contact_cellphone"), address.getCellphone()); + } + if (address.getPhone1() != null) { + metadata.put(getKey("contact_phone1"), address.getPhone1()); + } + if (address.getPhone2() != null) { + metadata.put(getKey("contact_phone2"), address.getPhone2()); + } + if (address.getFax() != null) { + metadata.put(getKey("contact_fax"), address.getFax()); + } + } + + private String getKey(String fieldName) { + return format(metadataKeyFormat, metadataPrefix, fieldName); + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java new file mode 100644 index 0000000..4788027 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerPopulator.java @@ -0,0 +1,35 @@ +package tools.sapcx.commerce.sso.auth0.replication; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.commerceservices.strategies.CustomerNameStrategy; +import de.hybris.platform.converters.Populator; +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.dto.converter.ConversionException; + +public class Auth0CustomerPopulator implements Populator { + private CustomerNameStrategy customerNameStrategy; + + public Auth0CustomerPopulator(CustomerNameStrategy customerNameStrategy) { + this.customerNameStrategy = customerNameStrategy; + } + + @Override + public void populate(CustomerModel source, User target) throws ConversionException { + target.setEmail(source.getContactEmail()); + target.setNickname(source.getCustomerID()); + target.setName(source.getName()); + + String[] nameParts = customerNameStrategy.splitName(source.getName()); + if (nameParts.length == 2) { + if (isNotBlank(nameParts[0])) { + target.setGivenName(nameParts[0]); + } + if (isNotBlank(nameParts[1])) { + target.setFamilyName(nameParts[1]); + } + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java new file mode 100644 index 0000000..539037e --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/auth0/replication/Auth0CustomerReplicationStrategy.java @@ -0,0 +1,132 @@ +package tools.sapcx.commerce.sso.auth0.replication; + +import static org.apache.commons.collections4.ListUtils.emptyIfNull; + +import java.util.List; + +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.roles.Role; +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.user.UserService; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import tools.sapcx.commerce.sso.auth0.actions.Actions; +import tools.sapcx.commerce.sso.replication.CustomerReplicationException; +import tools.sapcx.commerce.sso.replication.CustomerReplicationHook; +import tools.sapcx.commerce.sso.replication.CustomerReplicationStrategy; + +public class Auth0CustomerReplicationStrategy implements CustomerReplicationStrategy { + private static final Logger LOG = LoggerFactory.getLogger(Auth0CustomerReplicationStrategy.class); + + private UserService userService; + private List customerReplicationHooks; + private String auth0RoleForCustomers; + private boolean isCreationEnabled; + private boolean isRemovalEnabled; + + public Auth0CustomerReplicationStrategy( + UserService userService, + List customerReplicationHooks, + String auth0RoleForCustomers, + boolean isCreationEnabled, + boolean isRemovalEnabled) { + this.userService = userService; + this.customerReplicationHooks = emptyIfNull(customerReplicationHooks); + this.auth0RoleForCustomers = auth0RoleForCustomers; + this.isCreationEnabled = isCreationEnabled; + this.isRemovalEnabled = isRemovalEnabled; + } + + @Override + public void replicate(CustomerModel customer) { + if (userService.isAnonymousUser(customer)) { + LOG.debug("Anonymous user replication is disabled by convention."); + return; + } + + User user = createOrUpdateUser(customer); + if (user != null) { + updateUserRoles(user, !customer.isLoginDisabled()); + } + } + + private User createOrUpdateUser(CustomerModel customer) { + String customerId = customer.getUid(); + try { + User user = Actions.getUser(customerId); + if (user != null) { + LOG.debug("User for provided customer ID '{}' exists: '{}'.", customerId, user.getId()); + User updatedUser = Actions.updateUser(user, customer); + customerReplicationHooks.forEach(hook -> hook.customerSuccessfullyUpdated(customer, updatedUser)); + return updatedUser; + } else if (!isCreationEnabled) { + LOG.debug("Customer creation is disabled by configuration."); + return null; + } else { + LOG.debug("User for provided customer ID '{}' does not exist.", customerId); + User createdUser = Actions.createUser(customer); + customerReplicationHooks.forEach(hook -> hook.customerSuccessfullyCreated(customer, createdUser)); + return createdUser; + } + } catch (Auth0Exception exception) { + LOG.debug("Could not replicate customer with ID '{}'. Data may no be in sync and needs to be corrected manually!", customerId); + throw new CustomerReplicationException("Could not replicate customer to Auth0!", exception); + } + } + + private Role updateUserRoles(User user, boolean isLoginEnabled) { + try { + if (StringUtils.isBlank(auth0RoleForCustomers)) { + return null; + } + + Role role = Actions.getRole(auth0RoleForCustomers); + if (role == null) { + return null; + } + + if (isLoginEnabled) { + Actions.assignRole(role, user); + } else { + Actions.removeRole(role, user); + } + return role; + } catch (Auth0Exception exception) { + LOG.debug("Could not synchronize roles for customer ID '{}'. Data may no be in sync and needs to be corrected manually!", user.getEmail()); + throw new CustomerReplicationException("Could not synchronize customer roles to Auth0!", exception); + } + } + + @Override + public void remove(String customerId) { + if (userService.isUserExisting(customerId) && userService.isAnonymousUser(userService.getUserForUID(customerId))) { + LOG.debug("Anonymous user removal is disabled by convention."); + return; + } + + try { + User user = Actions.getUser(customerId); + if (!isRemovalEnabled) { + LOG.debug("Customer removal is disabled by configuration."); + updateUserRoles(user, false); + return; + } + + if (user == null) { + LOG.debug("User for provided customer ID '{}' does not exist! Removal not necessary.", customerId); + } else { + LOG.debug("User for provided customer ID '{}' exists: '{}'. Trigger user removal.", customerId, user.getId()); + Actions.removeUser(user, customerId); + customerReplicationHooks.forEach(hook -> hook.customerSuccessfullyRemoved(customerId)); + } + } catch (Auth0Exception exception) { + LOG.debug("Could not remove customer with ID '{}'! Account needs to be removed manually!", customerId); + throw new CustomerReplicationException("Could not remove customer on Auth0 side!", exception); + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/constants/CxSsoAuth0Constants.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/constants/CxSsoAuth0Constants.java new file mode 100644 index 0000000..ab8028b --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/constants/CxSsoAuth0Constants.java @@ -0,0 +1,11 @@ +package tools.sapcx.commerce.sso.constants; + +@SuppressWarnings({ "deprecation", "squid:CallToDeprecatedMethod" }) +public class CxSsoAuth0Constants extends GeneratedCxSsoAuth0Constants { + public static final String EXTENSIONNAME = "sapcxssoauth0"; + + private CxSsoAuth0Constants() { + // empty + } + +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java new file mode 100644 index 0000000..88f7e51 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/AllCustomerReplicationFilter.java @@ -0,0 +1,12 @@ +package tools.sapcx.commerce.sso.replication; + +import java.util.function.Predicate; + +import de.hybris.platform.core.model.user.CustomerModel; + +public class AllCustomerReplicationFilter implements Predicate { + @Override + public boolean test(CustomerModel customerModel) { + return true; + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java new file mode 100644 index 0000000..ded0bd0 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationException.java @@ -0,0 +1,22 @@ +package tools.sapcx.commerce.sso.replication; + +public class CustomerReplicationException extends RuntimeException { + public CustomerReplicationException() { + } + + public CustomerReplicationException(String message) { + super(message); + } + + public CustomerReplicationException(String message, Throwable cause) { + super(message, cause); + } + + public CustomerReplicationException(Throwable cause) { + super(cause); + } + + public CustomerReplicationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java new file mode 100644 index 0000000..58aa6b2 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationHook.java @@ -0,0 +1,16 @@ +package tools.sapcx.commerce.sso.replication; + +import com.auth0.json.mgmt.users.User; + +import de.hybris.platform.core.model.user.CustomerModel; + +public interface CustomerReplicationHook { + default void customerSuccessfullyCreated(CustomerModel customer, User createdUser) { + } + + default void customerSuccessfullyUpdated(CustomerModel customer, User updatedUser) { + } + + default void customerSuccessfullyRemoved(String customer) { + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java new file mode 100644 index 0000000..39e8356 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationInterceptor.java @@ -0,0 +1,55 @@ +package tools.sapcx.commerce.sso.replication; + +import java.util.function.Predicate; + +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.servicelayer.interceptor.InterceptorContext; +import de.hybris.platform.servicelayer.interceptor.InterceptorException; +import de.hybris.platform.servicelayer.interceptor.RemoveInterceptor; +import de.hybris.platform.servicelayer.interceptor.ValidateInterceptor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CustomerReplicationInterceptor implements ValidateInterceptor, RemoveInterceptor { + private static final Logger LOG = LoggerFactory.getLogger(CustomerReplicationInterceptor.class); + + private CustomerReplicationStrategy customerReplicationStrategy; + private Predicate customerReplicationFilter; + private boolean enabled; + + public CustomerReplicationInterceptor( + CustomerReplicationStrategy customerReplicationStrategy, + Predicate customerReplicationFilter, + boolean enabled) { + this.customerReplicationStrategy = customerReplicationStrategy; + this.customerReplicationFilter = customerReplicationFilter; + this.enabled = enabled; + } + + @Override + public void onValidate(CustomerModel customer, InterceptorContext interceptorContext) { + if (enabled && customer != null) { + try { + if (customerReplicationFilter.test(customer)) { + customerReplicationStrategy.replicate(customer); + } + } catch (RuntimeException e) { + LOG.warn(String.format("Could not replicate customer with ID '%s'. Data may no be in sync and needs to be corrected manually!", customer.getUid()), e); + } + } + } + + @Override + public void onRemove(CustomerModel customer, InterceptorContext interceptorContext) throws InterceptorException { + if (enabled && customer != null) { + try { + if (customerReplicationFilter.test(customer)) { + customerReplicationStrategy.remove(customer.getUid()); + } + } catch (RuntimeException e) { + LOG.warn(String.format("Could not remove customer with ID '%s'! Account needs to be removed manually!", customer.getUid()), e); + } + } + } +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java new file mode 100644 index 0000000..31390bc --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/src/tools/sapcx/commerce/sso/replication/CustomerReplicationStrategy.java @@ -0,0 +1,11 @@ +package tools.sapcx.commerce.sso.replication; + +import javax.annotation.Nonnull; + +import de.hybris.platform.core.model.user.CustomerModel; + +public interface CustomerReplicationStrategy { + void replicate(@Nonnull CustomerModel customer); + + void remove(@Nonnull String customerId); +} diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java new file mode 100644 index 0000000..6e5ccc9 --- /dev/null +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxssoauth0/testsrc/tools/sapcx/commerce/sso/config/ExtensionConfigurationTests.java @@ -0,0 +1,18 @@ +package tools.sapcx.commerce.sso.config; + +import de.hybris.bootstrap.annotations.UnitTest; + +import org.junit.Test; + +import tools.sapcx.commerce.toolkit.testing.verifier.InstalledExtensionVerifier; + +@UnitTest +public class ExtensionConfigurationTests { + @Test + public void extensionConfiguration() { + InstalledExtensionVerifier.verifier() + .requires("sapcxssoauth0") + .requires("sapcxsinglesignon") + .verify(); + } +} From 639ca18a9dc123c42659269ee58bbb7687bc7111 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 9 Dec 2025 23:28:16 +0100 Subject: [PATCH 14/17] activate new sapcxssoauth0 extension --- core-customize/hybris/config/localextensions.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core-customize/hybris/config/localextensions.xml b/core-customize/hybris/config/localextensions.xml index 67eb31b..20b3b71 100644 --- a/core-customize/hybris/config/localextensions.xml +++ b/core-customize/hybris/config/localextensions.xml @@ -15,5 +15,6 @@ + From ecb1a0ade673f9d3e34bb164ca1d33b7dc3054b5 Mon Sep 17 00:00:00 2001 From: Stefan Bechtold Date: Tue, 9 Dec 2025 23:29:00 +0100 Subject: [PATCH 15/17] adjust copyright note and bump version to 4.4.0 --- .../hybris/bin/custom/sapcxtools/sapcommercetoolkit/LICENSE.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcommercetoolkit/README.md | 2 +- .../bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml | 2 +- .../sapcxtools/sapcommercetoolkit/external-dependencies.xml | 2 +- .../custom/sapcxtools/sapcommercetoolkit/project.properties | 2 +- .../hybris/bin/custom/sapcxtools/sapcxbackoffice/LICENSE.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcxbackoffice/README.md | 2 +- .../bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml | 2 +- .../sapcxtools/sapcxbackoffice/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxbackoffice/project.properties | 2 +- .../hybris/bin/custom/sapcxtools/sapcxenvconfig/LICENSE.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcxenvconfig/README.md | 2 +- .../bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxenvconfig/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxenvconfig/project.properties | 2 +- .../hybris/bin/custom/sapcxtools/sapcxreporting/LICENSE.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcxreporting/README.md | 2 +- .../bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxreporting/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxreporting/project.properties | 2 +- .../hybris/bin/custom/sapcxtools/sapcxsearch/LICENSE.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcxsearch/README.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml | 2 +- .../custom/sapcxtools/sapcxsearch/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxsearch/project.properties | 2 +- .../hybris/bin/custom/sapcxtools/sapcxtemplate/LICENSE.md | 2 +- .../hybris/bin/custom/sapcxtools/sapcxtemplate/README.md | 2 +- .../bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml | 3 +-- .../custom/sapcxtools/sapcxtemplate/external-dependencies.xml | 2 +- .../bin/custom/sapcxtools/sapcxtemplate/project.properties | 2 +- 30 files changed, 30 insertions(+), 31 deletions(-) diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/README.md index 3fbad6f..22667dd 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/README.md @@ -372,4 +372,4 @@ them. And if you are missing a builder, feel free to raise an issue or to create _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml index 4a8e49d..f612629 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcommercetoolkit" version="4.4.0" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml index 5785f13..7f9ccf8 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcommercetoolkit - 4.3.1-SNAPSHOT + 4.4.0 jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/project.properties index 58215f3..c77300a 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcommercetoolkit/project.properties @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2023 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/README.md index 44a7038..9c4a0c8 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/README.md @@ -56,4 +56,4 @@ For the sync status updates, one need to configure a property using the conventi _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml index 3101048..ff94db6 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxbackoffice" version="4.4.0" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml index 846a147..f1b57ac 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxbackoffice - 4.3.1-SNAPSHOT + 4.4.0 jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/project.properties index f6e08fc..4e8e6df 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxbackoffice/project.properties @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2023 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/README.md index 1ebd0d1..1b47fc1 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/README.md @@ -171,4 +171,4 @@ Another example for configuring OAuth with Auth0 within the frontend can be foun _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml index 23b7007..56ec022 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxenvconfig" version="4.4.0" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml index ed862c4..6b48511 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxenvconfig - 4.3.1-SNAPSHOT + 4.4.0 jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/project.properties index 0f9fa67..4b3b628 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxenvconfig/project.properties @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2023 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/README.md index 52be553..348fbc8 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/README.md @@ -72,4 +72,4 @@ capabilities on custom objects, e.g. special item types introduces by the projec _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml index 50aedc2..0e42956 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxreporting" version="4.4.0" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml index 1c123aa..290834b 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxreporting - 4.3.1-SNAPSHOT + 4.4.0 jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/project.properties index ab677a0..724cd41 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxreporting/project.properties @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2021-2022 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/README.md index 07375ea..ce5f02b 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/README.md @@ -19,4 +19,4 @@ The `sapcxsearch` extension improves the solr facet search functions. _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml index 326fc52..feef887 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/extensioninfo.xml @@ -1,7 +1,7 @@ + name="sapcxsearch" version="4.4.0" usemaven="true"> diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml index 5957502..f7f0286 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxsearch - 4.3.1-SNAPSHOT + 4.4.0 jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/project.properties index 819ac8c..102f315 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxsearch/project.properties @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2021-2022 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/LICENSE.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/LICENSE.md index 7aff51c..9776fe0 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/LICENSE.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2023] [SAP CX Tools] + Copyright [2025] [SAP CX Tools] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/README.md b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/README.md index 8516996..dbff9e1 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/README.md +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/README.md @@ -22,4 +22,4 @@ COPY-TEXT ACTIVATION / SETTINGS _Licensed under the Apache License, Version 2.0, January 2004_ -_Copyright 2023, SAP CX Tools_ \ No newline at end of file +_Copyright 2025, SAP CX Tools_ \ No newline at end of file diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml index 06569fd..37b8de8 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/extensioninfo.xml @@ -1,7 +1,6 @@ - + diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml index 1e8162d..63f5a35 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/external-dependencies.xml @@ -3,7 +3,7 @@ 4.0.0 tools.sapcx sapcxtemplate - 4.3.1-SNAPSHOT + 4.4.0 jar diff --git a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/project.properties b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/project.properties index 7af6aca..156bc22 100644 --- a/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/project.properties +++ b/core-customize/hybris/bin/custom/sapcxtools/sapcxtemplate/project.properties @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------- -# Copyright (c) 2023 SAP CX Tools +# Copyright (c) 2025 SAP CX Tools # ---------------------------------------------------------------------------- # Location of the file for the global platform spring application context From 8bbd32c50ba7282977dcf89d007cc5f89935e20d Mon Sep 17 00:00:00 2001 From: Stefan Bechtold <453673+bechte@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:42:41 +0100 Subject: [PATCH 16/17] update sonar-project.properties for version 4.4.0 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 52ea013..60be04b 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.organization=sapcxtools # This is the name and version displayed in the SonarCloud UI. sonar.projectName=sapcxtools-workspace -sonar.projectVersion=4.3.1-SNAPSHOT +sonar.projectVersion=4.4.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=core-customize/hybris/bin/custom/sapcxtools From 016c55e5461f2e187aa9883d279a67831a7e2ebd Mon Sep 17 00:00:00 2001 From: Stefan Bechtold <453673+bechte@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:43:18 +0100 Subject: [PATCH 17/17] adjust typo in comment for production environment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../hybris/config/cloud/persona/production.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-customize/hybris/config/cloud/persona/production.properties b/core-customize/hybris/config/cloud/persona/production.properties index 322d1e7..8a8a277 100644 --- a/core-customize/hybris/config/cloud/persona/production.properties +++ b/core-customize/hybris/config/cloud/persona/production.properties @@ -1,6 +1,6 @@ # cloud/persona/production.properties -# Activate DisableLoginForImportedUserInterceptor for development purposes +# Activate DisableLoginForImportedUserInterceptor for production purposes disable.login.for.imported.user.interceptor.enabled=true # Sample & Test data