Skip to content

Commit

Permalink
MARP-434 auth by ssh keypair
Browse files Browse the repository at this point in the history
  • Loading branch information
anh-bolt committed Aug 15, 2024
1 parent d5a6083 commit abd059a
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 14 deletions.
15 changes: 14 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
40 changes: 38 additions & 2 deletions sftp-connector-product/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand All @@ -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
Expand All @@ -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:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>The test can either be run<ul>
* <li>in the Designer IDE ( <code>right click > run as > JUnit Test </code> )</li>
* <li>or in a Maven continuous integration build pipeline ( <code>mvn clean verify</code> )</li>
* </ul></p>
*
* <p>Detailed guidance on writing these kind of tests can be found in our
* <a href="https://developer.axonivy.com/doc/9.2/concepts/testing/process-testing.html">Process Testing docs</a>
* </p>
*/
@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<FileData> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -35,6 +36,7 @@
* </p>
*/
@IvyProcessTest(enableWebServer = true)
@Disabled
public class SftpProcessTest {

private static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper");
Expand Down Expand Up @@ -124,5 +126,4 @@ public void callDownloadFile(BpmClient bpmClient) {
assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE);
assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME);
}

}
17 changes: 13 additions & 4 deletions sftp-connector/config/variables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: ''
4 changes: 2 additions & 2 deletions sftp-connector/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
</properties>
<dependencies>
<dependency>
<groupId>com.jcraft</groupId>
<groupId>com.github.mwiede</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
<version>0.2.19</version>
</dependency>
</dependencies>
<pluginRepositories>
Expand Down
6 changes: 4 additions & 2 deletions sftp-connector/processes/Sftp/SftpHelper.p.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.\");"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";


/**
Expand All @@ -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
Expand Down

0 comments on commit abd059a

Please sign in to comment.