Merge sshkeys

This commit is contained in:
Nick O'Leary
2017-12-20 15:12:10 +00:00
14 changed files with 1704 additions and 82 deletions

View File

@@ -63,6 +63,9 @@ var storageModuleInterface = {
storageModuleInterface.projects = storageModule.projects;
}
}
if (storageModule.sshkeys) {
storageModuleInterface.sshkeys = storageModule.sshkeys;
}
return storageModule.init(runtime.settings,runtime);
},
getFlows: function() {

View File

@@ -24,6 +24,7 @@ var library = require("./library");
var sessions = require("./sessions");
var runtimeSettings = require("./settings");
var projects = require("./projects");
var sshkeys = require("./sshkeys");
var initialFlowLoadComplete = false;
var settings;
@@ -60,6 +61,7 @@ var localfilesystem = {
runtimeSettings.init(settings);
promises.push(library.init(settings));
promises.push(projects.init(settings, runtime));
promises.push(sshkeys.init(settings, runtime));
var packageFile = fspath.join(settings.userDir,"package.json");
var packagePromise = when.resolve();
@@ -94,7 +96,8 @@ var localfilesystem = {
saveSessions: sessions.saveSessions,
getLibraryEntry: library.getLibraryEntry,
saveLibraryEntry: library.saveLibraryEntry,
projects: projects
projects: projects,
sshkeys: sshkeys
};
module.exports = localfilesystem;

View File

@@ -18,6 +18,7 @@
var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");
var os = require('os');
var gitTools = require("./git");
var util = require("../util");
@@ -753,6 +754,7 @@ function createProject(user, metadata) {
if (metadata.git && metadata.git.remotes && metadata.git.remotes.origin) {
var originRemote = metadata.git.remotes.origin;
var auth;
console.log('originRemote:', originRemote);
if (originRemote.hasOwnProperty("username") && originRemote.hasOwnProperty("password")) {
authCache.set(project,originRemote.url,username,{ // TODO: hardcoded remote name
username: originRemote.username,
@@ -761,6 +763,15 @@ function createProject(user, metadata) {
);
auth = authCache.get(project,originRemote.url,username);
}
else if (originRemote.hasOwnProperty("key_file") && originRemote.hasOwnProperty("passphrase")) {
var key_file_name = (username === '_') ? '.default' + '_' + originRemote.key_file : username + '_' + originRemote.key_file;
authCache.set(project,originRemote.url,username,{ // TODO: hardcoded remote name
key_path: fspath.join(projectsDir, ".sshkeys", key_file_name),
passphrase: originRemote.passphrase
}
);
auth = authCache.get(project,originRemote.url,username);
}
return gitTools.clone(originRemote,auth,projectPath).then(function(result) {
// Check this is a valid project
// If it is empty

View File

@@ -45,7 +45,7 @@ var ResponseServer = function(auth) {
parts.push(data.substring(0, m));
data = data.substring(m);
var line = parts.join("");
console.log("LINE",line);
console.log("LINE:",line);
parts = [];
if (line==='Username') {
connection.end(auth.username);
@@ -79,8 +79,54 @@ var ResponseServer = function(auth) {
});
}
var ResponseSSHServer = function(auth) {
return new Promise(function(resolve, reject) {
server = net.createServer(function(connection) {
connection.setEncoding('utf8');
var parts = [];
connection.on('data', function(data) {
var m = data.indexOf("\n");
if (m !== -1) {
parts.push(data.substring(0, m));
data = data.substring(m);
var line = parts.join("");
console.log("LINE:",line);
parts = [];
if (line==='The') {
connection.end('yes');
// server.close();
} else if (line === 'Enter') {
connection.end(auth.passphrase);
// server.close();
} else {
}
}
if (data.length > 0) {
parts.push(data);
}
});
});
var listenPath = getListenPath();
server.listen(listenPath, function(ready) {
resolve({path:listenPath,close:function() { server.close(); }});
});
server.on('close', function() {
// console.log("Closing response server");
fs.removeSync(listenPath);
});
server.on('error',function(err) {
console.log("ResponseServer unexpectedError:",err.toString());
server.close();
reject(err);
})
});
}
module.exports = {
ResponseServer: ResponseServer
ResponseServer: ResponseServer,
ResponseSSHServer: ResponseSSHServer
}

View File

@@ -18,6 +18,7 @@ var when = require('when');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var authResponseServer = require('./authServer').ResponseServer;
var sshResponseServer = require('./authServer').ResponseSSHServer;
var clone = require('clone');
var path = require("path");
@@ -50,6 +51,8 @@ function runGitCommand(args,cwd,env) {
err.code = "git_auth_failed";
} else if(/HTTP Basic: Access denied/.test(stderr)) {
err.code = "git_auth_failed";
} else if(/Permission denied \(publickey\)/.test(stderr)) {
err.code = "git_auth_failed";
} else if(/Connection refused/.test(stderr)) {
err.code = "git_connection_failed";
} else if (/commit your changes or stash/.test(stderr)) {
@@ -80,6 +83,22 @@ function runGitCommandWithAuth(args,cwd,auth) {
})
}
function runGitCommandWithSSHCommand(args,cwd,auth) {
return sshResponseServer(auth).then(function(rs) {
var commandEnv = clone(process.env);
commandEnv.SSH_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh");
commandEnv.DISPLAY = "dummy:0";
commandEnv.NODE_RED_GIT_NODE_PATH = process.execPath;
commandEnv.NODE_RED_GIT_SOCK_PATH = rs.path;
commandEnv.NODE_RED_GIT_ASKPASS_PATH = path.join(__dirname,"authWriter.js");
commandEnv.GIT_SSH_COMMAND = "ssh -i " + auth.key_path + " -F /dev/null";
// console.log('commandEnv:', commandEnv);
return runGitCommand(args,cwd,commandEnv).finally(function() {
rs.close();
});
})
}
function cleanFilename(name) {
if (name[0] !== '"') {
return name;
@@ -362,7 +381,12 @@ module.exports = {
}
var promise;
if (auth) {
promise = runGitCommandWithAuth(args,cwd,auth);
if ( auth.key_path ) {
promise = runGitCommandWithSSHCommand(args,cwd,auth);
}
else {
promise = runGitCommandWithAuth(args,cwd,auth);
}
} else {
promise = runGitCommand(args,cwd)
}
@@ -393,7 +417,12 @@ module.exports = {
args.push("--porcelain");
var promise;
if (auth) {
promise = runGitCommandWithAuth(args,cwd,auth);
if ( auth.key_path ) {
promise = runGitCommandWithSSHCommand(args,cwd,auth);
}
else {
promise = runGitCommandWithAuth(args,cwd,auth);
}
} else {
promise = runGitCommand(args,cwd)
}
@@ -418,7 +447,12 @@ module.exports = {
}
args.push(".");
if (auth) {
return runGitCommandWithAuth(args,cwd,auth);
if ( auth.key_path ) {
return runGitCommandWithSSHCommand(args,cwd,auth);
}
else {
return runGitCommandWithAuth(args,cwd,auth);
}
} else {
return runGitCommand(args,cwd);
}
@@ -477,7 +511,12 @@ module.exports = {
fetch: function(cwd,remote,auth) {
var args = ["fetch",remote];
if (auth) {
return runGitCommandWithAuth(args,cwd,auth);
if ( auth.key_path ) {
return runGitCommandWithSSHCommand(args,cwd,auth);
}
else {
return runGitCommandWithAuth(args,cwd,auth);
}
} else {
return runGitCommand(args,cwd);
}

View File

@@ -0,0 +1,190 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");
var keygen = require('ssh-keygen');
var settings;
var runtime;
var log;
var sshkeyDir;
function init(_settings, _runtime) {
settings = _settings;
runtime = _runtime;
log = runtime.log;
sshkeyDir = fspath.join(settings.userDir, "projects", ".sshkeys");
// console.log('sshkeys.init()');
return createSSHKeyDirectory();
}
function createSSHKeyDirectory() {
return fs.ensureDir(sshkeyDir);
}
function listSSHKeys(username) {
var startStr = username + '_';
// console.log('sshkeyDir:', sshkeyDir);
return fs.readdir(sshkeyDir).then(function(fns) {
var ret = fns.sort()
.filter(function(fn) {
var fullPath = fspath.join(sshkeyDir,fn);
if (fn.length > 2 || fn[0] != ".") {
var stats = fs.lstatSync(fullPath);
if (stats.isFile()) {
return fn.startsWith(startStr);
}
}
return false;
})
.map(function(filename) {
return filename.substr(startStr.length);
})
.reduce(function(prev, current) {
var parsePath = fspath.parse(current);
if ( parsePath ) {
if ( parsePath.ext !== '.pub' ) {
// Private Keys
prev.keyFiles.push(parsePath.base);
}
else if ( parsePath.ext === '.pub' && (prev.keyFiles.some(function(elem){ return elem === parsePath.name; }))) {
prev.privateKeyFiles.push(parsePath.name);
}
}
return prev;
}, { keyFiles: [], privateKeyFiles: [] });
return ret.privateKeyFiles.map(function(filename) {
return {
name: filename
};
});
});
}
function getSSHKey(username, name) {
return checkSSHKeyFileAndGetPublicKeyFileName(username, name)
.then(function(publicSSHKeyPath) {
return fs.readFile(publicSSHKeyPath, 'utf-8');
});
}
function generateSSHKey(username, options) {
options = options || {};
var name = options.name || "";
return checkExistSSHKeyFiles(username, name)
.then(function(result) {
if ( result ) {
throw new Error('Some SSH Keyfile exists');
}
else {
var email = options.email || "";
var password = options.password || "";
var size = options.size || 2048;
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
return generateSSHKeyPair(privateKeyFilePath, email, password, size)
.then(function() {
return name;
});
}
})
.then(function(keyfile_name) {
return checkSSHKeyFileAndGetPublicKeyFileName(username, name)
.then(function() {
return keyfile_name;
})
.catch(function() {
throw new Error('Failed to generate ssh key files');
});
});
}
function deleteSSHKey(username, name) {
return checkSSHKeyFileAndGetPublicKeyFileName(username, name)
.then(function() {
return deleteSSHKeyFiles(username, name);
});
}
function checkExistSSHKeyFiles(username, name) {
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub');
return Promise.race([
fs.access(privateKeyFilePath, (fs.constants || fs).R_OK),
fs.access(publicKeyFilePath , (fs.constants || fs).R_OK)
])
.then(function() {
return true;
})
.catch(function() {
return false;
});
}
function checkSSHKeyFileAndGetPublicKeyFileName(username, name) {
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub');
return Promise.all([
fs.access(privateKeyFilePath, (fs.constants || fs).R_OK),
fs.access(publicKeyFilePath , (fs.constants || fs).R_OK)
])
.then(function() {
return publicKeyFilePath;
});
}
function deleteSSHKeyFiles(username, name) {
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub');
return Promise.all([
fs.remove(privateKeyFilePath),
fs.remove(publicKeyFilePath)
])
.then(function(retArray) {
return true;
});
}
function generateSSHKeyPair(privateKeyPath, comment, password, size) {
return new Promise(function(resolve, reject) {
keygen({
location: privateKeyPath,
comment: comment,
password: password,
size: size
}, function(err, out) {
if ( err ) {
reject(err);
}
else {
resolve();
}
});
});
}
module.exports = {
init: init,
listSSHKeys: listSSHKeys,
getSSHKey: getSSHKey,
generateSSHKey: generateSSHKey,
deleteSSHKey: deleteSSHKey
};