Fix reauthentication of remote repositories

This commit is contained in:
Nick O'Leary 2017-12-21 17:40:24 +00:00
parent 3c6ba72a2a
commit 9c350311e8
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
9 changed files with 184 additions and 286 deletions

View File

@ -50,15 +50,27 @@ RED.projects.userSettings = (function() {
.appendTo(title) .appendTo(title)
.click(function(evt) { .click(function(evt) {
addKeyButton.attr('disabled',true); addKeyButton.attr('disabled',true);
addKeyDialog.slideDown(200, function() { addKeyDialog.slideDown(200);
// addKeyDialog[0].scrollIntoView(); keyNameInput.focus();
}); saveButton.attr('disabled',true);
}); });
var validateForm = function() { var validateForm = function() {
var validName = /^[a-zA-Z0-9\-_]+$/.test(keyNameInput.val()); var validName = /^[a-zA-Z0-9\-_]+$/.test(keyNameInput.val());
saveButton.attr('disabled',!validName); var passphrase = passphraseInput.val();
var validPassphrase = passphrase.length === 0 || passphrase.length >= 8;
saveButton.attr('disabled',!validName || !validPassphrase);
keyNameInput.toggleClass('input-error',keyNameInputChanged&&!validName); keyNameInput.toggleClass('input-error',keyNameInputChanged&&!validName);
passphraseInput.toggleClass('input-error',!validPassphrase);
if (!validPassphrase) {
passphraseInputSubLabel.text("Passphrase too short");
} else if (passphrase.length === 0) {
passphraseInputSubLabel.text("Optional");
} else {
passphraseInputSubLabel.text("");
}
if (popover) { if (popover) {
popover.close(); popover.close();
popover = null; popover = null;
@ -68,8 +80,8 @@ RED.projects.userSettings = (function() {
var row = $('<div class="user-settings-row"></div>').appendTo(container); var row = $('<div class="user-settings-row"></div>').appendTo(container);
var addKeyDialog = $('<div class="projects-dialog-list-dialog"></div>').hide().appendTo(row); var addKeyDialog = $('<div class="projects-dialog-list-dialog"></div>').hide().appendTo(row);
$('<div class="projects-dialog-list-dialog-header">').text('Generate SSH Key').appendTo(addKeyDialog); $('<div class="projects-dialog-list-dialog-header">').text('Generate SSH Key').appendTo(addKeyDialog);
var addKeyDialogBody = $('<div>').appendTo(addKeyDialog);
row = $('<div class="user-settings-row"></div>').appendTo(addKeyDialog); row = $('<div class="user-settings-row"></div>').appendTo(addKeyDialogBody);
$('<label for=""></label>').text('Name').appendTo(row); $('<label for=""></label>').text('Name').appendTo(row);
var keyNameInput = $('<input type="text">').appendTo(row).on("change keyup paste",function() { var keyNameInput = $('<input type="text">').appendTo(row).on("change keyup paste",function() {
keyNameInputChanged = true; keyNameInputChanged = true;
@ -78,10 +90,10 @@ RED.projects.userSettings = (function() {
var keyNameInputChanged = false; var keyNameInputChanged = false;
$('<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>').appendTo(row).find("small"); $('<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>').appendTo(row).find("small");
row = $('<div class="user-settings-row"></div>').appendTo(addKeyDialog); row = $('<div class="user-settings-row"></div>').appendTo(addKeyDialogBody);
$('<label for=""></label>').text('Passphrase').appendTo(row); $('<label for=""></label>').text('Passphrase').appendTo(row);
passphraseInput = $('<input type="password">').appendTo(row).on("change keyup paste",validateForm); passphraseInput = $('<input type="password">').appendTo(row).on("change keyup paste",validateForm);
$('<label class="projects-edit-form-sublabel"><small>Optional</small></label>').appendTo(row).find("small"); var passphraseInputSubLabel = $('<label class="projects-edit-form-sublabel"><small>Optional</small></label>').appendTo(row).find("small");
var hideEditForm = function() { var hideEditForm = function() {
addKeyButton.attr('disabled',false); addKeyButton.attr('disabled',false);
@ -128,7 +140,7 @@ RED.projects.userSettings = (function() {
done(error); done(error);
}, },
200: function(data) { 200: function(data) {
refreshSSHKeyList(); refreshSSHKeyList(payload.name);
done(); done();
}, },
400: { 400: {
@ -166,7 +178,7 @@ RED.projects.userSettings = (function() {
utils.sendRequest(options); utils.sendRequest(options);
var formButtons = $('<span class="button-row" style="position: relative; float: right; margin: 10px;"></span>').appendTo(row); var formButtons = $('<span class="button-row" style="position: relative; float: right; margin: 10px;"></span>').appendTo(row);
$('<button class="editor-button editor-button-small">Copy to Clipboard</button>') $('<button class="editor-button editor-button-small">Copy to clipboard</button>')
.appendTo(formButtons) .appendTo(formButtons)
.click(function(evt) { .click(function(evt) {
evt.preventDefault(); evt.preventDefault();
@ -258,10 +270,13 @@ RED.projects.userSettings = (function() {
] ]
}); });
}); });
if (entry.expand) {
expandedRow = expandKey(container,entry);
}
} }
}); });
var refreshSSHKeyList = function() { var refreshSSHKeyList = function(justAdded) {
$.getJSON("settings/user/keys",function(result) { $.getJSON("settings/user/keys",function(result) {
if (result.keys) { if (result.keys) {
result.keys.sort(function(A,B) { result.keys.sort(function(A,B) {
@ -269,6 +284,9 @@ RED.projects.userSettings = (function() {
}); });
keyList.editableList('empty'); keyList.editableList('empty');
result.keys.forEach(function(key) { result.keys.forEach(function(key) {
if (key.name === justAdded) {
key.expand = true;
}
keyList.editableList('addItem',key); keyList.editableList('addItem',key);
}) })
} }
@ -278,137 +296,10 @@ RED.projects.userSettings = (function() {
} }
function sendSSHKeyManagementAPI(type, param, overlay, successCallback, failCallback) {
var url;
var method;
var payload;
switch(type) {
case 'GET_KEY_LIST':
method = 'GET';
url = "settings/user/keys";
break;
case 'GET_KEY_DETAIL':
method = 'GET';
url = "settings/user/keys/" + param;
break;
case 'GENERATE_KEY':
method = 'POST';
url = "settings/user/keys";
payload= param;
break;
case 'DELETE_KEY':
method = 'DELETE';
url = "settings/user/keys/" + param;
break;
default:
console.error('Unexpected type....');
return;
}
// var spinner = utils.addSpinnerOverlay(gitconfigContainer);
var spinner = overlay ? utils.addSpinnerOverlay(overlay) : null;
var done = function(err) {
if ( spinner ) {
spinner.remove();
}
if (err) {
console.log(err);
return;
}
};
console.log('method:', method);
console.log('url:', url);
utils.sendRequest({
url: url,
type: method,
responses: {
0: function(error) {
if ( failCallback ) {
failCallback(error);
}
done(error);
},
200: function(data) {
if ( successCallback ) {
successCallback(data);
}
done();
},
400: {
'unexpected_error': function(error) {
console.log(error);
if ( failCallback ) {
failCallback(error);
}
done(error);
}
},
}
},payload);
}
var dialog;
var dialogBody;
function createPublicKeyDialog() {
dialog = $('<div id="projects-dialog" class="hide node-red-dialog projects-edit-form"><form class="form-horizontal"></form></div>')
.appendTo("body")
.dialog({
modal: true,
autoOpen: false,
width: 600,
resize: false,
open: function(e) {
$(this).parent().find(".ui-dialog-titlebar-close").hide();
},
close: function(e) {
}
});
dialogBody = dialog.find("form");
dialog.dialog('option', 'title', 'SSH public key');
dialog.dialog('option', 'buttons', [
{
text: RED._("common.label.close"),
click: function() {
$( this ).dialog( "close" );
}
},
{
text: "Copy to Clipboard",
click: function() {
var target = document.getElementById('public-key-data');
document.getSelection().selectAllChildren(target);
var ret = document.execCommand('copy');
var msg = ret ? 'successful' : 'unsuccessful';
console.log('Copy text command was ' + msg);
$( this ).dialog("close");
}
}
]);
dialog.dialog({position: { 'my': 'center', 'at': 'center', 'of': window }});
var container = $('<div class="projects-dialog-screen-start"></div>');
$('<div class="projects-dialog-ssh-public-key-name"></div>').appendTo(container);
$('<div class="projects-dialog-ssh-public-key"><pre id="public-key-data"></pre></div>').appendTo(container);
dialogBody.append(container);
}
function setDialogContext(name, data) {
var title = dialog.find("div.projects-dialog-ssh-public-key-name");
title.text(name);
var context = dialog.find("div.projects-dialog-ssh-public-key>pre");
context.text(data);
}
function createSettingsPane(activeProject) { function createSettingsPane(activeProject) {
var pane = $('<div id="user-settings-tab-gitconfig" class="project-settings-tab-pane node-help"></div>'); var pane = $('<div id="user-settings-tab-gitconfig" class="project-settings-tab-pane node-help"></div>');
createGitUserSection(pane); createGitUserSection(pane);
createSSHKeySection(pane); createSSHKeySection(pane);
createPublicKeyDialog();
return pane; return pane;
} }

View File

@ -539,9 +539,9 @@ RED.projects = (function() {
$(".projects-dialog-screen-create-row-creds").hide(); $(".projects-dialog-screen-create-row-creds").hide();
$(".projects-dialog-screen-create-row-passphrase").show(); $(".projects-dialog-screen-create-row-passphrase").show();
$(".projects-dialog-screen-create-row-sshkey").show(); $(".projects-dialog-screen-create-row-sshkey").show();
if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) { // if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) {
valid = false; // valid = false;
} // }
} else if (/^https?:\/\//.test(repo)) { } else if (/^https?:\/\//.test(repo)) {
$(".projects-dialog-screen-create-row-creds").show(); $(".projects-dialog-screen-create-row-creds").show();
$(".projects-dialog-screen-create-row-passphrase").hide(); $(".projects-dialog-screen-create-row-passphrase").hide();
@ -576,7 +576,7 @@ RED.projects = (function() {
} }
} }
} }
$("#projects-dialog-create").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid); $("#projects-dialog-create").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
} }
@ -757,14 +757,21 @@ RED.projects = (function() {
projectRepoPasswordInput = $('<input id="projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow); projectRepoPasswordInput = $('<input id="projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(container); row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(container);
$('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH Private key file</label>').appendTo(row);
projectRepoSSHKeySelect = createSSHKeyList({ $('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH Key</label>').appendTo(row);
height: "120px", projectRepoSSHKeySelect = $("<select>").appendTo(row);
selectAction: function(entry, header) {
$('.projects-dialog-sshkey-list-entry').removeClass('selected'); $.getJSON("settings/user/keys", function(data) {
header.addClass('selected'); var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
// projectRepoSSHKeySelect
} }
}).appendTo(row); });
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-passphrase"></div>').hide().appendTo(container); row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-passphrase"></div>').hide().appendTo(container);
$('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH key passphrase</label>').appendTo(row); $('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH key passphrase</label>').appendTo(row);
@ -838,17 +845,17 @@ RED.projects = (function() {
var repoUrl = projectRepoInput.val(); var repoUrl = projectRepoInput.val();
var metaData = {}; var metaData = {};
if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) { if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) {
var selected = getSelectedSSHKey(projectRepoSSHKeySelect); var selected = projectRepoSSHKeySelect.val();//false;//getSelectedSSHKey(projectRepoSSHKeySelect);
if ( selected && selected.name ) { if ( selected ) {
projectData.git = { projectData.git = {
remotes: { remotes: {
'origin': { 'origin': {
url: repoUrl, url: repoUrl,
key_file: selected.name, key_file: selected,
passphrase: projectRepoPassphrase.val() passphrase: projectRepoPassphrase.val()
} }
} }
}; };
} }
else { else {
console.log("Error! Can't get selected SSH key path."); console.log("Error! Can't get selected SSH key path.");
@ -1156,86 +1163,8 @@ RED.projects = (function() {
return container; return container;
} }
function createSSHKeyList(options) {
options = options || {};
var minHeight = "33px";
var maxHeight = options.height || "120px";
var container = $('<div></div>',{style:"max-height: "+maxHeight+";"});
var sshkeyList = $('<ol>',{class:"projects-dialog-sshkey-list", style:"max-height:"+maxHeight+";min-height:"+minHeight+";"}).appendTo(container).editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(row,index,entry) {
var header = $('<div></div>',{class:"projects-dialog-sshkey-list-entry"}).appendTo(row);
$('<span class="projects-dialog-sshkey-list-entry-icon"><i class="fa fa-key"></i></span>').appendTo(header);
$('<span class="projects-dialog-sshkey-list-entry-name" style=""></span>').text(entry.name).appendTo(header);
var deleteButton = $('<span/>',{class:"projects-dialog-sshkey-list-entry-icon projects-dialog-sshkey-list-button-remove editor-button editor-button-small"})
.hide()
.appendTo(header)
.click(function(evt) {
evt.preventDefault();
console.log('deleteButton --- click');
if ( options.deleteAction ) {
options.deleteAction(entry, header);
}
return false;
});
$('<i/>',{class:"fa fa-trash-o"}).appendTo(deleteButton);
header.addClass("selectable");
row.click(function(evt) {
if ( !deleteButton.is(":visible") ) {
if ( options.selectAction ) {
options.selectAction(entry, header);
}
$.data(container[0], 'selected', entry);
}
return false;
})
}
});
$.getJSON("settings/user/keys", function(data) {
data.keys.forEach(function(key) {
if ( sshkeyList ) {
sshkeyList.editableList('addItem',key);
}
else {
console.log('[create] Error! selectedSSHKey is not set up.');
}
});
});
if ( sshkeyList ) {
sshkeyList.addClass("projects-dialog-sshkey-list-small");
$.data(container[0], 'selected', null);
$.data(container[0], 'sshkeys', sshkeyList);
}
return container;
}
function getSelectedSSHKey(container) {
var selected = $.data(container[0], 'selected');
if ( container && selected ) {
return selected;
}
else {
return null;
}
}
function refreshSSHKeyList(container) {
var sshkeyList = $.data(container[0], 'sshkeys');
if ( container && sshkeyList ) {
sshkeyList.empty();
$.getJSON("settings/user/keys", function(data) {
var keyList = $.data(container[0], 'sshkeys');
data.keys.forEach(function(key) {
if ( keyList ) {
keyList.editableList('addItem',key);
}
else {
console.log('[refresh] Error! selectedSSHKey is not set up.');
}
});
});
}
}
function sendRequest(options,body) { function sendRequest(options,body) {
// dialogBody.hide(); // dialogBody.hide();
@ -1306,12 +1235,36 @@ RED.projects = (function() {
return; return;
} else if (options.handleAuthFail !== false && xhr.responseJSON.error === 'git_auth_failed') { } else if (options.handleAuthFail !== false && xhr.responseJSON.error === 'git_auth_failed') {
var url = activeProject.git.remotes[options.remote||'origin'].fetch; var url = activeProject.git.remotes[options.remote||'origin'].fetch;
var message = $('<div>'+ var message = $('<div>'+
'<div class="form-row">Authentication required for repository:</div>'+ '<div class="form-row">Authentication required for repository:</div>'+
'<div class="form-row"><div style="margin-left: 20px;">'+url+'</div></div>'+ '<div class="form-row"><div style="margin-left: 20px;">'+url+'</div></div>'+
'<div class="form-row"><label for="projects-user-auth-username">Username</label><input id="projects-user-auth-username" type="text"></input></div>'+
'<div class="form-row"><label for=projects-user-auth-password">Password</label><input id="projects-user-auth-password" type="password"></input></div>'+
'</div>'); '</div>');
var isSSH = false;
if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
isSSH = true;
var row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-key">SSH Key</label>').appendTo(row);
var projectRepoSSHKeySelect = $('<select id="projects-user-auth-key">').width('70%').appendTo(row);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
// projectRepoSSHKeySelect
}
});
row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-passphrase">Passphrase</label>').appendTo(row);
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row);
} else {
$('<div class="form-row"><label for="projects-user-auth-username">Username</label><input id="projects-user-auth-username" type="text"></input></div>'+
'<div class="form-row"><label for=projects-user-auth-password">Password</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
}
var notification = RED.notify(message,{ var notification = RED.notify(message,{
type:"error", type:"error",
fixed: true, fixed: true,
@ -1327,14 +1280,15 @@ RED.projects = (function() {
},{ },{
text: $('<span><i class="fa fa-refresh"></i> Retry</span>'), text: $('<span><i class="fa fa-refresh"></i> Retry</span>'),
click: function() { click: function() {
var username = $('#projects-user-auth-username').val();
var password = $('#projects-user-auth-password').val();
body = body || {}; body = body || {};
var authBody = {git:{remotes:{}}}; var authBody = {};
authBody.git.remotes[options.remote||'origin'] = { if (isSSH) {
username: username, authBody.keyFile = $('#projects-user-auth-key').val();
password: password authBody.passphrase = $('#projects-user-auth-passphrase').val();
}; } else {
authBody.username = $('#projects-user-auth-username').val();
authBody.password = $('#projects-user-auth-password').val();
}
var done = function(err) { var done = function(err) {
if (err) { if (err) {
console.log("Failed to update auth"); console.log("Failed to update auth");
@ -1346,7 +1300,7 @@ RED.projects = (function() {
} }
sendRequest({ sendRequest({
url: "projects/"+activeProject.name, url: "projects/"+activeProject.name+"/remotes/"+(options.remote||'origin'),
type: "PUT", type: "PUT",
responses: { responses: {
0: function(error) { 0: function(error) {
@ -1361,7 +1315,7 @@ RED.projects = (function() {
} }
}, },
} }
},authBody); },{auth:authBody});
} }
} }
] ]
@ -1553,9 +1507,7 @@ RED.projects = (function() {
var projectsAPI = { var projectsAPI = {
sendRequest:sendRequest, sendRequest:sendRequest,
createBranchList:createBranchList, createBranchList:createBranchList,
addSpinnerOverlay:addSpinnerOverlay, addSpinnerOverlay:addSpinnerOverlay
createSSHKeyList:createSSHKeyList,
refreshSSHKeyList:refreshSSHKeyList
}; };
RED.projects.settings.init(projectsAPI); RED.projects.settings.init(projectsAPI);
RED.projects.userSettings.init(projectsAPI); RED.projects.userSettings.init(projectsAPI);

View File

@ -816,6 +816,7 @@ div.projects-dialog-ssh-public-key {
} }
} }
.projects-dialog-list-dialog { .projects-dialog-list-dialog {
position: relative;
margin-top: 10px; margin-top: 10px;
margin-bottom: 20px; margin-bottom: 20px;
background: white; background: white;

View File

@ -512,6 +512,23 @@ module.exports = {
}); });
}); });
// Update a remote
app.put("/:id/remotes/:remoteName", needsPermission("projects.write"), function(req,res) {
var projectName = req.params.id;
var remoteName = req.params.remoteName;
runtime.storage.projects.updateRemote(req.user, projectName, remoteName, req.body).then(function(data) {
res.status(204).end();
})
.catch(function(err) {
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; return app;
} }
} }

View File

@ -63,12 +63,15 @@ module.exports = {
// console.log('username:', username); // console.log('username:', username);
runtime.storage.sshkeys.getSSHKey(username, req.params.id) runtime.storage.sshkeys.getSSHKey(username, req.params.id)
.then(function(data) { .then(function(data) {
res.json({ if (data) {
publickey: data res.json({
}); publickey: data
});
} else {
res.status(404).end();
}
}) })
.catch(function(err) { .catch(function(err) {
console.log(err.stack);
if (err.code) { if (err.code) {
res.status(400).json({error:err.code, message: err.message}); res.status(400).json({error:err.code, message: err.message});
} else { } else {
@ -90,7 +93,6 @@ module.exports = {
}); });
}) })
.catch(function(err) { .catch(function(err) {
console.log(err.stack);
if (err.code) { if (err.code) {
res.status(400).json({error:err.code, message: err.message}); res.status(400).json({error:err.code, message: err.message});
} else { } else {
@ -107,11 +109,10 @@ module.exports = {
app.delete("/:id", needsPermission("settings.write"), function(req,res) { app.delete("/:id", needsPermission("settings.write"), function(req,res) {
var username = getUsername(req.user); var username = getUsername(req.user);
runtime.storage.sshkeys.deleteSSHKey(username, req.params.id) runtime.storage.sshkeys.deleteSSHKey(username, req.params.id)
.then(function(ret) { .then(function() {
res.status(204).end(); res.status(204).end();
}) })
.catch(function(err) { .catch(function(err) {
console.log(err.stack);
if (err.code) { if (err.code) {
res.status(400).json({error:err.code, message: err.message}); res.status(400).json({error:err.code, message: err.message});
} else { } else {

View File

@ -120,12 +120,14 @@ Project.prototype.loadRemotes = function() {
}).then(function() { }).then(function() {
var allRemotes = Object.keys(project.remotes); var allRemotes = Object.keys(project.remotes);
var match = ""; var match = "";
allRemotes.forEach(function(remote) { if (project.branches.remote) {
if (project.branches.remote.indexOf(remote) === 0 && match.length < remote.length) { allRemotes.forEach(function(remote) {
match = remote; if (project.branches.remote.indexOf(remote) === 0 && match.length < remote.length) {
} match = remote;
}); }
project.currentRemote = project.parseRemoteBranch(project.branches.remote).remote; });
project.currentRemote = project.parseRemoteBranch(project.branches.remote).remote;
}
}); });
} }
@ -541,8 +543,21 @@ Project.prototype.addRemote = function(user,remote,options) {
}); });
} }
Project.prototype.updateRemote = function(user,remote,options) { Project.prototype.updateRemote = function(user,remote,options) {
// TODO: once the sshkey support is added, move the updating of remotes, var username;
// including their auth details, down here. if (!user) {
username = "_";
} else {
username = user.username;
}
if (options.auth) {
var url = this.remotes[remote].fetch;
if (options.auth.keyFile) {
options.auth.key_path = fspath.join(projectsDir, ".sshkeys", ((username === '_')?'__default':username) + '_' + options.auth.keyFile);
}
authCache.set(this.name,url,username,options.auth);
}
return Promise.resolve();
} }
Project.prototype.removeRemote = function(user, remote) { Project.prototype.removeRemote = function(user, remote) {
// TODO: if this was the last remote using this url, then remove the authCache // TODO: if this was the last remote using this url, then remove the authCache
@ -764,7 +779,7 @@ function createProject(user, metadata) {
auth = authCache.get(project,originRemote.url,username); auth = authCache.get(project,originRemote.url,username);
} }
else if (originRemote.hasOwnProperty("key_file") && originRemote.hasOwnProperty("passphrase")) { else if (originRemote.hasOwnProperty("key_file") && originRemote.hasOwnProperty("passphrase")) {
var key_file_name = (username === '_') ? '.default' + '_' + originRemote.key_file : username + '_' + originRemote.key_file; var key_file_name = (username === '_') ? '__default' + '_' + originRemote.key_file : username + '_' + originRemote.key_file;
authCache.set(project,originRemote.url,username,{ // TODO: hardcoded remote name authCache.set(project,originRemote.url,username,{ // TODO: hardcoded remote name
key_path: fspath.join(projectsDir, ".sshkeys", key_file_name), key_path: fspath.join(projectsDir, ".sshkeys", key_file_name),
passphrase: originRemote.passphrase passphrase: originRemote.passphrase

View File

@ -266,6 +266,10 @@ function removeRemote(user, project, remote) {
checkActiveProject(project); checkActiveProject(project);
return activeProject.removeRemote(user, remote); return activeProject.removeRemote(user, remote);
} }
function updateRemote(user, project, remote, body) {
checkActiveProject(project);
return activeProject.updateRemote(user, remote, body);
}
function getActiveProject(user) { function getActiveProject(user) {
return activeProject; return activeProject;
@ -491,6 +495,7 @@ module.exports = {
getRemotes: getRemotes, getRemotes: getRemotes,
addRemote: addRemote, addRemote: addRemote,
removeRemote: removeRemote, removeRemote: removeRemote,
updateRemote: updateRemote,
getFlows: getFlows, getFlows: getFlows,
saveFlows: saveFlows, saveFlows: saveFlows,

View File

@ -80,6 +80,8 @@ function getSSHKey(username, name) {
return checkSSHKeyFileAndGetPublicKeyFileName(username, name) return checkSSHKeyFileAndGetPublicKeyFileName(username, name)
.then(function(publicSSHKeyPath) { .then(function(publicSSHKeyPath) {
return fs.readFile(publicSSHKeyPath, 'utf-8'); return fs.readFile(publicSSHKeyPath, 'utf-8');
}).catch(function() {
return null;
}); });
} }
@ -89,29 +91,32 @@ function generateSSHKey(username, options) {
return checkExistSSHKeyFiles(username, name) return checkExistSSHKeyFiles(username, name)
.then(function(result) { .then(function(result) {
if ( result ) { if ( result ) {
throw new Error('Some SSH Keyfile exists'); var e = new Error("SSH Key name exists");
} e.code = "key_exists";
else { throw e;
} else {
var comment = options.comment || ""; var comment = options.comment || "";
var password = options.password || ""; var password = options.password || "";
if (password.length > 0 && password.length < 5) {
var e2 = new Error("SSH Key passphrase too short");
e2.code = "key_passphrase_too_short";
throw e2;
}
var size = options.size || 2048; var size = options.size || 2048;
var sshKeyFileBasename = username + '_' + name; var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename); var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
return generateSSHKeyPair(privateKeyFilePath, comment, password, size) return generateSSHKeyPair(name, privateKeyFilePath, comment, password, size)
.then(function() {
return name;
});
} }
}) })
.then(function(keyfile_name) { // .then(function(keyfile_name) {
return checkSSHKeyFileAndGetPublicKeyFileName(username, name) // return checkSSHKeyFileAndGetPublicKeyFileName(username, name)
.then(function() { // .then(function() {
return keyfile_name; // return keyfile_name;
}) // })
.catch(function() { // .catch(function(err) {
throw new Error('Failed to generate ssh key files'); // throw new Error('Failed to generate ssh key files');
}); // });
}); // });
} }
function deleteSSHKey(username, name) { function deleteSSHKey(username, name) {
@ -125,7 +130,7 @@ function checkExistSSHKeyFiles(username, name) {
var sshKeyFileBasename = username + '_' + name; var sshKeyFileBasename = username + '_' + name;
var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename); var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename);
var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub'); var publicKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename + '.pub');
return Promise.race([ return Promise.all([
fs.access(privateKeyFilePath, (fs.constants || fs).R_OK), fs.access(privateKeyFilePath, (fs.constants || fs).R_OK),
fs.access(publicKeyFilePath , (fs.constants || fs).R_OK) fs.access(publicKeyFilePath , (fs.constants || fs).R_OK)
]) ])
@ -157,13 +162,11 @@ function deleteSSHKeyFiles(username, name) {
return Promise.all([ return Promise.all([
fs.remove(privateKeyFilePath), fs.remove(privateKeyFilePath),
fs.remove(publicKeyFilePath) fs.remove(publicKeyFilePath)
]) ]);
.then(function(retArray) {
return true;
});
} }
function generateSSHKeyPair(privateKeyPath, comment, password, size) { function generateSSHKeyPair(name, privateKeyPath, comment, password, size) {
log.trace("ssh-keygen["+[name,privateKeyPath,comment,size,"hasPassword?"+!!password].join(",")+"]");
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
keygen({ keygen({
location: privateKeyPath, location: privateKeyPath,
@ -172,10 +175,11 @@ function generateSSHKeyPair(privateKeyPath, comment, password, size) {
size: size size: size
}, function(err, out) { }, function(err, out) {
if ( err ) { if ( err ) {
err.code = "key_generation_failed";
reject(err); reject(err);
} }
else { else {
resolve(); resolve(name);
} }
}); });
}); });

View File

@ -151,6 +151,18 @@ describe("api/editor/sshkeys", function() {
}); });
}); });
it('GET /settings/user/keys/<key_file_name> --- return 404', function(done) {
mockRuntime.storage.sshkeys.getSSHKey.returns(Promise.resolve(null));
request(app)
.get("/settings/user/keys/NOT_REAL")
.expect(404)
.end(function(err,res) {
if (err) {
return done(err);
}
done();
});
});
it('GET /settings/user/keys --- return Unexpected Error', function(done) { it('GET /settings/user/keys --- return Unexpected Error', function(done) {
var errInstance = new Error("Messages....."); var errInstance = new Error("Messages.....");
mockRuntime.storage.sshkeys.listSSHKeys.returns(Promise.reject(errInstance)); mockRuntime.storage.sshkeys.listSSHKeys.returns(Promise.reject(errInstance));