From 4b7a6bc6f9c05264f382bc585b5cb8bcbf82e237 Mon Sep 17 00:00:00 2001 From: Dan Allen Date: Thu, 8 Feb 2018 23:54:38 -0700 Subject: [PATCH] resolves #53 allow private key file to be specified; expand leading ~ --- index.js | 23 +++++++++++++++- test/gulp-ssh-test.js | 61 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 851cbfa..b83e573 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,8 @@ * Licensed under the MIT license. */ +const fs = require('fs') +const os = require('os') const path = require('path') const util = require('util') const EventEmitter = require('events').EventEmitter @@ -47,6 +49,8 @@ GulpSSH.prototype.getClient = function () { ssh.gulpQueue = [] ssh.gulpConnected = false this.connections[ssh.gulpId] = ssh + var options = this.options + var privateKeyFile ssh .on('error', function (err) { @@ -59,7 +63,24 @@ GulpSSH.prototype.getClient = function () { delete ctx.connections[this.gulpId] }) .on('ready', ssh.gulpFlushReady) - .connect(this.options.sshConfig) + + if ((privateKeyFile = options.sshConfig.privateKeyFile)) { + if (privateKeyFile.charAt() === '~' && (path.sep === '\\' + ? /\/|\\/.test(privateKeyFile.charAt(1)) : privateKeyFile.charAt(1) === '/')) { + privateKeyFile = os.homedir() + privateKeyFile.substr(1) + } + var gulpSSH = this + fs.readFile(privateKeyFile, function (err, privateKey) { + if (err) throw err + var sshConfig = Object.assign({}, options.sshConfig, { privateKey }) + delete sshConfig.privateKeyFile + gulpSSH.options = Object.assign({}, options, { sshConfig }) + ssh.connect(sshConfig) + }) + } else { + ssh.connect(options.sshConfig) + } + return ssh } diff --git a/test/gulp-ssh-test.js b/test/gulp-ssh-test.js index fd3f75f..0370d7a 100644 --- a/test/gulp-ssh-test.js +++ b/test/gulp-ssh-test.js @@ -7,6 +7,7 @@ const { expect } = chai const fs = require('fs-extra') const gulp = require('gulp') const GulpSSH = require('..') +const os = require('os') const path = require('path') const { obj: map } = require('through2') @@ -15,11 +16,12 @@ const FIXTURES_DIR = path.join(__dirname, 'fixtures') describe('GulpSSH', () => { let gulpSSH + const privateKeyFile = path.resolve(__dirname, 'etc/ssh/id_rsa') let sshConfig = { host: 'localhost', port: process.env.CI ? 2222 : 22, username: process.env.USER, - privateKey: fs.readFileSync(path.resolve(__dirname, 'etc/ssh/id_rsa')) + privateKey: fs.readFileSync(privateKeyFile) } const collectFiles = (files, cb) => { @@ -42,6 +44,14 @@ describe('GulpSSH', () => { it('should fail if options are not provided', () => { expect(GulpSSH).to.throw('sshConfig required') }) + + it('should defer loading of private key specified in privateKeyFile option', () => { + const localSshConfig = Object.assign({}, sshConfig, { privateKeyFile }) + delete localSshConfig.privateKey + gulpSSH = new GulpSSH({ ignoreErrors: false, sshConfig: localSshConfig }) + expect(gulpSSH.options.sshConfig.privateKeyFile).to.equal(privateKeyFile) + expect(gulpSSH.options.sshConfig).to.not.have.property('privateKey') + }) }) describe('connect', () => { @@ -49,15 +59,62 @@ describe('GulpSSH', () => { gulpSSH.on('error', done) gulpSSH.getClient().gulpReady(function () { expect(this.gulpConnected).to.equal(true) + gulpSSH.close() done() }) }) it('should fail to connect if credentials are bad', (done) => { - gulpSSH.options.sshConfig = Object.assign({}, gulpSSH.options.sshConfig, { username: 'nobody' }) + gulpSSH.options.sshConfig = Object.assign({}, sshConfig, { username: 'nobody' }) gulpSSH.on('error', () => done()) gulpSSH.getClient().gulpReady(() => { expect.fail() + gulpSSH.close() + done() + }) + }) + + it('should load contents of private key on connect if privateKeyFile option is specified', (done) => { + const localSshConfig = Object.assign({}, sshConfig, { privateKeyFile }) + delete localSshConfig.privateKey + gulpSSH = new GulpSSH({ ignoreErrors: false, sshConfig: localSshConfig }) + gulpSSH.on('error', done) + gulpSSH.getClient().gulpReady(function () { + expect(gulpSSH.options.sshConfig.privateKey).to.eql(sshConfig.privateKey) + expect(gulpSSH.options.sshConfig).to.not.have.property('privateKeyFile') + expect(localSshConfig.privateKeyFile).to.equal(privateKeyFile) + expect(localSshConfig).to.not.have.property('privateKey') + gulpSSH.close() + done() + }) + }) + + it('should expand leading tilde in privateKeyFile option', (done) => { + const tildePrivateKeyFile = path.join('~', path.relative(os.homedir(), privateKeyFile)) + const localSshConfig = Object.assign({}, sshConfig, { privateKeyFile: tildePrivateKeyFile }) + delete localSshConfig.privateKey + gulpSSH = new GulpSSH({ ignoreErrors: false, sshConfig: localSshConfig }) + gulpSSH.on('error', done) + gulpSSH.getClient().gulpReady(function () { + expect(gulpSSH.options.sshConfig.privateKey).to.eql(sshConfig.privateKey) + expect(gulpSSH.options.sshConfig).to.not.have.property('privateKeyFile') + expect(localSshConfig.privateKeyFile).to.equal(tildePrivateKeyFile) + expect(localSshConfig).to.not.have.property('privateKey') + gulpSSH.close() + done() + }) + }) + + it('should use private key from privateKeyFile option instead of privateKey option', (done) => { + const localSshConfig = Object.assign({}, sshConfig, { privateKeyFile, privateKey: 'bogus' }) + gulpSSH = new GulpSSH({ ignoreErrors: false, sshConfig: localSshConfig }) + gulpSSH.on('error', done) + gulpSSH.getClient().gulpReady(function () { + expect(gulpSSH.options.sshConfig.privateKey).to.eql(sshConfig.privateKey) + expect(gulpSSH.options.sshConfig).to.not.have.property('privateKeyFile') + expect(localSshConfig.privateKeyFile).to.equal(privateKeyFile) + expect(localSshConfig.privateKey).to.equal('bogus') + gulpSSH.close() done() }) })