Add SSH key management API

This commit is contained in:
Hideki Nakamura
2017-12-07 23:11:24 +09:00
parent 304c597a2f
commit 923893e160
8 changed files with 1140 additions and 46 deletions

View File

@@ -97,6 +97,10 @@ module.exports = {
// User Settings
editorApp.post("/settings/user",needsPermission("settings.write"),info.updateUserSettings,apiUtil.errorHandler);
// SSH keys
var sshkeys = require("./sshkeys");
sshkeys.init(runtime);
editorApp.use("/settings/user/keys",sshkeys.app());
return editorApp;
}

125
red/api/editor/sshkeys.js Normal file
View File

@@ -0,0 +1,125 @@
/**
* 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 express = require("express");
var os = require("os");
var runtime;
var settings;
var needsPermission = require("../auth").needsPermission;
function getUsername(userObj) {
var username = os.hostname();
if ( userObj && userObj.name ) {
username = userObj.name;
}
return username;
}
module.exports = {
init: function(_runtime) {
runtime = _runtime;
settings = runtime.settings;
},
app: function() {
var app = express();
// SSH keys
// List all SSH keys
app.get("/", needsPermission("settings.read"), function(req,res) {
var username = getUsername(req.user);
runtime.storage.sshkeys.listSSHKeys(username)
.then(function(list) {
res.json({
keys: list
});
})
.catch(function(err) {
// console.log(err.stack);
if (err.code) {
res.status(400).json({error:err.code, message: err.message});
} else {
res.status(400).json({error:"unexpected_error", message:err.toString()});
}
});
});
// Get SSH key detail
app.get("/:id", needsPermission("settings.read"), function(req,res) {
var username = getUsername(req.user);
console.log('username:', username);
runtime.storage.sshkeys.getSSHKey(username, req.params.id)
.then(function(data) {
res.json({
publickey: data
});
})
.catch(function(err) {
console.log(err.stack);
if (err.code) {
res.status(400).json({error:err.code, message: err.message});
} else {
res.status(400).json({error:"unexpected_error", message:err.toString()});
}
});
});
// Generate a SSH key
app.post("/", needsPermission("settings.write"), function(req,res) {
var username = getUsername(req.user);
console.log('req.body:', req.body);
if ( req.body && req.body.name ) {
runtime.storage.sshkeys.generateSSHKey(username, "", req.body.name, req.body)
.then(function(name) {
console.log('generate key --- success name:', name);
res.json({
name: name
});
})
.catch(function(err) {
console.log(err.stack);
if (err.code) {
res.status(400).json({error:err.code, message: err.message});
} else {
res.status(400).json({error:"unexpected_error", message:err.toString()});
}
});
}
else {
res.status(400).json({error:"unexpected_error", message:"You need to have body or body.name"});
}
});
// Delete a SSH key
app.delete("/:id", needsPermission("settings.write"), function(req,res) {
var username = getUsername(req.user);
runtime.storage.sshkeys.deleteSSHKey(username, req.params.id)
.then(function(ret) {
res.status(204).end();
})
.catch(function(err) {
console.log(err.stack);
if (err.code) {
res.status(400).json({error:err.code, message: err.message});
} else {
res.status(400).json({error:"unexpected_error", message:err.toString()});
}
});
});
return app;
}
}

View File

@@ -57,6 +57,9 @@ var storageModuleInterface = {
if (storageModule.projects) {
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

@@ -0,0 +1,152 @@
/**
* 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[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, email, name, data) {
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
return generateSSHKeyPair(privateKeyFilePath, email, data.password, data.size)
.then(function() {
return name;
});
}
function deleteSSHKey(username, name) {
return checkSSHKeyFileAndGetPublicKeyFileName(username, name)
.then(function() {
return deleteSSHKeyFiles(username, name);
});
}
function checkSSHKeyFileAndGetPublicKeyFileName(username, name) {
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub');
return when.all([
fs.access(privateKeyFilePath, (fs.constants || fs).R_OK),
fs.access(publicKeyFilePath , (fs.constants || fs).R_OK)
])
.then(function() {
return when.resolve(publicKeyFilePath);
});
}
function deleteSSHKeyFiles(username, name) {
var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub');
return when.all([
fs.remove(privateKeyFilePath),
fs.remove(publicKeyFilePath)
])
.then(function(retArray) {
return when.resolve(true);
});
}
function generateSSHKeyPair(privateKeyPath, comment, password, size) {
return when.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
};