diff --git a/README.md b/README.md index f63b69b..fa5bf24 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,263 @@ # OSF Builder Suite For Salesforce Commerce Cloud :: Data Import Import your site data to a Salesforce Commerce Cloud instance + +**OSF Builder Suite For Salesforce Commerce Cloud :: Data Import** is a very easy-to-use and highly configurable Jenkins plugin that is used to automate data imports to your Salesforce Commerce Cloud continuous integration sandbox, development or staging instance. + +If you have a bug to report or maybe a feature that you wish to request, please do so [on GitHub, on the project's issues page](https://github.com/jenkinsci/osf-builder-suite-for-sfcc-data-import-plugin/issues). + +  +# Features + +- Simple. It does one thing, and it does it well. +- Easy to install, use and keep updated. +- Easy to configure. The plugin can be configured from the Jenkins web interface. +- Support for classical mode, Jenkins [Pipelines](https://jenkins.io/doc/book/pipeline/) and also the new modern [Blue Ocean](https://jenkins.io/doc/book/blueocean/) interface. +- Super flexible. Every little thing is configurable so that the plugin can be easily adjusted to your workflow. +- Integrated with the Jenkins [credentials plugin](https://plugins.jenkins.io/credentials) so that your credentials are safely stored encrypted. +- Support for two factor authentication +- Good documentation. Every option is documented both here on this page but also inline in Jenkins's UI by clicking the question mark icon next to the item for which you wish to display the help information. +- Support for HTTP proxy with basic or [NTLM](https://en.wikipedia.org/wiki/NT_LAN_Manager) authentication. +- Free +- Open source +- Fast builds. The data is checked for changes and if nothing changed the build is skipped so that you don't import same unchanged data over and over again. + +  +# Installation + +Just go to `Manage Jenkins > Manage Plugins > Available`, search for `OSF Builder Suite`, select `OSF Builder Suite For Salesforce Commerce Cloud :: Data Import` and click `Download now and install after restart` button. + +  +# Configuration + +![](imgs/hostname.png) + +Hostname of the SFCC instance where this build should be deployed. Examples: + +| | | +| -------------------------------------------: | :-------------------------------------------------------------------------------- | +| `cert.staging.realm.customer.demandware.net` | For deployments to a staging instance that has two factor auth enabled.  | +| `staging-realm-customer.demandware.net` | For deployments to a staging instance that does not have two factor auth enabled. | +| `development-realm-customer.demandware.net` | For deployments to a development instance. | +| `devNN-realm-customer.demandware.net` | For deployments to a sandbox instance. | + +![](imgs/tf_credentials.png) + +Two Factor Auth credentials of type `OSF Builder Suite :: Two Factor Auth Credentials` for the SFCC instance where this build should be deployed. Select `- none -` if you deploy to a instance that does not require two factor auth. + +![](imgs/oc_credentials.png) + +Open Commerce API credentials of type `OSF Builder Suite :: Open Commerce API Credentials` for the SFCC instance where this build should be deployed. + +![](imgs/oc_version.png) + +The version to be used by the calls made to OCAPI. The Open Commerce API Version starts with the character `v` (lowercase) followed by the actual version number, separated by an underscore. + +For example: `v19_10` + +![](imgs/archive_name.png) + +Name of the zip file that will be created by compressing your data that will be uploaded to your target instance and then imported. Only the name, without the `.zip` extension as it will be added by the plugin automatically.  + +For example: `metadata` + + +![](imgs/source_path.png) + +Path (relative to the workspace) to a directory where the builder will look for data to be imported.  + +For example: `scm/my-git-repo/metadata` + + +![](imgs/exclude_patterns.png) + +You can also define a list of patterns to be ignored. If a path matches any of the patterns in this list then it will be ignored and not added to the build. The pattern needs to be relative to the source path defined above. + +When a path is matched against a pattern, the following special characters can be used: + +| | | +| ---: | --------------------------------------------------------------- | +| `?` | Matches one character (any character except path separators) | +| `*` | Matches zero or more characters (not including path separators) | +| `**` | Matches zero or more path segments | + +Examples: + +| | | +| ---------------------: | -------------------------------------------------------------------------------------------------------------------------------- | +| `**/*.js` | Matches all .js files/dirs in a directory tree | +| `node_modules/**` | Matches the node_modules folder and all its contents | +| `test/a??.js` | Matches all files/dirs which start with an a, then two more characters and then .js, in a directory called test | +| `**` | Matches everything in a directory tree | +| `**/test/**/XYZ*` | Matches all files/dirs which start with XYZ and where there is a parent directory called test (e.g. abc/test/def/ghi/XYZ123) | +  + +![](imgs/tmp_dir.png) + +Path (relative to the workspace) to a temp directory, that will be used during the build. If the directory does not exist, it will be created by the builder and it will also be automatically cleaned up before each build. + +For example: `tmp/metadata` + +  +![](imgs/proxy_host.png) + +If your Jenkins server sits behind a firewall and does not have direct access to the internet, you can specify the HTTP proxy host in this field to allow Jenkins to connect to the internet trough it. + +![](imgs/proxy_port.png) + +This field works in conjunction with the proxy host field to specify the HTTP proxy port. + +![](imgs/proxy_username.png) + +This field works in conjunction with the proxy host field to specify the username used to authenticate with the proxy. + +If this proxy requires Microsoft's [NTLM](https://en.wikipedia.org/wiki/NT_LAN_Manager) authentication scheme then the domain name can be encoded within the username by prefixing the domain name followed by a back-slash `\` before the username, e.g `ACME\John Doe`. + +![](imgs/proxy_password.png) + +This field works in conjunction with the proxy host field to specify the HTTP proxy password. + +![](imgs/ssl_validation.png) + +When this option is checked, the builder will no longer validate the SSL certificate and hostname of the target instance. + +**This has potential security implications so make sure you know what you are doing before enabling this option!** +# **Open Commerce API Settings** +Go to `Administration > Site Development > Open Commerce API Settings`, select type `Data`, select context `Global` and add following configuration: + +```JSON +{ + "_v": "19.10", + "clients": [ + { + "client_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "resources": [ + { + "resource_id": "/code_versions/*", + "methods": ["put", "patch"], + "read_attributes": "(**)", + "write_attributes": "(**)" + }, + { + "resource_id": "/jobs/*/executions", + "methods": ["post"], + "read_attributes": "(**)", + "write_attributes": "(**)" + }, + { + "resource_id": "/jobs/*/executions/*", + "methods": ["get"], + "read_attributes": "(**)", + "write_attributes": "(**)" + } + ] + } + ] +} +``` + +Go to `Administration > Organization > WebDAV Client Permissions` and add following configuration: + +```JSON +{ + "clients": [ + { + "client_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "permissions": [ + { + "path": "/cartridges", + "operations": [ + "read_write" + ] + }, + { + "path": "/impex", + "operations": [ + "read_write" + ] + } + ] + } + ] +} + +``` + + +# Jenkins Pipeline Configuration +Here's a sample pipeline configuration to get you started: + +```Groovy +node { + stage('Cleanup') { + cleanWs() + } + + stage('Git') { + dir('scm/github.com/???/???') { + git( + branch: '???', + credentialsId: '???', + url: 'git@github.com:???/???.git' + ) + } + } + + stage('Yarn') { + dir('scm/github.com/???/???') { + nodejs('NodeJS v12') { + sh('yarn install') + } + } + } + + stage('Build') { + dir('scm/github.com/???/???') { + nodejs('NodeJS v12') { + sh('yarn run webpack:prd') + } + } + } + + /* See https://plugins.jenkins.io/osf-builder-suite-for-sfcc-deploy + stage('CodePush') { + osfBuilderSuiteForSFCCDeploy( + hostname: '???', + tfCredentialsId: '???', + ocCredentialsId: '???', + ocVersion: 'v19_10', + buildVersion: 'dev', + sourcePaths: [[sourcePath: 'scm/github.com/???/???/cartridges']], + activateBuild: true, + createBuildInfoCartridge: true, + tempDirectory: 'tmp/code' + ) + } + */ + + stage('DataPush') { + osfBuilderSuiteForSFCCDataImport( + hostname: '???', + tfCredentialsId: '???', + ocCredentialsId: '???', + ocVersion: 'v19_10', + archiveName: 'metadata', + sourcePath: 'scm/github.com/???/???/metadata', + importStrategy: 'DELTA', + tempDirectory: 'tmp/data' + ) + } +} +``` + +You can also always consult the pipelines documentation available at  or check the pipeline syntax link right inside Jenkins on the left navigation menu. + +![](imgs/left_nav.png) +  +# Version history + + + +# Dev +- `mvn hpi:run` +- `mvn clean package hpi:hpi` +- `mvn release:prepare release:perform` diff --git a/imgs/archive_name.png b/imgs/archive_name.png new file mode 100644 index 0000000..4dbf6a4 Binary files /dev/null and b/imgs/archive_name.png differ diff --git a/imgs/exclude_patterns.png b/imgs/exclude_patterns.png new file mode 100644 index 0000000..09923cb Binary files /dev/null and b/imgs/exclude_patterns.png differ diff --git a/imgs/hostname.png b/imgs/hostname.png new file mode 100644 index 0000000..5f6dab7 Binary files /dev/null and b/imgs/hostname.png differ diff --git a/imgs/left_nav.png b/imgs/left_nav.png new file mode 100644 index 0000000..146dd58 Binary files /dev/null and b/imgs/left_nav.png differ diff --git a/imgs/oc_credentials.png b/imgs/oc_credentials.png new file mode 100644 index 0000000..2fe1efa Binary files /dev/null and b/imgs/oc_credentials.png differ diff --git a/imgs/oc_version.png b/imgs/oc_version.png new file mode 100644 index 0000000..1d5cf04 Binary files /dev/null and b/imgs/oc_version.png differ diff --git a/imgs/proxy_host.png b/imgs/proxy_host.png new file mode 100644 index 0000000..f54e070 Binary files /dev/null and b/imgs/proxy_host.png differ diff --git a/imgs/proxy_password.png b/imgs/proxy_password.png new file mode 100644 index 0000000..c93e06c Binary files /dev/null and b/imgs/proxy_password.png differ diff --git a/imgs/proxy_port.png b/imgs/proxy_port.png new file mode 100644 index 0000000..1786237 Binary files /dev/null and b/imgs/proxy_port.png differ diff --git a/imgs/proxy_username.png b/imgs/proxy_username.png new file mode 100644 index 0000000..2209581 Binary files /dev/null and b/imgs/proxy_username.png differ diff --git a/imgs/source_path.png b/imgs/source_path.png new file mode 100644 index 0000000..db9ee55 Binary files /dev/null and b/imgs/source_path.png differ diff --git a/imgs/ssl_validation.png b/imgs/ssl_validation.png new file mode 100644 index 0000000..bc63991 Binary files /dev/null and b/imgs/ssl_validation.png differ diff --git a/imgs/tf_credentials.png b/imgs/tf_credentials.png new file mode 100644 index 0000000..9f0a98a Binary files /dev/null and b/imgs/tf_credentials.png differ diff --git a/imgs/tmp_dir.png b/imgs/tmp_dir.png new file mode 100644 index 0000000..9528a51 Binary files /dev/null and b/imgs/tmp_dir.png differ diff --git a/pom.xml b/pom.xml index 28ed285..c762574 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ osf-builder-suite-for-sfcc-data-import - 1.0.10-SNAPSHOT + 1.1.0-SNAPSHOT hpi OSF Builder Suite For Salesforce Commerce Cloud :: Data Import @@ -134,14 +134,14 @@ org.apache.commons commons-lang3 - 3.8.1 + 3.9 org.apache.commons commons-text - 1.6 + 1.8 @@ -155,7 +155,7 @@ org.apache.httpcomponents httpclient - 4.5.7 + 4.5.10 @@ -183,7 +183,7 @@ org.codehaus.plexus plexus-utils - 3.1.1 + 3.3.0 diff --git a/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder.java b/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder.java index f761aaa..3febc58 100644 --- a/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder.java +++ b/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder.java @@ -5,13 +5,19 @@ import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.cloudbees.plugins.credentials.common.StandardListBoxModel; import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder; -import hudson.*; -import hudson.model.*; +import hudson.AbortException; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractProject; +import hudson.model.Item; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.model.queue.Tasks; import hudson.remoting.VirtualChannel; import hudson.security.ACL; -import hudson.tasks.Builder; import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Builder; import hudson.util.ListBoxModel; import jenkins.MasterToSlaveFileCallable; import jenkins.model.Jenkins; @@ -20,34 +26,12 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.http.*; -import org.apache.http.Header; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.NTCredentials; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.entity.GzipDecompressingEntity; -import org.apache.http.config.ConnectionConfig; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.TrustStrategy; -import org.apache.http.impl.client.*; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.http.ssl.SSLContexts; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; import org.codehaus.plexus.util.MatchPattern; import org.jenkinsci.Symbol; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.HTTPProxyCredentials; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.OpenCommerceAPICredentials; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.TwoFactorAuthCredentials; -import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.BusinessManagerAuthCredentials; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.dataimport.model.DataImportAction; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.dataimport.model.DataImportEnvAction; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.dataimport.repeatable.ExcludePattern; @@ -58,17 +42,12 @@ import org.zeroturnaround.zip.ZipUtil; import javax.annotation.Nonnull; -import javax.net.ssl.SSLContext; import java.io.*; -import java.nio.file.*; -import java.security.*; -import java.security.cert.CertificateException; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.X509Certificate; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -79,7 +58,6 @@ public class DataImportBuilder extends Builder implements SimpleBuildStep { private String hostname; - private String bmCredentialsId; private String tfCredentialsId; private String ocCredentialsId; private String ocVersion; @@ -93,7 +71,6 @@ public class DataImportBuilder extends Builder implements SimpleBuildStep { @DataBoundConstructor public DataImportBuilder( String hostname, - String bmCredentialsId, String tfCredentialsId, String ocCredentialsId, String ocVersion, @@ -105,7 +82,6 @@ public DataImportBuilder( String tempDirectory) { this.hostname = hostname; - this.bmCredentialsId = bmCredentialsId; this.tfCredentialsId = tfCredentialsId; this.ocCredentialsId = ocCredentialsId; this.ocVersion = ocVersion; @@ -128,17 +104,6 @@ public void setHostname(String hostname) { this.hostname = hostname; } - @SuppressWarnings("unused") - public String getBmCredentialsId() { - return bmCredentialsId; - } - - @SuppressWarnings("unused") - @DataBoundSetter - public void setBmCredentialsId(String bmCredentialsId) { - this.bmCredentialsId = StringUtils.trim(bmCredentialsId); - } - @SuppressWarnings("unused") public String getTfCredentialsId() { return tfCredentialsId; @@ -260,19 +225,6 @@ public void perform( throw abortException; } - BusinessManagerAuthCredentials bmCredentials = null; - if (StringUtils.isNotEmpty(bmCredentialsId)) { - bmCredentials = com.cloudbees.plugins.credentials.CredentialsProvider.findCredentialById( - bmCredentialsId, - BusinessManagerAuthCredentials.class, - build, URIRequirementBuilder.create().build() - ); - } - - if (bmCredentials != null) { - com.cloudbees.plugins.credentials.CredentialsProvider.track(build, bmCredentials); - } - TwoFactorAuthCredentials tfCredentials = null; if (StringUtils.isNotEmpty(tfCredentialsId)) { tfCredentials = com.cloudbees.plugins.credentials.CredentialsProvider.findCredentialById( @@ -332,8 +284,6 @@ public void perform( DataImportResult dataImportResult = workspace.act(new DataImportCallable( listener, expandedHostname, - bmCredentialsId, - bmCredentials, tfCredentialsId, tfCredentials, ocCredentialsId, @@ -384,38 +334,6 @@ public boolean isApplicable(Class jobType) { return true; } - @SuppressWarnings("unused") - public ListBoxModel doFillBmCredentialsIdItems( - @AncestorInPath Item item, - @QueryParameter String credentialsId) { - - StandardListBoxModel result = new StandardListBoxModel(); - - if (item == null) { - if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { - return result.includeCurrentValue(credentialsId); - } - } else { - if (!item.hasPermission(Item.EXTENDED_READ) - && !item.hasPermission(CredentialsProvider.USE_ITEM)) { - return result.includeCurrentValue(credentialsId); - } - } - - return result - .includeEmptyValue() - .includeMatchingAs( - item instanceof hudson.model.Queue.Task - ? Tasks.getAuthenticationOf((hudson.model.Queue.Task) item) - : ACL.SYSTEM, - item, - StandardCredentials.class, - URIRequirementBuilder.create().build(), - CredentialsMatchers.instanceOf(BusinessManagerAuthCredentials.class) - ) - .includeCurrentValue(credentialsId); - } - @SuppressWarnings("unused") public ListBoxModel doFillTfCredentialsIdItems( @AncestorInPath Item item, @@ -528,7 +446,7 @@ public String getHttpProxyCredentialsId() { return httpProxyCredentialsId; } - @SuppressWarnings({"unused"}) + @SuppressWarnings("unused") public void setHttpProxyCredentialsId(String httpProxyCredentialsId) { this.httpProxyCredentialsId = httpProxyCredentialsId; } @@ -538,7 +456,7 @@ public Boolean getDisableSSLValidation() { return disableSSLValidation; } - @SuppressWarnings({"WeakerAccess", "unused"}) + @SuppressWarnings("unused") public void setDisableSSLValidation(Boolean disableSSLValidation) { this.disableSSLValidation = disableSSLValidation; } @@ -560,8 +478,6 @@ private static class DataImportCallable extends MasterToSlaveFileCallable { - if (!request.containsHeader("Accept-Encoding")) { - request.addHeader("Accept-Encoding", "gzip"); - } - }); - - httpClientBuilder.addInterceptorFirst((HttpResponseInterceptor) (response, context) -> { - HttpEntity entity = response.getEntity(); - if (entity != null) { - Header header = entity.getContentEncoding(); - if (header != null) { - for (HeaderElement headerElement : header.getElements()) { - if (headerElement.getName().equalsIgnoreCase("gzip")) { - response.setEntity(new GzipDecompressingEntity(response.getEntity())); - return; - } - } - } - } - }); - - httpClientBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() - .setBufferSize(5242880 /* 5 MegaBytes */) - .setFragmentSizeHint(5242880 /* 5 MegaBytes */) - .build() - ); - - httpClientBuilder.setDefaultRequestConfig(RequestConfig.custom() - .setSocketTimeout(300000 /* 5 minutes */) - .setConnectTimeout(300000 /* 5 minutes */) - .setConnectionRequestTimeout(300000 /* 5 minutes */) - .build() + OpenCommerceAPI openCommerceAPI = new OpenCommerceAPI( + hostname, + httpProxyCredentials, + disableSSLValidation, + tfCredentials, + ocCredentials, + ocVersion ); - org.apache.http.client.CredentialsProvider httpCredentialsProvider = new BasicCredentialsProvider(); - - // Proxy Auth - if (httpProxyCredentials != null) { - String httpProxyHost = httpProxyCredentials.getHost(); - String httpProxyPort = httpProxyCredentials.getPort(); - String httpProxyUsername = httpProxyCredentials.getUsername(); - String httpProxyPassword = httpProxyCredentials.getPassword().getPlainText(); - - int httpProxyPortInteger; - - try { - httpProxyPortInteger = Integer.parseInt(httpProxyPort); - } catch (NumberFormatException e) { - logger.println(); - throw new AbortException( - String.format("Invalid value \"%s\" for HTTP proxy port!", httpProxyPort) + " " + - "Please enter a valid port number." - ); - } - - if (httpProxyPortInteger <= 0 || httpProxyPortInteger > 65535) { - logger.println(); - throw new AbortException( - String.format("Invalid value \"%s\" for HTTP proxy port!", httpProxyPort) + " " + - "Please enter a valid port number." - ); - } - - HttpHost httpClientProxy = new HttpHost(httpProxyHost, httpProxyPortInteger); - httpClientBuilder.setProxy(httpClientProxy); - - if (StringUtils.isNotEmpty(httpProxyUsername) && StringUtils.isNotEmpty(httpProxyPassword)) { - if (httpProxyUsername.contains("\\")) { - String domain = httpProxyUsername.substring(0, httpProxyUsername.indexOf("\\")); - String user = httpProxyUsername.substring(httpProxyUsername.indexOf("\\") + 1); - - httpCredentialsProvider.setCredentials( - new AuthScope(httpProxyHost, httpProxyPortInteger), - new NTCredentials(user, httpProxyPassword, "", domain) - ); - } else { - httpCredentialsProvider.setCredentials( - new AuthScope(httpProxyHost, httpProxyPortInteger), - new UsernamePasswordCredentials(httpProxyUsername, httpProxyPassword) - ); - } - } - } - - httpClientBuilder.setDefaultCredentialsProvider(httpCredentialsProvider); - - SSLContextBuilder sslContextBuilder = SSLContexts.custom(); - - if (tfCredentials != null) { - Provider bouncyCastleProvider = new BouncyCastleProvider(); - - // Server Certificate - Reader serverCertificateReader = new StringReader(tfCredentials.getServerCertificate()); - PEMParser serverCertificateParser = new PEMParser(serverCertificateReader); - - JcaX509CertificateConverter serverCertificateConverter = new JcaX509CertificateConverter(); - serverCertificateConverter.setProvider(bouncyCastleProvider); - - X509Certificate serverCertificate; - - try { - serverCertificate = serverCertificateConverter.getCertificate( - (X509CertificateHolder) serverCertificateParser.readObject() - ); - } catch (CertificateException | IOException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while loading two factor auth server certificate!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - serverCertificate.checkValidity(); - } catch (CertificateExpiredException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "The server certificate used for two factor auth is expired!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } catch (CertificateNotYetValidException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "The server certificate used for two factor auth is not yet valid!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - // Client Certificate - Reader clientCertificateReader = new StringReader(tfCredentials.getClientCertificate()); - PEMParser clientCertificateParser = new PEMParser(clientCertificateReader); - - JcaX509CertificateConverter clientCertificateConverter = new JcaX509CertificateConverter(); - clientCertificateConverter.setProvider(bouncyCastleProvider); - - X509Certificate clientCertificate; - - try { - clientCertificate = clientCertificateConverter.getCertificate( - (X509CertificateHolder) clientCertificateParser.readObject() - ); - } catch (CertificateException | IOException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while loading two factor auth client certificate!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - clientCertificate.checkValidity(); - } catch (CertificateExpiredException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "The client certificate used for two factor auth is expired!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } catch (CertificateNotYetValidException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "The client certificate used for two factor auth is not yet valid!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - // Client Private Key - Reader clientPrivateKeyReader = new StringReader(tfCredentials.getClientPrivateKey()); - PEMParser clientPrivateKeyParser = new PEMParser(clientPrivateKeyReader); - - Object clientPrivateKeyObject; - - try { - clientPrivateKeyObject = clientPrivateKeyParser.readObject(); - } catch (IOException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while loading two factor auth client private key!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - PrivateKeyInfo clientPrivateKeyInfo; - - if (clientPrivateKeyObject instanceof PrivateKeyInfo) { - clientPrivateKeyInfo = (PrivateKeyInfo) clientPrivateKeyObject; - } else if (clientPrivateKeyObject instanceof PEMKeyPair) { - clientPrivateKeyInfo = ((PEMKeyPair) clientPrivateKeyObject).getPrivateKeyInfo(); - } else { - logger.println(); - throw new AbortException("Failed to load two factor auth client private key!"); - } - - // Trust Store - KeyStore customTrustStore; - - try { - customTrustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - } catch (KeyStoreException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom trust store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - customTrustStore.load(null, null); - } catch (IOException | NoSuchAlgorithmException | CertificateException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom trust store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - customTrustStore.setCertificateEntry(hostname, serverCertificate); - } catch (KeyStoreException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom trust store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - sslContextBuilder.loadTrustMaterial(customTrustStore, null); - } catch (NoSuchAlgorithmException | KeyStoreException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom trust store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - // Key Store - KeyFactory customKeyStoreKeyFactory; - - try { - customKeyStoreKeyFactory = KeyFactory.getInstance("RSA"); - } catch (NoSuchAlgorithmException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - PrivateKey customKeyStorePrivateKey; - - try { - customKeyStorePrivateKey = customKeyStoreKeyFactory.generatePrivate( - new PKCS8EncodedKeySpec(clientPrivateKeyInfo.getEncoded()) - ); - } catch (InvalidKeySpecException | IOException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - KeyStore customKeyStore; - - try { - customKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - } catch (KeyStoreException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - customKeyStore.load(null, null); - } catch (IOException | NoSuchAlgorithmException | CertificateException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - char[] keyStorePassword = RandomStringUtils.randomAscii(32).toCharArray(); - - try { - customKeyStore.setKeyEntry( - hostname, customKeyStorePrivateKey, keyStorePassword, - new X509Certificate[]{clientCertificate, serverCertificate} - ); - } catch (KeyStoreException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - sslContextBuilder.loadKeyMaterial(customKeyStore, keyStorePassword); - } catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - } - - if (disableSSLValidation != null && disableSSLValidation) { - try { - sslContextBuilder.loadTrustMaterial(null, (TrustStrategy) (arg0, arg1) -> true); - } catch (NoSuchAlgorithmException | KeyStoreException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while setting up the custom key store!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - } - - SSLContext customSSLContext; - - try { - customSSLContext = sslContextBuilder.build(); - } catch (NoSuchAlgorithmException | KeyManagementException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while creating custom SSL context!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - if (disableSSLValidation != null && disableSSLValidation) { - httpClientBuilder.setSSLSocketFactory( - new SSLConnectionSocketFactory( - customSSLContext, NoopHostnameVerifier.INSTANCE - ) - ); - } else { - httpClientBuilder.setSSLSocketFactory( - new SSLConnectionSocketFactory( - customSSLContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier() - ) - ); - } - - CloseableHttpClient httpClient = httpClientBuilder.build(); - /* Setup HTTP Client */ - - /* Cleaning up leftover data from previous data import */ logger.println(); logger.println("[+] Cleaning up leftover data from previous data import"); for (String rmPath : Arrays.asList(archiveName, String.format("%s.zip", archiveName))) { - WebDAV.cleanupLeftoverData( - OpenCommerceAPI.auth( - httpClient, - hostname, - bmCredentials, - ocCredentials - ), - httpClient, - hostname, - rmPath - ); - + openCommerceAPI.cleanupLeftoverData(rmPath); logger.println(String.format(" - %s", rmPath)); } @@ -1359,18 +856,7 @@ public DataImportResult invoke(File dir, VirtualChannel channel) throws IOExcept logger.println(); logger.println(String.format("[+] Uploading data (%s.zip)", archiveName)); - WebDAV.uploadData( - OpenCommerceAPI.auth( - httpClient, - hostname, - bmCredentials, - ocCredentials - ), - httpClient, - hostname, - dataZip, - archiveName - ); + openCommerceAPI.uploadData(dataZip, archiveName); logger.println(" + Ok"); /* Uploading data */ @@ -1380,44 +866,19 @@ public DataImportResult invoke(File dir, VirtualChannel channel) throws IOExcept logger.println(); logger.println(String.format("[+] Importing data (%s.zip)", archiveName)); - Map executeSiteArchiveImportJobResult = OpenCommerceAPI.executeSiteArchiveImportJob( - OpenCommerceAPI.auth( - httpClient, - hostname, - bmCredentials, - ocCredentials - ), - httpClient, - hostname, - ocVersion, - archiveName, - ocCredentials - ); - - String executionId = executeSiteArchiveImportJobResult.get("id"); - String executionStatus = executeSiteArchiveImportJobResult.get("execution_status"); - logger.println(String.format(" - %s", executionStatus)); + OpenCommerceAPI.JobExecutionResult impJobResult = openCommerceAPI.executeSiteArchiveImportJob(archiveName); + logger.println(String.format(" - %s", impJobResult.getStatus())); - while (!StringUtils.equalsIgnoreCase(executionStatus, "finished")) { + String currentExecutionStatus = impJobResult.getStatus(); + while (!StringUtils.equalsIgnoreCase(currentExecutionStatus, "finished")) { TimeUnit.MINUTES.sleep(1); - - Map checkSiteArchiveImportJobResult = OpenCommerceAPI.checkSiteArchiveImportJob( - OpenCommerceAPI.auth( - httpClient, - hostname, - bmCredentials, - ocCredentials - ), - httpClient, - hostname, - ocVersion, + OpenCommerceAPI.JobExecutionResult chkJobResult = openCommerceAPI.checkSiteArchiveImportJob( archiveName, - executionId, - ocCredentials + impJobResult.getId() ); - executionStatus = checkSiteArchiveImportJobResult.get("execution_status"); - logger.println(String.format(" - %s", executionStatus)); + currentExecutionStatus = chkJobResult.getStatus(); + logger.println(String.format(" - %s", currentExecutionStatus)); } logger.println(" + Ok"); @@ -1429,40 +890,14 @@ public DataImportResult invoke(File dir, VirtualChannel channel) throws IOExcept logger.println("[+] Cleaning up leftover data from current data import"); for (String rmPath : Arrays.asList(archiveName, String.format("%s.zip", archiveName))) { - WebDAV.cleanupLeftoverData( - OpenCommerceAPI.auth( - httpClient, - hostname, - bmCredentials, - ocCredentials - ), - httpClient, - hostname, - rmPath - ); - + openCommerceAPI.cleanupLeftoverData(rmPath); logger.println(String.format(" - %s", rmPath)); } logger.println(" + Ok"); /* Cleaning up leftover data from current data import */ - - /* Close HTTP Client */ - try { - httpClient.close(); - } catch (IOException e) { - logger.println(); - AbortException abortException = new AbortException(String.format( - "Exception thrown while closing HTTP client!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - /* Close HTTP Client */ - - + openCommerceAPI.close(); return new DataImportResult(currentDataFingerprints, "IMPORTED"); } } diff --git a/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/OpenCommerceAPI.java b/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/OpenCommerceAPI.java index f7869a7..5c33d84 100644 --- a/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/OpenCommerceAPI.java +++ b/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/OpenCommerceAPI.java @@ -5,59 +5,479 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import hudson.AbortException; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.http.Consts; -import org.apache.http.HttpStatus; -import org.apache.http.NameValuePair; -import org.apache.http.StatusLine; +import org.apache.http.*; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.NTCredentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.GzipDecompressingEntity; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.config.ConnectionConfig; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.entity.ContentType; +import org.apache.http.entity.FileEntity; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.*; import org.apache.http.message.BasicNameValuePair; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; -import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.BusinessManagerAuthCredentials; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.HTTPProxyCredentials; import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.OpenCommerceAPICredentials; +import org.jenkinsci.plugins.osfbuildersuiteforsfcc.credentials.TwoFactorAuthCredentials; +import javax.net.ssl.SSLContext; +import java.io.File; import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; import java.net.URLEncoder; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; import java.util.stream.Stream; class OpenCommerceAPI { - static Map auth( - CloseableHttpClient httpClient, + private String hostname; + private OpenCommerceAPICredentials ocCredentials; + private String ocVersion; + + private String cacheAuthType; + private String cacheAuthToken; + private Long cacheAuthExpire; + + private CloseableHttpClient httpClient; + + OpenCommerceAPI( String hostname, - BusinessManagerAuthCredentials bmCredentials, - OpenCommerceAPICredentials ocCredentials) throws IOException { + HTTPProxyCredentials httpProxyCredentials, + Boolean disableSSLValidation, + TwoFactorAuthCredentials tfCredentials, + OpenCommerceAPICredentials ocCredentials, + String ocVersion) throws IOException { + + this.hostname = hostname; + this.ocCredentials = ocCredentials; + this.ocVersion = ocVersion; + + this.cacheAuthType = ""; + this.cacheAuthToken = ""; + this.cacheAuthExpire = 0L; + + /* Setup HTTP Client */ + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + httpClientBuilder.setUserAgent("Jenkins (OSF Builder Suite For Salesforce Commerce Cloud)"); + httpClientBuilder.setDefaultCookieStore(new BasicCookieStore()); + + httpClientBuilder.addInterceptorFirst((HttpRequestInterceptor) (request, context) -> { + if (!request.containsHeader("Accept-Encoding")) { + request.addHeader("Accept-Encoding", "gzip"); + } + }); + + httpClientBuilder.addInterceptorFirst((HttpResponseInterceptor) (response, context) -> { + HttpEntity entity = response.getEntity(); + if (entity != null) { + Header header = entity.getContentEncoding(); + if (header != null) { + for (HeaderElement headerElement : header.getElements()) { + if (headerElement.getName().equalsIgnoreCase("gzip")) { + response.setEntity(new GzipDecompressingEntity(response.getEntity())); + return; + } + } + } + } + }); + + httpClientBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() + .setBufferSize(5242880 /* 5 MegaBytes */) + .setFragmentSizeHint(5242880 /* 5 MegaBytes */) + .build() + ); + + httpClientBuilder.setDefaultRequestConfig(RequestConfig.custom() + .setSocketTimeout(300000 /* 5 minutes */) + .setConnectTimeout(300000 /* 5 minutes */) + .setConnectionRequestTimeout(300000 /* 5 minutes */) + .build() + ); + + org.apache.http.client.CredentialsProvider httpCredentialsProvider = new BasicCredentialsProvider(); + + // Proxy Auth + if (httpProxyCredentials != null) { + String httpProxyHost = httpProxyCredentials.getHost(); + String httpProxyPort = httpProxyCredentials.getPort(); + String httpProxyUsername = httpProxyCredentials.getUsername(); + String httpProxyPassword = httpProxyCredentials.getPassword().getPlainText(); + + int httpProxyPortInteger; + + try { + httpProxyPortInteger = Integer.parseInt(httpProxyPort); + } catch (NumberFormatException e) { + throw new AbortException( + String.format("Invalid value \"%s\" for HTTP proxy port!", httpProxyPort) + " " + + "Please enter a valid port number." + ); + } + + if (httpProxyPortInteger <= 0 || httpProxyPortInteger > 65535) { + throw new AbortException( + String.format("Invalid value \"%s\" for HTTP proxy port!", httpProxyPort) + " " + + "Please enter a valid port number." + ); + } + + HttpHost httpClientProxy = new HttpHost(httpProxyHost, httpProxyPortInteger); + httpClientBuilder.setProxy(httpClientProxy); + + if (StringUtils.isNotEmpty(httpProxyUsername) && StringUtils.isNotEmpty(httpProxyPassword)) { + if (httpProxyUsername.contains("\\")) { + String domain = httpProxyUsername.substring(0, httpProxyUsername.indexOf("\\")); + String user = httpProxyUsername.substring(httpProxyUsername.indexOf("\\") + 1); + + httpCredentialsProvider.setCredentials( + new AuthScope(httpProxyHost, httpProxyPortInteger), + new NTCredentials(user, httpProxyPassword, "", domain) + ); + } else { + httpCredentialsProvider.setCredentials( + new AuthScope(httpProxyHost, httpProxyPortInteger), + new UsernamePasswordCredentials(httpProxyUsername, httpProxyPassword) + ); + } + } + } + + httpClientBuilder.setDefaultCredentialsProvider(httpCredentialsProvider); + + SSLContextBuilder sslContextBuilder = SSLContexts.custom(); + + if (tfCredentials != null) { + Provider bouncyCastleProvider = new BouncyCastleProvider(); + + // Server Certificate + Reader serverCertificateReader = new StringReader(tfCredentials.getServerCertificate()); + PEMParser serverCertificateParser = new PEMParser(serverCertificateReader); + + JcaX509CertificateConverter serverCertificateConverter = new JcaX509CertificateConverter(); + serverCertificateConverter.setProvider(bouncyCastleProvider); + + X509Certificate serverCertificate; + + try { + serverCertificate = serverCertificateConverter.getCertificate( + (X509CertificateHolder) serverCertificateParser.readObject() + ); + } catch (CertificateException | IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while loading two factor auth server certificate!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + serverCertificate.checkValidity(); + } catch (CertificateExpiredException e) { + AbortException abortException = new AbortException(String.format( + "The server certificate used for two factor auth is expired!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } catch (CertificateNotYetValidException e) { + AbortException abortException = new AbortException(String.format( + "The server certificate used for two factor auth is not yet valid!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + // Client Certificate + Reader clientCertificateReader = new StringReader(tfCredentials.getClientCertificate()); + PEMParser clientCertificateParser = new PEMParser(clientCertificateReader); + + JcaX509CertificateConverter clientCertificateConverter = new JcaX509CertificateConverter(); + clientCertificateConverter.setProvider(bouncyCastleProvider); + + X509Certificate clientCertificate; + + try { + clientCertificate = clientCertificateConverter.getCertificate( + (X509CertificateHolder) clientCertificateParser.readObject() + ); + } catch (CertificateException | IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while loading two factor auth client certificate!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + clientCertificate.checkValidity(); + } catch (CertificateExpiredException e) { + AbortException abortException = new AbortException(String.format( + "The client certificate used for two factor auth is expired!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } catch (CertificateNotYetValidException e) { + AbortException abortException = new AbortException(String.format( + "The client certificate used for two factor auth is not yet valid!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + // Client Private Key + Reader clientPrivateKeyReader = new StringReader(tfCredentials.getClientPrivateKey()); + PEMParser clientPrivateKeyParser = new PEMParser(clientPrivateKeyReader); + + Object clientPrivateKeyObject; + + try { + clientPrivateKeyObject = clientPrivateKeyParser.readObject(); + } catch (IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while loading two factor auth client private key!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + PrivateKeyInfo clientPrivateKeyInfo; + + if (clientPrivateKeyObject instanceof PrivateKeyInfo) { + clientPrivateKeyInfo = (PrivateKeyInfo) clientPrivateKeyObject; + } else if (clientPrivateKeyObject instanceof PEMKeyPair) { + clientPrivateKeyInfo = ((PEMKeyPair) clientPrivateKeyObject).getPrivateKeyInfo(); + } else { + throw new AbortException("Failed to load two factor auth client private key!"); + } + + // Trust Store + KeyStore customTrustStore; + + try { + customTrustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + } catch (KeyStoreException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom trust store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + customTrustStore.load(null, null); + } catch (IOException | NoSuchAlgorithmException | CertificateException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom trust store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + customTrustStore.setCertificateEntry(hostname, serverCertificate); + } catch (KeyStoreException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom trust store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + sslContextBuilder.loadTrustMaterial(customTrustStore, null); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom trust store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + // Key Store + KeyFactory customKeyStoreKeyFactory; + + try { + customKeyStoreKeyFactory = KeyFactory.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + PrivateKey customKeyStorePrivateKey; + + try { + customKeyStorePrivateKey = customKeyStoreKeyFactory.generatePrivate( + new PKCS8EncodedKeySpec(clientPrivateKeyInfo.getEncoded()) + ); + } catch (InvalidKeySpecException | IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + KeyStore customKeyStore; + + try { + customKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + } catch (KeyStoreException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + customKeyStore.load(null, null); + } catch (IOException | NoSuchAlgorithmException | CertificateException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + char[] keyStorePassword = RandomStringUtils.randomAscii(32).toCharArray(); + + try { + customKeyStore.setKeyEntry( + hostname, customKeyStorePrivateKey, keyStorePassword, + new X509Certificate[]{clientCertificate, serverCertificate} + ); + } catch (KeyStoreException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + sslContextBuilder.loadKeyMaterial(customKeyStore, keyStorePassword); + } catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + } + + if (disableSSLValidation != null && disableSSLValidation) { + try { + sslContextBuilder.loadTrustMaterial(null, (TrustStrategy) (arg0, arg1) -> true); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while setting up the custom key store!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + } + + SSLContext customSSLContext; + + try { + customSSLContext = sslContextBuilder.build(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while creating custom SSL context!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + if (disableSSLValidation != null && disableSSLValidation) { + httpClientBuilder.setSSLSocketFactory( + new SSLConnectionSocketFactory( + customSSLContext, NoopHostnameVerifier.INSTANCE + ) + ); + } else { + httpClientBuilder.setSSLSocketFactory( + new SSLConnectionSocketFactory( + customSSLContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier() + ) + ); + } + + httpClient = httpClientBuilder.build(); + /* Setup HTTP Client */ + } + + private AuthResponse auth() throws IOException { + Long currentTs = new Date().getTime() / 1000L; + if (cacheAuthExpire > currentTs) { + return new AuthResponse(cacheAuthToken, cacheAuthType); + } List httpPostParams = new ArrayList<>(); - httpPostParams.add(new BasicNameValuePair( - "grant_type", "urn:demandware:params:oauth:grant-type:client-id:dwsid:dwsecuretoken" - )); + httpPostParams.add(new BasicNameValuePair("grant_type", "client_credentials")); RequestBuilder requestBuilder = RequestBuilder.create("POST"); requestBuilder.setHeader("Authorization", String.format( "Basic %s", Base64.getEncoder().encodeToString( String.format( - "%s:%s:%s", - bmCredentials.getUsername(), - bmCredentials.getPassword().getPlainText(), + "%s:%s", + ocCredentials.getClientId(), ocCredentials.getClientPassword().getPlainText() ).getBytes(Consts.UTF_8) ) )); + requestBuilder.setUri("https://account.demandware.com/dwsso/oauth2/access_token"); requestBuilder.setEntity(new UrlEncodedFormEntity(httpPostParams, Consts.UTF_8)); - requestBuilder.setUri(String.format( - "https://%s/dw/oauth2/access_token?client_id=%s", - hostname, - URLEncoder.encode(ocCredentials.getClientId(), "UTF-8") - )); CloseableHttpResponse httpResponse; @@ -65,7 +485,7 @@ static Map auth( httpResponse = httpClient.execute(requestBuilder.build()); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -78,7 +498,7 @@ static Map auth( httpEntityString = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -89,7 +509,7 @@ static Map auth( httpResponse.close(); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -100,7 +520,7 @@ static Map auth( if (httpStatusLine.getStatusCode() != HttpStatus.SC_OK) { throw new AbortException(String.format( - "\nFailed to authenticate with OCAPI! %s - %s!\nResponse=%s", + "Failed to authenticate with OCAPI! %s - %s!\nResponse=%s", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase(), httpEntityString @@ -114,7 +534,7 @@ static Map auth( jsonElement = jsonParser.parse(httpEntityString); } catch (JsonParseException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while parsing OCAPI JSON response!\nResponse=%s\n%s", + "Exception thrown while parsing OCAPI JSON response!\nResponse=%s\n%s", httpEntityString, ExceptionUtils.getStackTrace(e) )); @@ -124,40 +544,139 @@ static Map auth( if (!jsonElement.isJsonObject()) { throw new AbortException(String.format( - "\nFailed to parse OCAPI JSON response!\nResponse=%s", + "Failed to parse OCAPI JSON response!\nResponse=%s", httpEntityString )); } JsonObject jsonObject = jsonElement.getAsJsonObject(); - boolean isValidJson = Stream.of("access_token", "token_type").allMatch(jsonObject::has); + boolean isValidJson = Stream.of("access_token", "token_type", "expires_in").allMatch(jsonObject::has); if (!isValidJson) { throw new AbortException(String.format( - "\nFailed to parse OCAPI JSON response!\nResponse=%s", + "Failed to parse OCAPI JSON response!\nResponse=%s", httpEntityString )); } - Map authResponseMap = new HashMap<>(); - authResponseMap.put("token_type", jsonObject.get("token_type").getAsString()); - authResponseMap.put("access_token", jsonObject.get("access_token").getAsString()); - return authResponseMap; + String accessToken = jsonObject.get("access_token").getAsString(); + String tokenType = jsonObject.get("token_type").getAsString(); + long expiresIn = jsonObject.get("expires_in").getAsLong(); + + cacheAuthToken = accessToken; + cacheAuthType = tokenType; + cacheAuthExpire = (new Date().getTime() / 1000L) + expiresIn - 60; + + return new AuthResponse(cacheAuthToken, cacheAuthType); } - static Map executeSiteArchiveImportJob( - Map authResponseMap, - CloseableHttpClient httpClient, - String hostname, - String ocVersion, - String archiveName, - OpenCommerceAPICredentials ocCredentials) throws IOException { + void cleanupLeftoverData(String path) throws IOException { + AuthResponse authResponse = auth(); + + RequestBuilder requestBuilder = RequestBuilder.create("DELETE"); + requestBuilder.setHeader("Authorization", String.format( + "%s %s", + authResponse.getAuthType(), + authResponse.getAuthToken() + )); + + requestBuilder.setUri(String.format( + "https://%s/on/demandware.servlet/webdav/Sites/Impex/src/instance/%s", + hostname, + URLEncoder.encode(path, "UTF-8") + )); + + CloseableHttpResponse httpResponse; + + try { + httpResponse = httpClient.execute(requestBuilder.build()); + } catch (IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while making HTTP request!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + httpResponse.close(); + } catch (IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while making HTTP request!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + StatusLine httpStatusLine = httpResponse.getStatusLine(); + + if (!Arrays.asList(HttpStatus.SC_NOT_FOUND, HttpStatus.SC_NO_CONTENT).contains(httpStatusLine.getStatusCode())) { + throw new AbortException(String.format( + "%s - %s!", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase() + )); + } + } + + void uploadData(File dataZip, String archiveName) throws IOException { + AuthResponse authResponse = auth(); + + RequestBuilder requestBuilder = RequestBuilder.create("PUT"); + requestBuilder.setHeader("Authorization", String.format( + "%s %s", + authResponse.getAuthType(), + authResponse.getAuthToken() + )); + + requestBuilder.setEntity(new FileEntity(dataZip, ContentType.APPLICATION_OCTET_STREAM)); + requestBuilder.setUri(String.format( + "https://%s/on/demandware.servlet/webdav/Sites/Impex/src/instance/%s.zip", + hostname, + URLEncoder.encode(archiveName, "UTF-8") + )); + + CloseableHttpResponse httpResponse; + + try { + httpResponse = httpClient.execute(requestBuilder.build()); + } catch (IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while making HTTP request!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + try { + httpResponse.close(); + } catch (IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while making HTTP request!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + + StatusLine httpStatusLine = httpResponse.getStatusLine(); + + if (httpStatusLine.getStatusCode() != HttpStatus.SC_CREATED) { + throw new AbortException(String.format( + "%s - %s!", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase() + )); + } + } + + JobExecutionResult executeSiteArchiveImportJob(String archiveName) throws IOException { + AuthResponse authResponse = auth(); RequestBuilder requestBuilder = RequestBuilder.create("POST"); requestBuilder.setHeader("Authorization", String.format( "%s %s", - authResponseMap.get("token_type"), - authResponseMap.get("access_token") + authResponse.getAuthType(), + authResponse.getAuthToken() )); JsonObject requestJson = new JsonObject(); @@ -178,7 +697,7 @@ static Map executeSiteArchiveImportJob( httpResponse = httpClient.execute(requestBuilder.build()); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -191,7 +710,7 @@ static Map executeSiteArchiveImportJob( httpEntityString = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -202,7 +721,7 @@ static Map executeSiteArchiveImportJob( httpResponse.close(); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -216,7 +735,7 @@ static Map executeSiteArchiveImportJob( jsonElement = jsonParser.parse(httpEntityString); } catch (JsonParseException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while parsing OCAPI JSON response!\nResponse=%s\n%s", + "Exception thrown while parsing OCAPI JSON response!\nResponse=%s\n%s", httpEntityString, ExceptionUtils.getStackTrace(e) )); @@ -228,7 +747,7 @@ static Map executeSiteArchiveImportJob( if (!Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED).contains(httpStatusLine.getStatusCode())) { throw new AbortException(String.format( - "\nFailed to execute OCAPI data import job! %s - %s!\nResponse=%s", + "Failed to execute OCAPI data import job! %s - %s!\nResponse=%s", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase(), httpEntityString @@ -237,7 +756,7 @@ static Map executeSiteArchiveImportJob( if (!jsonElement.isJsonObject()) { throw new AbortException(String.format( - "\nFailed to parse OCAPI execute data import job JSON response!\nResponse=%s", + "Failed to parse OCAPI execute data import job JSON response!\nResponse=%s", httpEntityString )); } @@ -247,31 +766,24 @@ static Map executeSiteArchiveImportJob( if (!isValidJson) { throw new AbortException(String.format( - "\nFailed to parse OCAPI execute data import job JSON response!\nResponse=%s", + "Failed to parse OCAPI execute data import job JSON response!\nResponse=%s", httpEntityString )); } - Map executeJobResponseMap = new HashMap<>(); - executeJobResponseMap.put("execution_status", jsonObject.get("execution_status").getAsString()); - executeJobResponseMap.put("id", jsonObject.get("id").getAsString()); - return executeJobResponseMap; + String jobId = jsonObject.get("id").getAsString(); + String jobStatus = jsonObject.get("execution_status").getAsString(); + return new JobExecutionResult(jobId, jobStatus); } - static Map checkSiteArchiveImportJob( - Map authResponseMap, - CloseableHttpClient httpClient, - String hostname, - String ocVersion, - String archiveName, - String jobId, - OpenCommerceAPICredentials ocCredentials) throws IOException { + JobExecutionResult checkSiteArchiveImportJob(String archiveName, String jobId) throws IOException { + AuthResponse authResponse = auth(); RequestBuilder requestBuilder = RequestBuilder.create("GET"); requestBuilder.setHeader("Authorization", String.format( "%s %s", - authResponseMap.get("token_type"), - authResponseMap.get("access_token") + authResponse.getAuthType(), + authResponse.getAuthToken() )); requestBuilder.setUri(String.format( @@ -288,7 +800,7 @@ static Map checkSiteArchiveImportJob( httpResponse = httpClient.execute(requestBuilder.build()); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -301,7 +813,7 @@ static Map checkSiteArchiveImportJob( httpEntityString = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -312,7 +824,7 @@ static Map checkSiteArchiveImportJob( httpResponse.close(); } catch (IOException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", + "Exception thrown while making HTTP request!\n%s", ExceptionUtils.getStackTrace(e) )); abortException.initCause(e); @@ -326,7 +838,7 @@ static Map checkSiteArchiveImportJob( jsonElement = jsonParser.parse(httpEntityString); } catch (JsonParseException e) { AbortException abortException = new AbortException(String.format( - "\nException thrown while parsing OCAPI JSON response!\nResponse=%s\n%s", + "Exception thrown while parsing OCAPI JSON response!\nResponse=%s\n%s", httpEntityString, ExceptionUtils.getStackTrace(e) )); @@ -338,7 +850,7 @@ static Map checkSiteArchiveImportJob( if (httpStatusLine.getStatusCode() != HttpStatus.SC_OK) { throw new AbortException(String.format( - "\nFailed to get OCAPI data import job status! %s - %s!\nResponse=%s", + "Failed to get OCAPI data import job status! %s - %s!\nResponse=%s", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase(), httpEntityString @@ -347,7 +859,7 @@ static Map checkSiteArchiveImportJob( if (!jsonElement.isJsonObject()) { throw new AbortException(String.format( - "\nFailed to parse OCAPI get data import job JSON response!\nResponse=%s", + "Failed to parse OCAPI get data import job JSON response!\nResponse=%s", httpEntityString )); } @@ -355,7 +867,7 @@ static Map checkSiteArchiveImportJob( JsonObject jsonObject = jsonElement.getAsJsonObject(); if (!jsonObject.has("execution_status")) { throw new AbortException(String.format( - "\nFailed to parse OCAPI get data import job JSON response!\nResponse=%s", + "Failed to parse OCAPI get data import job JSON response!\nResponse=%s", httpEntityString )); } @@ -366,7 +878,7 @@ static Map checkSiteArchiveImportJob( if (StringUtils.equalsIgnoreCase(executionStatus, "finished")) { if (!jsonObject.has("exit_status")) { throw new AbortException(String.format( - "\nFailed to parse OCAPI get data import job JSON response!\nResponse=%s", + "Failed to parse OCAPI get data import job JSON response!\nResponse=%s", httpEntityString )); } @@ -375,7 +887,7 @@ static Map checkSiteArchiveImportJob( if (!exitStatusElement.isJsonObject()) { throw new AbortException(String.format( - "\nFailed to parse OCAPI get data import job JSON response!\nResponse=%s", + "Failed to parse OCAPI get data import job JSON response!\nResponse=%s", httpEntityString )); } @@ -387,15 +899,65 @@ static Map checkSiteArchiveImportJob( if (!StringUtils.equalsIgnoreCase(exitStatusStatus, "ok")) { throw new AbortException(String.format( - "\nFailed to import %s.zip!\nResponse=%s", + "Failed to import %s.zip!\nResponse=%s", archiveName, httpEntityString )); } } - Map checkJobResponseMap = new HashMap<>(); - checkJobResponseMap.put("execution_status", jsonObject.get("execution_status").getAsString()); - return checkJobResponseMap; + String jobStatus = jsonObject.get("execution_status").getAsString(); + return new JobExecutionResult(jobId, jobStatus); + } + + void close() throws IOException { + /* Close HTTP Client */ + try { + httpClient.close(); + } catch (IOException e) { + AbortException abortException = new AbortException(String.format( + "Exception thrown while closing HTTP client!\n%s", + ExceptionUtils.getStackTrace(e) + )); + abortException.initCause(e); + throw abortException; + } + /* Close HTTP Client */ + } + + private static final class AuthResponse { + private String authToken; + private String authType; + + AuthResponse(String authToken, String authType) { + this.authToken = authToken; + this.authType = authType; + } + + String getAuthToken() { + return authToken; + } + + String getAuthType() { + return authType; + } + } + + static final class JobExecutionResult { + private String id; + private String status; + + JobExecutionResult(String id, String status) { + this.id = id; + this.status = status; + } + + String getId() { + return id; + } + + String getStatus() { + return status; + } } } diff --git a/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/WebDAV.java b/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/WebDAV.java deleted file mode 100644 index 8c2344e..0000000 --- a/src/main/java/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/WebDAV.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.jenkinsci.plugins.osfbuildersuiteforsfcc.dataimport; - -import hudson.AbortException; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.RequestBuilder; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.FileEntity; -import org.apache.http.impl.client.CloseableHttpClient; - -import java.io.File; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.Map; - -class WebDAV { - static void cleanupLeftoverData( - Map authResponseMap, - CloseableHttpClient httpClient, - String hostname, - String path) throws IOException { - - RequestBuilder requestBuilder = RequestBuilder.create("DELETE"); - requestBuilder.setHeader("Authorization", String.format( - "%s %s", - authResponseMap.get("token_type"), - authResponseMap.get("access_token") - )); - - requestBuilder.setUri(String.format( - "https://%s/on/demandware.servlet/webdav/Sites/Impex/src/instance/%s", - hostname, - URLEncoder.encode(path, "UTF-8") - )); - - CloseableHttpResponse httpResponse; - - try { - httpResponse = httpClient.execute(requestBuilder.build()); - } catch (IOException e) { - AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - httpResponse.close(); - } catch (IOException e) { - AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - StatusLine httpStatusLine = httpResponse.getStatusLine(); - - if (httpStatusLine.getStatusCode() != HttpStatus.SC_NOT_FOUND) { - if (httpStatusLine.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { - throw new AbortException("\nInvalid username or password!"); - } else if (httpStatusLine.getStatusCode() != HttpStatus.SC_NO_CONTENT) { - throw new AbortException(String.format( - "\n%s - %s!", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase() - )); - } - } - } - - static void uploadData( - Map authResponseMap, - CloseableHttpClient httpClient, - String hostname, - File dataZip, - String archiveName) throws IOException { - - RequestBuilder requestBuilder = RequestBuilder.create("PUT"); - requestBuilder.setHeader("Authorization", String.format( - "%s %s", - authResponseMap.get("token_type"), - authResponseMap.get("access_token") - )); - - requestBuilder.setEntity(new FileEntity(dataZip, ContentType.APPLICATION_OCTET_STREAM)); - requestBuilder.setUri(String.format( - "https://%s/on/demandware.servlet/webdav/Sites/Impex/src/instance/%s.zip", - hostname, - URLEncoder.encode(archiveName, "UTF-8") - )); - - CloseableHttpResponse httpResponse; - - try { - httpResponse = httpClient.execute(requestBuilder.build()); - } catch (IOException e) { - AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - try { - httpResponse.close(); - } catch (IOException e) { - AbortException abortException = new AbortException(String.format( - "\nException thrown while making HTTP request!\n%s", - ExceptionUtils.getStackTrace(e) - )); - abortException.initCause(e); - throw abortException; - } - - StatusLine httpStatusLine = httpResponse.getStatusLine(); - - if (httpStatusLine.getStatusCode() != HttpStatus.SC_CREATED) { - if (httpStatusLine.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { - throw new AbortException("\nInvalid username or password!"); - } else { - throw new AbortException(String.format( - "\n%s - %s!", httpStatusLine.getStatusCode(), httpStatusLine.getReasonPhrase() - )); - } - } - } -} \ No newline at end of file diff --git a/src/main/resources/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder/config.jelly b/src/main/resources/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder/config.jelly index 932c8d4..4ef15db 100644 --- a/src/main/resources/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/osfbuildersuiteforsfcc/dataimport/DataImportBuilder/config.jelly @@ -16,14 +16,6 @@ - - - - - - Business Manager credentials of type "OSF Builder Suite :: Business Manager Credentials" - for the SFCC instance where this data should be imported. - diff --git a/src/main/webapp/help/projectConfig-tempDirectory.html b/src/main/webapp/help/projectConfig-tempDirectory.html index 31a6d8f..bc1abd6 100644 --- a/src/main/webapp/help/projectConfig-tempDirectory.html +++ b/src/main/webapp/help/projectConfig-tempDirectory.html @@ -4,5 +4,5 @@ it will also be automatically cleaned up before each build.

- For example: "tmp/data_import" + For example: "tmp/metadata"