Skip to content

Commit

Permalink
Merge pull request #6 from zazuko/sftp-privatekey
Browse files Browse the repository at this point in the history
Add support for SFTP SSH key authentication
  • Loading branch information
martinmaillard authored Nov 26, 2019
2 parents 3ea6c98 + 115e1d2 commit 84ac374
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 26 deletions.
14 changes: 10 additions & 4 deletions packages/ftp/lib/SftpClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@ const FileParser = require('ftp/lib/parser')
const { PassThrough } = require('stream')

class SftpClient {
constructor ({ host, port = 21, user, password }) {
constructor ({ host, port = 22, user, password, privateKey, passphrase }) {
this.host = host
this.port = port
this.user = user
this.password = password
this.privateKey = privateKey
this.passphrase = passphrase
this.client = new SFTP()
this.session = null
}

async connect () {
this.session = await this.client.session({
const options = {
host: this.host,
port: this.port,
username: this.user,
password: this.password
})
password: this.password,
privateKey: this.privateKey,
passphrase: this.passphrase
}

this.session = await this.client.session(options)

return this.session
}
Expand Down
24 changes: 18 additions & 6 deletions packages/ftp/test/list.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* global describe, expect, it */

const fs = require('fs')
const list = require('../list')
const FtpServer = require('./support/FtpServer')
const SftpServer = require('./support/SftpServer')
Expand All @@ -14,23 +15,34 @@ describe('list', () => {
it.each([
[
'on a FTP server with anonymous user',
() => new FtpServer()
() => new FtpServer(),
{}
],
[
'on a FTP server with username/password',
() => new FtpServer({ user: 'test', password: '1234' })
() => new FtpServer({ user: 'test', password: '1234' }),
{}
],
[
'on a SFTP server with anonymous user',
() => new SftpServer()
() => new SftpServer(),
{}
],
[
'on a SFTP server with username/password',
() => new SftpServer({ user: 'test', password: '1234' })
() => new SftpServer({ user: 'test', password: '1234' }),
{}
],
[
'on a SFTP server with private key',
() => new SftpServer({ user: 'test', password: '1234' }),
{ password: undefined, privateKey: fs.readFileSync('test/support/test.key') }
]
])('lists files from the given directory %s', async (label, serverFactory) => {
])('lists files from the given directory %s', async (label, serverFactory, additionalOptions) => {
await withServer(serverFactory, async (server) => {
const stream = await list({ pathname: 'data', ...server.options })
const options = { ...server.options, ...additionalOptions }

const stream = await list({ pathname: 'data', ...options })
const filenames = await getStream.array(stream)

expect(filenames).toEqual(['data/abc.txt', 'data/xyz.txt'])
Expand Down
23 changes: 17 additions & 6 deletions packages/ftp/test/move.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,33 @@ describe('move', () => {
it.each([
[
'on a FTP server with anonymous user',
() => new FtpServer()
() => new FtpServer(),
{}
],
[
'on a FTP server with username/password',
() => new FtpServer({ user: 'test', password: '1234' })
() => new FtpServer({ user: 'test', password: '1234' }),
{}
],
[
'on a SFTP server with anonymous user',
() => new SftpServer()
() => new SftpServer(),
{}
],
[
'on a SFTP server with username/password',
() => new SftpServer({ user: 'test', password: '1234' })
() => new SftpServer({ user: 'test', password: '1234' }),
{}
],
[
'on a SFTP server with private key',
() => new SftpServer({ user: 'test', password: '1234' }),
{ password: undefined, privateKey: fs.readFileSync('test/support/test.key') }
]
])('moves a file from the given place to another place %s', async (label, serverFactory) => {
])('moves a file from the given place to another place %s', async (label, serverFactory, additionalOptions) => {
await withServer(serverFactory, async (server) => {
const options = { ...server.options, ...additionalOptions }

const root = resolve(__dirname, 'support/tmp/move')
const original = resolve(__dirname, 'support/data/xyz.txt')
const source = resolve(root, 'xyz.txt')
Expand All @@ -43,7 +54,7 @@ describe('move', () => {

const input = new Readable({ read: () => input.push(null) })

const stream = await move({ source: 'tmp/move/xyz.txt', target: 'tmp/move/xyz.moved', ...server.options })
const stream = await move({ source: 'tmp/move/xyz.txt', target: 'tmp/move/xyz.moved', ...options })
input.pipe(stream)
await getStream(stream)
const content = await fs.readFile(target)
Expand Down
24 changes: 18 additions & 6 deletions packages/ftp/test/read.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* global describe, expect, it */

const fs = require('fs')
const read = require('../read')
const FtpServer = require('./support/FtpServer')
const SftpServer = require('./support/SftpServer')
Expand All @@ -14,23 +15,34 @@ describe('read', () => {
it.each([
[
'on a FTP server with anonymous user',
() => new FtpServer()
() => new FtpServer(),
{}
],
[
'on a FTP server with username/password',
() => new FtpServer({ user: 'test', password: '1234' })
() => new FtpServer({ user: 'test', password: '1234' }),
{}
],
[
'on a SFTP server with anonymous user',
() => new SftpServer()
() => new SftpServer(),
{}
],
[
'on a SFTP server with username/password',
() => new SftpServer({ user: 'test', password: '1234' })
() => new SftpServer({ user: 'test', password: '1234' }),
{}
],
[
'on a SFTP server with private key',
() => new SftpServer({ user: 'test', password: '1234' }),
{ password: undefined, privateKey: fs.readFileSync('test/support/test.key') }
]
])('read file from the given path %s', async (label, serverFactory) => {
])('read file from the given path %s', async (label, serverFactory, additionalOptions) => {
await withServer(serverFactory, async (server) => {
const stream = await read({ filename: 'data/xyz.txt', ...server.options })
const options = { ...server.options, ...additionalOptions }

const stream = await read({ filename: 'data/xyz.txt', ...options })
const content = await getStream(stream)

expect(content).toBe('987\n654')
Expand Down
17 changes: 13 additions & 4 deletions packages/ftp/test/support/SftpServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,20 @@ class FileSystem extends ImplFileSystem {
}

async authenticate (session, request) {
if (request.method === 'none') {
return true
} else if (request.method !== 'password' ||
const validMethods = ['password', 'publickey', 'none']
const method = request.method

if (!validMethods.includes(method)) {
return validMethods
}

if (
(method === 'none' && this.username) ||
(method === 'password' && (
request.username !== this.username ||
request.password !== this.password) {
request.password !== this.password
))
) {
throw new PermissionDeniedError()
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/ftp/test/support/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const readFile = promisify(fs.readFile)
const rmdir = promisify(rimraf)

module.exports = {
...fs,
copyFile,
createReadStream,
mkdir,
Expand Down

0 comments on commit 84ac374

Please sign in to comment.