From abd059a9c5a993f7bab35e1cba9eefebbc018b80 Mon Sep 17 00:00:00 2001
From: "anh.phamtu"
Date: Fri, 9 Aug 2024 14:03:07 +0700
Subject: [PATCH] MARP-434 auth by ssh keypair
---
.github/workflows/ci.yml | 15 +-
sftp-connector-product/README.md | 40 ++++-
.../images/RebexTinySftpServer.exe.config.png | Bin 12428 -> 24712 bytes
.../sftp/test/SftpProcessSSHTest.java | 144 ++++++++++++++++++
.../connector/sftp/test/SftpProcessTest.java | 3 +-
sftp-connector/config/variables.yaml | 17 ++-
sftp-connector/pom.xml | 4 +-
.../processes/Sftp/SftpHelper.p.json | 6 +-
.../sftp/service/SftpClientService.java | 15 +-
9 files changed, 230 insertions(+), 14 deletions(-)
create mode 100644 sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 24865a5..0a3dc5a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -33,16 +33,29 @@ jobs:
- name: Install and start SFTP
run: |
sudo apt install openssh-server
+ sudo sh -c 'echo "ChallengeResponseAuthentication no" >> /etc/ssh/sshd_config'
+ sudo sh -c 'echo "PasswordAuthentication no" >> /etc/ssh/sshd_config'
sudo systemctl enable ssh
sudo systemctl start ssh
-
+
- name: Create a test user account
run: |
sshGroupRaw=$(getent group | grep ssh)
sshGroup=${sshGroupRaw%:x*}
echo "adding user to group ${sshGroup}"
sudo useradd -s /bin/bash -d /home/usr -m -g ${sshGroup} -p $(echo pwd | openssl passwd -1 -stdin) usr
+
+ ssh-keygen -t rsa -b 4096 -N "123456" -f ~/.ssh/sftptest
+ chmod -R 700 ~/.ssh/sftptest
+ chmod 600 ~/.ssh/sftptest.pub
+ sudo -u usr mkdir /home/usr/.ssh/
+ sudo cat ~/.ssh/sftptest.pub >> /home/usr/.ssh/authorized_keys
+ sudo chown -R usr:${sshGroup} /home/usr/.ssh
+ sudo chmod -R 700 /home/usr/.ssh
+ sudo chmod 664 /home/usr/.ssh/authorized_keys
+ cp ~/.ssh/sftptest ${GITHUB_WORKSPACE}/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/sftptest
+
- name: Setup Maven
uses: stCarolas/setup-maven@v5
with:
diff --git a/sftp-connector-product/README.md b/sftp-connector-product/README.md
index 4caf2be..2304ede 100644
--- a/sftp-connector-product/README.md
+++ b/sftp-connector-product/README.md
@@ -53,6 +53,8 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co
1. Open the following settings in “RebexTinySftpServer.exe.config” with a text editor and update the following values:
![RebexTinySftpServer.exe.config](images/RebexTinySftpServer.exe.config.png)
+ \* In order to test the connector with SSH key pair, put the public key file to folder `c:/sshkey`.
+
2. Open the `configuration/variables.yaml` in your Designer and update the following global variables:
```
@@ -62,7 +64,10 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co
com.axonivy.connector.sftp.server:
# The host name to the SFTP server
host: 'localhost'
-
+
+ # Auth type to the SFPT server: password OR ssh
+ auth: 'password'
+
# The password to the SFTP server
password: pwd
@@ -74,7 +79,38 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co
```
-4. Save the changed settings.
+ Or in order to enable the connector with SSH keypair, update following global variables:
+ ```
+
+ Variables:
+
+ com.axonivy.connector.sftp.server:
+ # The host name to the SFTP server
+ host: 'localhost'
+
+ # Auth type to the SFPT server: password OR ssh
+ auth: 'ssh'
+
+ # The password to the SFTP server
+ password: ''
+
+ # The port number to the SFTP server
+ port: 22
+
+ # The username to the SFTP server
+ username: 'usr'
+
+ # The ssh key string to SFTP server
+ # [secret private key]
+ secret_sshkey: |
+ YOUR PRIVATE KEY CONTENT HERE
+
+ # The ssh key passphrase
+ secret_sshpassphrase: 'Your ssh key passphrase'
+ ```
+ \* the private key is in pair of the public key put in step 1
+
+3. Save the changed settings.
### Prerequisites:
diff --git a/sftp-connector-product/images/RebexTinySftpServer.exe.config.png b/sftp-connector-product/images/RebexTinySftpServer.exe.config.png
index 16dddd863d8a42ced456b8b3362e0abfab2969c1..f54c67ea9df288ddaa7ac5e23f5a0f45cebeb6f0 100644
GIT binary patch
literal 24712
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
diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java
new file mode 100644
index 0000000..698bda2
--- /dev/null
+++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java
@@ -0,0 +1,144 @@
+package com.axonivy.connector.sftp.test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+import com.axonivy.connector.sftp.service.SftpClientService;
+import com.axonivy.connector.sftp.service.SftpClientService.FileData;
+
+import ch.ivyteam.ivy.bpm.engine.client.BpmClient;
+import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement;
+import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess;
+import ch.ivyteam.ivy.bpm.engine.client.sub.SubProcessCallResult;
+import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest;
+import ch.ivyteam.ivy.environment.Ivy;
+import ch.ivyteam.ivy.scripting.objects.File;
+
+
+/**
+ * This SftpProcessTest simulates SFTP operations by calling the sub processes:
+ * SftpUploadFile and SftpDownloadFile.
+ *
+ * The test can either be run
+ * - in the Designer IDE (
right click > run as > JUnit Test
)
+ * - or in a Maven continuous integration build pipeline (
mvn clean verify
)
+ *
+ *
+ * Detailed guidance on writing these kind of tests can be found in our
+ * Process Testing docs
+ *
+ */
+@IvyProcessTest(enableWebServer = true)
+public class SftpProcessSSHTest {
+
+ private static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper");
+ private static final BpmProcess TEST_UPLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpUploadFile");
+ private static final BpmProcess TEST_DOWNLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpDownloadFile");
+
+ private static final String TEST_FILE_NAME = "market_market_connector_sftp.pdf";
+ private static final long TEST_FILE_SIZE = 207569L;
+
+
+ @BeforeAll
+ public static void init() throws Exception {
+ String prefix = "com_axonivy_connector_sftp_server_";
+ Ivy.var().set(prefix+"auth", "ssh");
+ Ivy.var().set(prefix+"password", "");
+
+ String keyString = Files.readString(Paths.get(SftpProcessSSHTest.class.getResource("sftptest").toURI()));
+ Ivy.var().set(prefix+"secret_sshkey", keyString);
+ Ivy.var().set(prefix+"secret_sshpassphrase", "123456");
+ }
+
+ @Test
+ @Order(1)
+ public void callOpenConnection(BpmClient bpmClient) throws Exception {
+ BpmElement startable = TEST_HELPER_PROCESS.elementName("openConnection()");
+
+ SubProcessCallResult result = bpmClient.start()
+ .subProcess(startable)
+ .execute() // Callable sub process input arguments
+ .subResult();
+
+ SftpClientService sftpClient = result.param("sftpClient", SftpClientService.class);
+ assertThat(sftpClient).isNotNull();
+ if (sftpClient != null) {
+ sftpClient.close();
+ }
+ }
+
+ @Test
+ @Order(2)
+ public void callUploadFile(BpmClient bpmClient) {
+ InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME);
+
+ BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(InputStream,String)");
+
+ SubProcessCallResult result = bpmClient.start()
+ .subProcess(startable)
+ .execute(fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments
+ .subResult();
+
+ Boolean isSuccess = result.param("isSuccess", Boolean.class);
+ assertThat(isSuccess).isTrue();
+ }
+
+ @Test
+ @Order(3)
+ public void callUploadIvyFile(BpmClient bpmClient) throws IOException {
+ InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME);
+ java.io.File javaFile = new java.io.File(TEST_FILE_NAME);
+ FileUtils.copyInputStreamToFile(fileToBeUploaded, javaFile);
+
+ File ivyFile = new File(TEST_FILE_NAME, true);
+ FileUtils.moveFile(javaFile, ivyFile.getJavaFile());
+
+ BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(File)");
+
+ SubProcessCallResult result = bpmClient.start()
+ .subProcess(startable)
+ .execute(ivyFile) // Callable sub process input arguments
+ .subResult();
+
+ Boolean isSuccess = result.param("isSuccess", Boolean.class);
+ assertThat(isSuccess).isTrue();
+ }
+
+ @Test
+ @Order(4)
+ public void callListAllFiles(BpmClient bpmClient) {
+ BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("listAllFiles(String)");
+
+ SubProcessCallResult result = bpmClient.start()
+ .subProcess(startable)
+ .execute(".") // Callable sub process input arguments
+ .subResult();
+ List listFiles = result.param("listFiles", List.class);
+ assertThat(listFiles.size()).isGreaterThanOrEqualTo(1);
+ assertThat(listFiles).anyMatch(f -> f.getName().equals(TEST_FILE_NAME));
+ }
+
+ @Test
+ @Order(5)
+ public void callDownloadFile(BpmClient bpmClient) {
+ BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("downloadFile(String)");
+
+ SubProcessCallResult result = bpmClient.start()
+ .subProcess(startable)
+ .execute(TEST_FILE_NAME) // Callable sub process input arguments
+ .subResult();
+ java.io.File downloadedFile = result.param("toFile", java.io.File.class);
+ assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE);
+ assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME);
+ }
+}
diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java
index e486466..fc93b9b 100644
--- a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java
+++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java
@@ -7,6 +7,7 @@
import java.util.List;
import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -35,6 +36,7 @@
*
*/
@IvyProcessTest(enableWebServer = true)
+@Disabled
public class SftpProcessTest {
private static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper");
@@ -124,5 +126,4 @@ public void callDownloadFile(BpmClient bpmClient) {
assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE);
assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME);
}
-
}
diff --git a/sftp-connector/config/variables.yaml b/sftp-connector/config/variables.yaml
index 5a9221e..afe98cb 100644
--- a/sftp-connector/config/variables.yaml
+++ b/sftp-connector/config/variables.yaml
@@ -4,12 +4,21 @@ Variables:
# The host name to the SFTP server
host: 'localhost'
- # The password to the SFTP server
- # [password]
- password: pwd
-
# The port number to the SFTP server
port: 22
# The username to the SFTP server
username: 'usr'
+
+ # Auth type to the SFPT server: password OR ssh
+ auth: 'ssh'
+
+ # The password to the SFTP server
+ # [password]
+ password: ''
+
+ # The ssh key string to SFTP server
+ # [secret private key]
+ secret.sshkey: ''
+ # The ssh key passphrase
+ secret.sshpassphrase: ''
diff --git a/sftp-connector/pom.xml b/sftp-connector/pom.xml
index 119c50d..6d43de1 100644
--- a/sftp-connector/pom.xml
+++ b/sftp-connector/pom.xml
@@ -11,9 +11,9 @@
- com.jcraft
+ com.github.mwiede
jsch
- 0.1.55
+ 0.2.19
diff --git a/sftp-connector/processes/Sftp/SftpHelper.p.json b/sftp-connector/processes/Sftp/SftpHelper.p.json
index 20840f8..3e064b1 100644
--- a/sftp-connector/processes/Sftp/SftpHelper.p.json
+++ b/sftp-connector/processes/Sftp/SftpHelper.p.json
@@ -58,11 +58,13 @@
"}",
"String username = ivy.var.variable(prefix+\"username\").value();",
"String password = ivy.var.variable(prefix+\"password\").value();",
+ "String auth = ivy.var.get(prefix+\"auth\");",
+ "String ssh = ivy.var.get(prefix+\"secret_sshkey\");",
+ "String sshpassphrase = ivy.var.get(prefix+\"secret_sshpassphrase\");",
"",
"ivy.log.debug(\"The following settings will be used to connect to the SFTP server: hostname: {0}, port: {1}, username: {2}. Connection in progress...\", ",
" host, port, username);",
- "",
- "in.sftpClient = new SftpClientService(host, port, username, password);",
+ "in.sftpClient = new SftpClientService(host, port, username, auth, password, ssh, sshpassphrase);",
"",
"ivy.log.debug(\"Connection established.\");"
]
diff --git a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java
index 775b4b0..21c04a5 100644
--- a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java
+++ b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java
@@ -8,7 +8,9 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Properties;
+import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.jcraft.jsch.ChannelSftp;
@@ -28,6 +30,7 @@ public class SftpClientService implements AutoCloseable {
private static final String PATHSEPARATOR = "/";
private static final int SESSION_TIMEOUT = 10000;
private static final int CHANNEL_TIMEOUT = 5000;
+ private static final String PASSWORD = "password";
/**
@@ -44,17 +47,25 @@ public class SftpClientService implements AutoCloseable {
* Instantiates the SftpClientService object with given the host, port, username and password.
*
* @param host the host name
+ * @param authType authentication type: password, ssh
* @param port the port number
* @param username the user name
* @param password the password
+ * @param keyString the ssh key string
+ * @param passphrase the ssh passphrase
* @throws IOException
*/
- public SftpClientService(String host, int port, String username, String password) throws IOException {
+ public SftpClientService(String host, int port, String username, String authType, String password, String keyString, String passphrase) throws IOException {
try {
JSch jsch = new JSch();
session = jsch.getSession(username, host, port);
- session.setPassword(password);
+ if (StringUtils.isEmpty(authType) || PASSWORD.equalsIgnoreCase(authType)) {
+ session.setPassword(password);
+ } else {
+ session.setConfig("PreferredAuthentications", "publickey");
+ jsch.addIdentity(null, keyString.getBytes(), null, passphrase.getBytes());
+ }
session.setConfig("StrictHostKeyChecking", "no");
// 10 seconds session timeout