From fe10b8650f8ebf0d5dd334af30b8bbcf7047ac13 Mon Sep 17 00:00:00 2001 From: Hideki Nakamura Date: Sat, 16 Dec 2017 00:07:47 +0900 Subject: [PATCH] Add Git access feature via SSH and Enhance SSH Key management --- editor/js/ui/projectUserSettings.js | 299 ++++++++++++++---- editor/js/ui/projects.js | 146 ++++++++- editor/sass/projects.scss | 88 ++++++ .../localfilesystem/projects/Project.js | 11 + .../projects/git/authServer.js | 50 ++- .../localfilesystem/projects/git/index.js | 59 +++- .../storage/localfilesystem/projects/index.js | 2 +- 7 files changed, 571 insertions(+), 84 deletions(-) diff --git a/editor/js/ui/projectUserSettings.js b/editor/js/ui/projectUserSettings.js index 2401e5985..afb111134 100644 --- a/editor/js/ui/projectUserSettings.js +++ b/editor/js/ui/projectUserSettings.js @@ -16,6 +16,7 @@ RED.projects.userSettings = (function() { + var gitconfigContainer; var gitUsernameInput; var gitEmailInput; @@ -24,11 +25,9 @@ RED.projects.userSettings = (function() { var currentGitSettings = RED.settings.get('git') || {}; currentGitSettings.user = currentGitSettings.user || {}; - - var title = $('

').text("Committer Details").appendTo(pane); - var gitconfigContainer = $('
').appendTo(pane); + gitconfigContainer = $('
').appendTo(pane); $('
').appendTo(gitconfigContainer).text("Leave blank to use system default"); var row = $('
').appendTo(gitconfigContainer); @@ -40,77 +39,245 @@ RED.projects.userSettings = (function() { $('').text('Email').appendTo(row); gitEmailInput = $('').appendTo(row); gitEmailInput.val(currentGitSettings.user.email||""); - // var sshkeyTitle = $('

').text("SSH Keys").appendTo(gitconfigContainer); - // var generateSshKeyButton = $('') - // .appendTo(sshkeyTitle) - // .click(function(evt) { - // console.log('click generateSshKeyButton'); - // }); - // row = $('
').appendTo(gitconfigContainer); - // var sshkeysList = $('
    ').appendTo(row); - // sshkeysList.editableList({ - // addButton: false, - // height: 'auto', - // addItem: function(outer,index,entry) { + var sshkeyTitle = $('

    ').text("SSH Keys").appendTo(gitconfigContainer); + var editSshKeyListButton = $('') + .appendTo(sshkeyTitle) + .click(function(evt) { + editSshKeyListButton.hide(); + formButtons.show(); + sshkeyInputRow.show(); + $(".projects-dialog-sshkey-list-button-remove").css('display', 'inline-block'); + }); + + var sshkeyListOptions = { + height: "300px", + deleteAction: function(entry, header) { + sendSSHKeyManagementAPI("DELETE_KEY", entry.name, function(data) { + hideSSHKeyGenerateForm(); + utils.refreshSSHKeyList(sshkeysList); + }); + }, + selectAction: function(entry, header) { + sendSSHKeyManagementAPI("GET_KEY_DETAIL", entry.name, function(data) { + setDialogContext(entry.name, data.publickey); + dialog.dialog("open"); + }); + } + }; + var sshkeysListRow = $('').appendTo(gitconfigContainer); + var sshkeysList = utils.createSSHKeyList(sshkeyListOptions).appendTo(sshkeysListRow); - // var header = $('
    ').appendTo(outer); - // entry.header = $('').text(entry.path||"Add new remote").appendTo(header); - // var body = $('
    ').appendTo(outer); - // entry.body = body; - // if (entry.path) { - // entry.removeButton = $('') - // // .hide() - // .appendTo(header) - // .click(function(e) { - // entry.removed = true; - // body.fadeOut(100); - // entry.header.css("text-decoration","line-through") - // entry.header.css("font-style","italic") - // if (entry.copyToClipboard) { - // entry.copyToClipboard.hide(); - // } - // $(this).hide(); - // }); - // if (entry.data) { - // entry.copyToClipboard = $('') - // // .hide() - // .appendTo(header) - // .click(function(e) { - // var textarea = document.createElement("textarea"); - // textarea.style.position = 'fixed'; - // textarea.style.top = 0; - // textarea.style.left = 0; - // textarea.style.width = '2em'; - // textarea.style.height = '2em'; - // textarea.style.padding = 0; - // textarea.style.border = 'none'; - // textarea.style.outline = 'none'; - // textarea.style.boxShadow = 'none'; - // textarea.style.background = 'transparent'; - // textarea.value = entry.data; - // document.body.appendChild(textarea); - // textarea.select(); - // try { - // var ret = document.execCommand('copy'); - // var msg = ret ? 'successful' : 'unsuccessful'; - // console.log('Copy text command was ' + msg); - // } catch (err) { - // console.log('Oops unable to copy'); - // } - // document.body.removeChild(textarea); - // }); - // } - // } - // } - // }); + var sshkeyInputRow = $('').hide().appendTo(gitconfigContainer); + var sshkeyNameLabel = $('').text('Key Name').appendTo(sshkeyInputRow); + var sshkeyNameInput = $('').appendTo(sshkeyInputRow); + var sshkeyPassphraseLabel = $('').text('Passphrase').appendTo(sshkeyInputRow); + var sshkeyPassphraseInput = $('').appendTo(sshkeyInputRow); + var sshkeySamePassphraseLabel = $('').text('Same Passphrase').appendTo(sshkeyInputRow); + var sshkeySamePassphraseInput = $('').appendTo(sshkeyInputRow); - // var remoteListAddButton = row.find(".red-ui-editableList-addButton").hide(); + var formButtonArea = $('
    ').appendTo(gitconfigContainer); + var formButtons = $('') + .hide().appendTo(formButtonArea); + + function hideSSHKeyGenerateForm() { + editSshKeyListButton.show(); + formButtons.hide(); + sshkeyInputRow.hide(); + sshkeyNameInput.val(""); + sshkeyPassphraseInput.val(""); + sshkeySamePassphraseInput.val(""); + if ( sshkeyNameInput.hasClass('input-error') ) { + sshkeyNameInput.removeClass('input-error'); + } + if ( sshkeyPassphraseInput.hasClass('input-error') ) { + sshkeyPassphraseInput.removeClass('input-error'); + } + if ( sshkeySamePassphraseInput.hasClass('input-error') ) { + sshkeySamePassphraseInput.removeClass('input-error'); + } + $(".projects-dialog-sshkey-list-button-remove").hide(); + } + + $('') + .appendTo(formButtons) + .click(function(evt) { + evt.preventDefault(); + hideSSHKeyGenerateForm(); + }); + var generateButton = $('') + .appendTo(formButtons) + .click(function(evt) { + evt.preventDefault(); + if ( sshkeyNameInput.hasClass('input-error') ) { + sshkeyNameInput.removeClass('input-error'); + } + if ( sshkeyPassphraseInput.hasClass('input-error') ) { + sshkeyPassphraseInput.removeClass('input-error'); + } + if ( sshkeySamePassphraseInput.hasClass('input-error') ) { + sshkeySamePassphraseInput.removeClass('input-error'); + } + var valid = true; + if ( sshkeyNameInput.val() === "" ) { + sshkeyNameInput.addClass('input-error'); + valid = false; + } + if ( sshkeyPassphraseInput.val() !== sshkeySamePassphraseInput.val() ) { + sshkeySamePassphraseInput.addClass('input-error'); + valid = false; + } + if ( valid ) { + sendSSHKeyManagementAPI("GENERATE_KEY", + { + name: sshkeyNameInput.val(), + email: gitEmailInput.val(), + password: sshkeyPassphraseInput.val(), + size: 4096 + }, + function() { + hideSSHKeyGenerateForm(); + utils.refreshSSHKeyList(sshkeysList); + }, + function(err) { + console.log('err message:', err.message); + if ( err.message.includes('Some SSH Keyfile exists') ) { + sshkeyNameInput.addClass('input-error'); + } + else if ( err.message.includes('Failed to generate ssh key files') ) { + sshkeyPassphraseInput.addClass('input-error'); + sshkeySamePassphraseInput.addClass('input-error'); + } + } + ); + } + }); + } + + function sendSSHKeyManagementAPI(type, param, 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 done = function(err) { + 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 = $('
    ') + .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 = $('
    '); + $('
    ').appendTo(container); + $('
    ').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) { var pane = $('
    '); createRemoteRepositorySection(pane); + createPublicKeyDialog(); return pane; } diff --git a/editor/js/ui/projects.js b/editor/js/ui/projects.js index 0493bad0a..e6a71f728 100644 --- a/editor/js/ui/projects.js +++ b/editor/js/ui/projects.js @@ -54,6 +54,7 @@ RED.projects = (function() { var projectRepoUserInput; var projectRepoPasswordInput; var projectNameSublabel; + var projectRepoSSHKeySelect; var projectRepoPassphrase; var projectRepoRemoteName var projectRepoBranch; @@ -126,12 +127,18 @@ RED.projects = (function() { if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repo)) { $(".projects-dialog-screen-create-row-creds").hide(); $(".projects-dialog-screen-create-row-passphrase").show(); + $(".projects-dialog-screen-create-row-sshkey").show(); + if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) { + valid = false; + } } else if (/^https?:\/\//.test(repo)) { $(".projects-dialog-screen-create-row-creds").show(); $(".projects-dialog-screen-create-row-passphrase").hide(); + $(".projects-dialog-screen-create-row-sshkey").hide(); } else { $(".projects-dialog-screen-create-row-creds").show(); $(".projects-dialog-screen-create-row-passphrase").hide(); + $(".projects-dialog-screen-create-row-sshkey").hide(); } @@ -331,6 +338,16 @@ RED.projects = (function() { $('').appendTo(subrow); projectRepoPasswordInput = $('').appendTo(subrow); + row = $('
    ').hide().appendTo(container); + $('').appendTo(row); + projectRepoSSHKeySelect = createSSHKeyList({ + height: "120px", + selectAction: function(entry, header) { + $('.projects-dialog-sshkey-list-entry').removeClass('selected'); + header.addClass('selected'); + } + }).appendTo(row); + row = $('
    ').hide().appendTo(container); $('').appendTo(row); projectRepoPassphrase = $('').appendTo(row); @@ -397,14 +414,29 @@ RED.projects = (function() { projectData.copy = copyProject.name; } else if (projectType === 'clone') { // projectData.credentialSecret = projectSecretInput.val(); - projectData.git = { - remotes: { - 'origin': { - url: projectRepoInput.val(), - username: projectRepoUserInput.val(), - password: projectRepoPasswordInput.val() + var repoUrl = projectRepoInput.val(); + var metaData = {}; + if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) { + projectData.git = { + remotes: { + 'origin': { + url: repoUrl, + key_file: getSelectedSSHKey(projectRepoSSHKeySelect).name, + passphrase: projectRepoPassphrase.val() + } } - } + }; + } + else { + projectData.git = { + remotes: { + 'origin': { + url: repoUrl, + username: projectRepoUserInput.val(), + password: projectRepoPasswordInput.val() + } + } + }; } } @@ -434,6 +466,8 @@ RED.projects = (function() { projectRepoUserInput.addClass("input-error"); projectRepoPasswordInput.addClass("input-error"); // getRepoAuthDetails(req); + projectRepoSSHKeySelect.addClass("input-error"); + projectRepoPassphrase.addClass("input-error"); console.log("git auth error",error); }, 'unexpected_error': function(error) { @@ -694,6 +728,100 @@ RED.projects = (function() { return container; } + // var selectedSSHKey = null; + // var sshkeyList = null; + $.fn.isVisible = function() { + return $.expr.filters.visible(this[0]); + } + function createSSHKeyList(options) { + options = options || {}; + var minHeight = "33px"; + var maxHeight = options.height || "120px"; + // var container = $('
    ',{style:"min-height: "+height+"; height: "+height+";"}); + var container = $('
    ',{style:"max-height: "+maxHeight+";"}); + + // var sshkeyList = $('
      ',{class:"projects-dialog-sshkey-list", style:"height:"+height}).appendTo(container).editableList({ + var sshkeyList = $('
        ',{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 = $('
        ',{class:"projects-dialog-sshkey-list-entry"}).appendTo(row); + $('').appendTo(header); + $('').text(entry.name).appendTo(header); + var deleteButton = $('',{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; + }); + $('',{class:"fa fa-trash-o"}).appendTo(deleteButton); + header.addClass("selectable"); + row.click(function(evt) { + if ( !deleteButton.isVisible() ) { + 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) { + console.log('key:', 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); + } + console.log('container.sshkeys:', container.data('sshkeys')); + return container; + } + function getSelectedSSHKey(container) { + var selected = $.data(container[0], 'selected'); + if ( container && selected ) { + return selected; + } + else { + return null; + } + } + function refreshSSHKeyList(container) { + console.log('refreshSSHKeyList'); + var sshkeyList = $.data(container[0], 'sshkeys'); + console.log(' ---> container:', container); + console.log(' ---> container.sshkeyList:', sshkeyList); + if ( container && sshkeyList ) { + sshkeyList.empty(); + $.getJSON("settings/user/keys", function(data) { + var keyList = $.data(container[0], 'sshkeys'); + data.keys.forEach(function(key) { + console.log('key:', key); + if ( keyList ) { + keyList.editableList('addItem',key); + } + else { + console.log('[refresh] Error! selectedSSHKey is not set up.'); + } + }); + }); + } + } + function sendRequest(options,body) { // dialogBody.hide(); console.log(options.url,body); @@ -1013,7 +1141,9 @@ RED.projects = (function() { var projectsAPI = { sendRequest:sendRequest, createBranchList:createBranchList, - addSpinnerOverlay:addSpinnerOverlay + addSpinnerOverlay:addSpinnerOverlay, + createSSHKeyList:createSSHKeyList, + refreshSSHKeyList:refreshSSHKeyList }; RED.projects.settings.init(projectsAPI); RED.projects.userSettings.init(projectsAPI); diff --git a/editor/sass/projects.scss b/editor/sass/projects.scss index 410b3da63..cec421b13 100644 --- a/editor/sass/projects.scss +++ b/editor/sass/projects.scss @@ -654,6 +654,94 @@ float: right; } + + +.projects-dialog-sshkey-list { + li { + padding: 0 !important; + } + &.projects-dialog-sshkey-list-small { + .projects-dialog-sshkey-list-entry { + padding: 6px 0; + i { + font-size: 1em; + } + } + .projects-dialog-sshkey-list-entry-name { + font-size: 1em; + } + .projects-dialog-sshkey-list-entry-current { + margin-right: 10px; + padding-top: 2px; + } + } +} +.red-ui-editableList-container { + .projects-dialog-sshkey-list { + li:last-child { + border-bottom: 0px none; + } + } +} +.projects-dialog-sshkey-list-entry { + padding: 12px 0; + border-left: 3px solid #fff; + border-right: 3px solid #fff; + &.sshkey-list-entry-current { + &:not(.selectable) { + background: #f9f9f9; + } + i { + color: #999; + } + } + &.selectable { + cursor: pointer; + &:hover { + background: #f3f3f3; + border-left-color: #aaa; + border-right-color: #aaa; + } + } + i { + color: #ccc; + font-size: 2em; + + } + &.selected { + background: #efefef; + border-left-color:#999; + border-right-color:#999; + } + span { + display: inline-block; + vertical-align:middle; + } + .projects-dialog-sshkey-list-entry-icon { + margin: 0 10px 0 5px; + } + .projects-dialog-sshkey-list-entry-name { + font-size: 1.2em; + } + .projects-dialog-sshkey-list-entry-current { + float: right; + margin-right: 20px; + font-size: 0.9em; + color: #999; + padding-top: 4px; + } + .projects-dialog-sshkey-list-button-remove { + position: absolute; + right: 4px; + } +} + +div.projects-dialog-ssh-public-key { + pre { + word-break: break-all; + } +} + /* .expandable-list-entry { .exandable-list-entry-header { diff --git a/red/runtime/storage/localfilesystem/projects/Project.js b/red/runtime/storage/localfilesystem/projects/Project.js index 67aaaadf9..58d3bb786 100644 --- a/red/runtime/storage/localfilesystem/projects/Project.js +++ b/red/runtime/storage/localfilesystem/projects/Project.js @@ -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"); @@ -680,6 +681,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, @@ -688,6 +690,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 === '_') ? os.hostname() + '_' + 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 diff --git a/red/runtime/storage/localfilesystem/projects/git/authServer.js b/red/runtime/storage/localfilesystem/projects/git/authServer.js index a8048721a..a4ab209e8 100644 --- a/red/runtime/storage/localfilesystem/projects/git/authServer.js +++ b/red/runtime/storage/localfilesystem/projects/git/authServer.js @@ -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 } diff --git a/red/runtime/storage/localfilesystem/projects/git/index.js b/red/runtime/storage/localfilesystem/projects/git/index.js index 2152390e4..374df51ae 100644 --- a/red/runtime/storage/localfilesystem/projects/git/index.js +++ b/red/runtime/storage/localfilesystem/projects/git/index.js @@ -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"); @@ -41,6 +42,11 @@ function runGitCommand(args,cwd,env) { }); child.on('close', function(code) { + console.log("==============================================================="); + console.log("stdout:", stdout); + console.log("==============================================================="); + console.log("stderr:", stderr); + console.log("==============================================================="); if (code !== 0) { var err = new Error(stderr); err.stdout = stdout; @@ -49,6 +55,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)) { @@ -56,9 +64,10 @@ function runGitCommand(args,cwd,env) { } else if (/CONFLICT/.test(err.stdout)) { err.code = "git_pull_merge_conflict"; } - - - + console.log("==============================================================="); + console.log('err:', err); + console.log("==============================================================="); + return reject(err); } resolve(stdout); @@ -78,6 +87,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; @@ -331,7 +356,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) } @@ -362,7 +392,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) } @@ -387,7 +422,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); } @@ -442,7 +482,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); } diff --git a/red/runtime/storage/localfilesystem/projects/index.js b/red/runtime/storage/localfilesystem/projects/index.js index 15003f4d5..20c8515ba 100644 --- a/red/runtime/storage/localfilesystem/projects/index.js +++ b/red/runtime/storage/localfilesystem/projects/index.js @@ -134,7 +134,7 @@ function getProject(user, name) { return Projects.get(name).then(function(project) { var result = project.toJSON(); var projectSettings = settings.get("projects").projects; - if (projectSettings[name].git && projectSettings[name].git.user[username]) { + if (projectSettings[name] && projectSettings[name].git && projectSettings[name].git.user[username]) { result.git.user = projectSettings[name].git.user[username]; } return result;