/** * 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 fspath = require("path"); var keygen = require("./keygen"); var settings; var log = require("@node-red/util").log; var sshkeyDir; var userSSHKeyDir; function init(_settings) { settings = _settings; sshkeyDir = fspath.resolve(fspath.join(settings.userDir, "projects", ".sshkeys")); userSSHKeyDir = fspath.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH, ".ssh"); // console.log('sshkeys.init()'); return fs.ensureDir(sshkeyDir); } function listSSHKeys(username) { return listSSHKeysInDir(sshkeyDir,username + '_').then(function(customKeys) { return listSSHKeysInDir(userSSHKeyDir).then(function(existingKeys) { existingKeys.forEach(function(k){ k.system = true; customKeys.push(k); }) return customKeys; }); }); } function listSSHKeysInDir(dir,startStr) { startStr = startStr || ""; return fs.readdir(dir).then(function(fns) { var ret = fns.sort() .filter(function(fn) { var fullPath = fspath.join(dir,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 }; }); }).then(function(result) { return result; }).catch(function() { return [] }); } function getSSHKey(username, name) { return checkSSHKeyFileAndGetPublicKeyFileName(username, name) .then(function(publicSSHKeyPath) { return fs.readFile(publicSSHKeyPath, 'utf-8'); }).catch(function() { var privateKeyPath = fspath.join(userSSHKeyDir,name); var publicKeyPath = privateKeyPath+".pub"; return checkFilePairExist(privateKeyPath,publicKeyPath).then(function() { return fs.readFile(publicKeyPath, 'utf-8'); }).catch(function() { return null }); }); } function generateSSHKey(username, options) { options = options || {}; var name = options.name || ""; if (!/^[a-zA-Z0-9\-_]+$/.test(options.name)) { var err = new Error("Invalid SSH Key name"); e.code = "invalid_key_name"; return Promise.reject(err); } return checkExistSSHKeyFiles(username, name) .then(function(result) { if ( result ) { var e = new Error("SSH Key name exists"); e.code = "key_exists"; throw e; } else { var comment = options.comment || ""; var password = options.password || ""; var size = options.size || 2048; var sshKeyFileBasename = username + '_' + name; var privateKeyFilePath = fspath.normalize(fspath.join(sshkeyDir, sshKeyFileBasename)); return generateSSHKeyPair(name, privateKeyFilePath, comment, password, size) } }) } 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 checkFilePairExist(privateKeyFilePath,publicKeyFilePath) .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 checkFilePairExist(privateKeyFilePath,publicKeyFilePath).then(function() { return publicKeyFilePath; }); } function checkFilePairExist(privateKeyFilePath,publicKeyFilePath) { return Promise.all([ fs.access(privateKeyFilePath, (fs.constants || fs).R_OK), fs.access(publicKeyFilePath , (fs.constants || fs).R_OK) ]) } 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) ]) } function generateSSHKeyPair(name, privateKeyPath, comment, password, size) { log.trace("ssh-keygen["+[name,privateKeyPath,comment,size,"hasPassword?"+!!password].join(",")+"]"); return keygen.generateKey({location: privateKeyPath, comment: comment, password: password, size: size}) .then(function(stdout) { return name; }) .catch(function(err) { log.log('[SSHKey generation] error:', err); throw err; }); } function getPrivateKeyPath(username, name) { var sshKeyFileBasename = username + '_' + name; var privateKeyFilePath = fspath.normalize(fspath.join(sshkeyDir, sshKeyFileBasename)); try { fs.accessSync(privateKeyFilePath, (fs.constants || fs).R_OK); } catch(err) { privateKeyFilePath = fspath.join(userSSHKeyDir,name); try { fs.accessSync(privateKeyFilePath, (fs.constants || fs).R_OK); } catch(err2) { return null; } } if (fspath.sep === '\\') { privateKeyFilePath = privateKeyFilePath.replace(/\\/g,'\\\\'); } return privateKeyFilePath; } module.exports = { init: init, listSSHKeys: listSSHKeys, getSSHKey: getSSHKey, getPrivateKeyPath: getPrivateKeyPath, generateSSHKey: generateSSHKey, deleteSSHKey: deleteSSHKey };