1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Allow credSecret to be managed via project settings

This commit is contained in:
Nick O'Leary 2017-09-26 22:51:08 +01:00
parent d8fd218409
commit 6a06142e1e
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
14 changed files with 401 additions and 216 deletions

View File

@ -23,7 +23,7 @@ RED.popover = (function() {
}, },
"small": { "small": {
top: 5, top: 5,
leftRight: 8, leftRight: 17,
leftLeft: 16 leftLeft: 16
} }
} }
@ -43,7 +43,7 @@ RED.popover = (function() {
var active; var active;
var div; var div;
var openPopup = function() { var openPopup = function(instant) {
if (active) { if (active) {
div = $('<div class="red-ui-popover red-ui-popover-'+direction+'"></div>').appendTo("body"); div = $('<div class="red-ui-popover red-ui-popover-'+direction+'"></div>').appendTo("body");
if (size !== "default") { if (size !== "default") {
@ -62,7 +62,6 @@ RED.popover = (function() {
var targetPos = target.offset(); var targetPos = target.offset();
var targetWidth = target.width(); var targetWidth = target.width();
var targetHeight = target.height(); var targetHeight = target.height();
var divHeight = div.height(); var divHeight = div.height();
var divWidth = div.width(); var divWidth = div.width();
if (direction === 'right') { if (direction === 'right') {
@ -70,16 +69,23 @@ RED.popover = (function() {
} else if (direction === 'left') { } else if (direction === 'left') {
div.css({top: targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top,left:targetPos.left-deltaSizes[size].leftLeft-divWidth}); div.css({top: targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top,left:targetPos.left-deltaSizes[size].leftLeft-divWidth});
} }
if (instant) {
div.show();
} else {
div.fadeIn("fast"); div.fadeIn("fast");
} }
} }
var closePopup = function() { }
var closePopup = function(instant) {
if (!active) { if (!active) {
if (div) { if (div) {
if (instant) {
$(this).remove();
} else {
div.fadeOut("fast",function() { div.fadeOut("fast",function() {
$(this).remove(); $(this).remove();
}); });
}
div = null; div = null;
} }
} }
@ -114,14 +120,17 @@ RED.popover = (function() {
var res = { var res = {
setContent: function(_content) { setContent: function(_content) {
content = _content; content = _content;
return res;
}, },
open: function () { open: function (instant) {
active = true; active = true;
openPopup(); openPopup(instant);
return res;
}, },
close: function () { close: function (instant) {
active = false; active = false;
closePopup(); closePopup(instant);
return res;
} }
} }
return res; return res;

View File

@ -500,8 +500,6 @@ RED.editor = (function() {
label = RED._("markdownEditor.title"); label = RED._("markdownEditor.title");
} else if (node.type === '_buffer') { } else if (node.type === '_buffer') {
label = RED._("bufferEditor.title"); label = RED._("bufferEditor.title");
} else if (node.type === '_project') {
label = "NLS: Edit project settings";
} else if (node.type === 'subflow') { } else if (node.type === 'subflow') {
label = RED._("subflow.editSubflow",{name:node.name}) label = RED._("subflow.editSubflow",{name:node.name})
} else if (node.type.indexOf("subflow:")===0) { } else if (node.type.indexOf("subflow:")===0) {
@ -2133,76 +2131,6 @@ RED.editor = (function() {
RED.tray.show(trayOptions); RED.tray.show(trayOptions);
} }
function editProject(options) {
var project = options.project;
var onComplete = options.complete;
var type = "_project"
editStack.push({type:type});
RED.view.state(RED.state.EDITING);
var trayOptions = {
title: options.title || getEditStackTitle(),
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
RED.tray.close();
}
},
{
id: "node-dialog-ok",
text: RED._("common.label.done"),
class: "primary",
click: function() {
onComplete("Whheeeeee");
RED.tray.close();
}
}
],
resize: function(dimensions) {
// editTrayWidthCache[type] = dimensions.width;
//
// var rows = $("#dialog-form>div:not(.node-text-editor-row)");
// var editorRow = $("#dialog-form>div.node-text-editor-row");
// var height = $("#dialog-form").height();
// for (var i=0;i<rows.size();i++) {
// height -= $(rows[i]).outerHeight(true);
// }
// height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
// $(".node-text-editor").css("height",height+"px");
// expressionEditor.resize();
},
open: function(tray) {
var trayBody = tray.find('.editor-tray-body');
trayBody.addClass("projects-edit-form");
var dialogForm = buildEditForm(trayBody,'dialog-form',type,'editor');
project._def = projectNodeDefinition;
prepareEditDialog(project,project._def,"node-input-project");
dialogForm.i18n();
},
close: function() {
editStack.pop();
},
show: function() {}
}
if (editTrayWidthCache.hasOwnProperty(type)) {
trayOptions.width = editTrayWidthCache[type];
}
RED.tray.show(trayOptions);
}
var projectNodeDefinition = {
defaults:{
name: {default:""},
summary: {default:""},
key: {default:""}
},
oneditprepare: function() {
}
}
function stringToUTF8Array(str) { function stringToUTF8Array(str) {
var data = []; var data = [];
var i=0, l = str.length; var i=0, l = str.length;
@ -2409,7 +2337,6 @@ RED.editor = (function() {
editJSON: editJSON, editJSON: editJSON,
editMarkdown: editMarkdown, editMarkdown: editMarkdown,
editBuffer: editBuffer, editBuffer: editBuffer,
editProject: editProject,
validateNode: validateNode, validateNode: validateNode,
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo

View File

@ -89,7 +89,7 @@ RED.projects.settings = (function() {
pane.get(project).hide().appendTo(tabContents); pane.get(project).hide().appendTo(tabContents);
}); });
settingsContent.i18n(); settingsContent.i18n();
settingsTabs.activateTab("project-settings-tab-"+(initialTab||'view')) settingsTabs.activateTab("project-settings-tab-"+(initialTab||'main'))
$("#sidebar-shade").show(); $("#sidebar-shade").show();
}, },
close: function() { close: function() {
@ -246,8 +246,10 @@ RED.projects.settings = (function() {
} }
function updateProjectDependencies(activeProject,depsList) { function updateProjectDependencies(activeProject,depsList) {
depsList.editableList('empty'); depsList.editableList('empty');
depsList.editableList('addItem',{index:1, label:"Unknown Dependencies"}); // TODO: nls
depsList.editableList('addItem',{index:3, label:"Unused dependencies"}); // TODO: nls var totalCount = 0;
var unknownCount = 0;
var unusedCount = 0;
for (var m in modulesInUse) { for (var m in modulesInUse) {
if (modulesInUse.hasOwnProperty(m)) { if (modulesInUse.hasOwnProperty(m)) {
@ -257,6 +259,13 @@ RED.projects.settings = (function() {
count: modulesInUse[m].count, count: modulesInUse[m].count,
known: activeProject.dependencies.hasOwnProperty(m) known: activeProject.dependencies.hasOwnProperty(m)
}); });
totalCount++;
if (modulesInUse[m].count === 0) {
unusedCount++;
}
if (!activeProject.dependencies.hasOwnProperty(m)) {
unknownCount++;
}
} }
} }
@ -269,15 +278,31 @@ RED.projects.settings = (function() {
count: 0, count: 0,
known: true known: true
}); });
totalCount++;
unusedCount++;
} }
} }
} }
if (unknownCount > 0) {
depsList.editableList('addItem',{index:1, label:"Unknown Dependencies"}); // TODO: nls
}
if (unusedCount > 0) {
depsList.editableList('addItem',{index:3, label:"Unused dependencies"}); // TODO: nls
}
if (totalCount === 0) {
depsList.editableList('addItem',{index:0, label:"None"}); // TODO: nls
}
} }
function editDependencies(activeProject,depsJSON,container,depsList) { function editDependencies(activeProject,depsJSON,container,depsList) {
var json = depsJSON||JSON.stringify(activeProject.dependencies||{},"",4);
if (json === "{}") {
json = "{\n\n}";
}
RED.editor.editJSON({ RED.editor.editJSON({
title: RED._('sidebar.project.editDependencies'), title: RED._('sidebar.project.editDependencies'),
value: depsJSON||JSON.stringify(activeProject.dependencies||{},"",4), value: json,
requireValid: true, requireValid: true,
complete: function(v) { complete: function(v) {
try { try {
@ -286,7 +311,7 @@ RED.projects.settings = (function() {
var done = function(err,res) { var done = function(err,res) {
if (err) { if (err) {
editDependencies(activeProject,v,container,depsList); return editDependencies(activeProject,v,container,depsList);
} }
activeProject.dependencies = parsed; activeProject.dependencies = parsed;
updateProjectDependencies(activeProject,depsList); updateProjectDependencies(activeProject,depsList);
@ -332,7 +357,11 @@ RED.projects.settings = (function() {
// console.log(entry); // console.log(entry);
var headerRow = $('<div>',{class:"palette-module-header"}).appendTo(row); var headerRow = $('<div>',{class:"palette-module-header"}).appendTo(row);
if (entry.label) { if (entry.label) {
if (entry.index === 0) {
headerRow.addClass("red-ui-search-empty")
} else {
row.parent().addClass("palette-module-section"); row.parent().addClass("palette-module-section");
}
headerRow.text(entry.label); headerRow.text(entry.label);
if (entry.index === 1) { if (entry.index === 1) {
var addButton = $('<button class="editor-button editor-button-small palette-module-button">add to project</button>').appendTo(headerRow).click(function(evt) { var addButton = $('<button class="editor-button editor-button-small palette-module-button">add to project</button>').appendTo(headerRow).click(function(evt) {
@ -358,6 +387,7 @@ RED.projects.settings = (function() {
}); });
} }
} else { } else {
headerRow.addClass("palette-module-header");
headerRow.toggleClass("palette-module-unused",entry.count === 0); headerRow.toggleClass("palette-module-unused",entry.count === 0);
entry.element = headerRow; entry.element = headerRow;
var titleRow = $('<div class="palette-module-meta palette-module-name"></div>').appendTo(headerRow); var titleRow = $('<div class="palette-module-meta palette-module-name"></div>').appendTo(headerRow);
@ -392,6 +422,216 @@ RED.projects.settings = (function() {
} }
function createSettingsPane(activeProject) {
var pane = $('<div id="project-settings-tab-settings" class="project-settings-tab-pane node-help"></div>');
$('<h3></h3>').text("Credentials").appendTo(pane);
var row = $('<div class="user-settings-row"></div>').appendTo(pane);
if (activeProject.settings.credentialsEncrypted) {
$('<span style="margin-right: 20px;"><i class="fa fa-lock"></i> Credentials are encrypted</span>').appendTo(row);
} else {
$('<span style="margin-right: 20px;"><i class="fa fa-unlock"></i> Credentials are not encrypted</span>').appendTo(row);
}
var resetButton;
var action;
var changeButton = $('<button id="" class="editor-button"></button>')
.text(activeProject.settings.credentialsEncrypted?"Change key":"Enable encryption")
.appendTo(row)
.click(function(evt) {
evt.preventDefault();
newKey.val("");
if (currentKey) {
currentKey.val("");
currentKey.removeClass("input-error");
}
checkInputs();
saveButton.text("Save");
$(".project-settings-credentials-row").show();
$(".project-settings-credentials-current-row").show();
$(this).prop('disabled',true);
if (resetButton) {
resetButton.prop('disabled',true);
}
action = 'change';
});
if (activeProject.settings.credentialsEncrypted) {
resetButton = $('<button id="" style="margin-left: 10px;" class="editor-button"></button>')
.text("Reset key")
.appendTo(row)
.click(function(evt) {
evt.preventDefault();
newKey.val("");
if (currentKey) {
currentKey.val("");
currentKey.removeClass("input-error");
}
checkInputs();
saveButton.text("Reset key");
$(".project-settings-credentials-row").show();
$(".project-settings-credentials-reset-row").show();
$(this).prop('disabled',true);
changeButton.prop('disabled',true);
action = 'reset';
});
}
if (activeProject.settings.credentialsInvalid) {
row = $('<div class="user-settings-row"></div>').appendTo(pane);
$('<div class="form-tips form-warning"><i class="fa fa-warning"></i> The current key is not valid. Set the correct key or reset credentials.</div>').appendTo(row);
}
var credentialsContainer = $('<div>',{style:"position:relative"}).appendTo(pane);
var currentKey;
var newKey;
var checkInputs = function() {
var valid = true;
if (newKey.val().length === 0) {
valid = false;
}
if (currentKey && currentKey.val() === 0) {
valid = false;
}
saveButton.toggleClass('disabled',!valid);
}
if (activeProject.settings.credentialsEncrypted) {
if (!activeProject.settings.credentialsInvalid) {
row = $('<div class="user-settings-row project-settings-credentials-current-row hide"></div>').appendTo(credentialsContainer);
$('<label for="">Current key</label>').appendTo(row);
currentKey = $('<input type="password">').appendTo(row);
currentKey.on("change keyup paste",function() {
if (popover) {
popover.close();
popover = null;
$(this).removeClass('input-error');
}
checkInputs();
});
}
row = $('<div class="user-settings-row project-settings-credentials-reset-row hide"></div>').appendTo(credentialsContainer);
$('<div class="form-tips form-warning"><i class="fa fa-warning"></i> Resetting the key will delete all existing credentials</div>').appendTo(row);
}
// $('<label for="" style="margin-left:20px; width: auto;"><input type="radio" name="project-settings-credentials-current" value="lost"> Forgotten key?</label>').appendTo(row);
row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
$('<label for=""></label>').text((activeProject.settings.credentialsEncrypted&& !activeProject.settings.credentialsInvalid)?"New key":"Encryption key").appendTo(row);
newKey = $('<input type="password">').appendTo(row).on("change keyup paste",checkInputs);
row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
var bg = $('<div class="button-group" style="text-align: right; margin-right:20px;"></div>').appendTo(row);
$('<button class="editor-button">Cancel</button>')
.appendTo(bg)
.click(function(evt) {
evt.preventDefault();
if (popover) {
popover.close();
popover = null;
}
changeButton.prop('disabled',false);
if (resetButton) {
resetButton.prop('disabled',false);
}
$(".project-settings-credentials-row").hide();
$(".project-settings-credentials-current-row").hide();
$(".project-settings-credentials-reset-row").hide();
});
var saveButton = $('<button class="editor-button primary disabled"></button>')
.text("Save")
.appendTo(bg)
.click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled')) {
return;
}
var spinner = addSpinnerOverlay(credentialsContainer);
var payload = {
credentialSecret: newKey.val()
};
if (activeProject.settings.credentialsInvalid) {
RED.deploy.setDeployInflight(true);
}
if (activeProject.settings.credentialsEncrypted) {
if (action === 'reset') {
payload.resetCredentialSecret = true;
} else if (!activeProject.settings.credentialsInvalid) {
payload.currentCredentialSecret = currentKey.val();
}
}
var done = function(err,res) {
spinner.remove();
if (err) {
console.log(err);
return;
}
}
utils.sendRequest({
url: "projects/"+activeProject.name,
type: "PUT",
responses: {
0: function(error) {
done(error,null);
},
200: function(data) {
if (popover) {
popover.close();
popover = null;
}
changeButton.prop('disabled',false);
if (resetButton) {
resetButton.prop('disabled',false);
}
$(".project-settings-credentials-row").hide();
$(".project-settings-credentials-current-row").hide();
$(".project-settings-credentials-reset-row").hide();
},
400: {
'unexpected_error': function(error) {
done(error,null);
},
'missing_current_credential_key': function(error) {
currentKey.addClass("input-error");
popover = RED.popover.create({
target: currentKey,
direction: 'right',
size: 'small',
content: "Incorrect key"
}).open();
done();
}
},
}
},payload).always(function() {
if (activeProject.settings.credentialsInvalid) {
RED.deploy.setDeployInflight(false);
}
});
});
// $('<h3></h3>').text("Credentials").appendTo(pane);
// row = $('<div class="user-settings-row"></div>').appendTo(pane);
// $('<span style="margin-right: 20px;"><i class="fa fa-unlock"></i> Credentials are not encrypted</span>').appendTo(row);
// $('<button id="" class="editor-button">Set key</button>').appendTo(row);
// $('<h3></h3>').text("Repository").appendTo(pane);
// row = $('<div class="user-settings-row"></div>').appendTo(pane);
// var input;
// $('<label for="">'+'Remote'+'</label>').appendTo(row);
// $('<input id="" type="text">').appendTo(row);
return pane;
}
var popover;
var utils; var utils;
var modulesInUse = {}; var modulesInUse = {};
function init(_utils) { function init(_utils) {
@ -408,6 +648,17 @@ RED.projects.settings = (function() {
get: createDependenciesPane, get: createDependenciesPane,
close: function() { } close: function() { }
}); });
addPane({
id:'settings',
title: "Settings", // TODO: nls
get: createSettingsPane,
close: function() {
if (popover) {
popover.close();
popover = null;
}
}
});
RED.events.on('nodes:add', function(n) { RED.events.on('nodes:add', function(n) {
if (!/^subflow:/.test(n.type)) { if (!/^subflow:/.test(n.type)) {
@ -444,6 +695,10 @@ RED.projects.settings = (function() {
} }
return { return {
init: init, init: init,
show: show show: show,
switchProject: function(name) {
// TODO: not ideal way to trigger this; should there be an editor-wide event?
modulesInUse = {};
}
}; };
})(); })();

View File

@ -273,61 +273,6 @@ RED.projects = (function() {
] ]
} }
})(), })(),
'credentialSecret': {
content: function() {
// Provide new secret or reset credentials.
var container = $('<div class="projects-dialog-screen-secret"></div>');
var row = $('<div class="form-row"></div>').appendTo(container);
// $('<label for=""><input type="radio" name="projects-dialog-credential-secret" value="set"> Update credential key</label>').appendTo(row);
$('<label for="projects-dialog-secret">Update credential key</label>').appendTo(row);
var projectSecret = $('<input id="projects-dialog-secret" type="password"></input>').appendTo(row);
return container;
},
buttons: [
{
// id: "clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
dialog.dialog( "close" );
}
},
{
// id: "clipboard-dialog-cancel",
text: "Update",
click: function() {
var done = function(err,data) {
if (err) {
console.log(err);
}
dialog.dialog( "close" );
}
RED.deploy.setDeployInflight(true);
sendRequest({
url: "projects/"+activeProject.name,
type: "PUT",
responses: {
200: function(data) {
done(null,data);
},
400: {
'credentials_load_failed': function(error) {
done(error,null);
},
'unexpected_error': function(error) {
done(error,null);
}
},
}
},{credentialSecret:$('#projects-dialog-secret').val()}).always(function() {
RED.deploy.setDeployInflight(false);
})
}
}
]
},
'open': { 'open': {
content: function() { content: function() {
return createProjectList({ return createProjectList({
@ -371,7 +316,7 @@ RED.projects = (function() {
function switchProject(name,done) { function switchProject(name,done) {
RED.deploy.setDeployInflight(true); RED.deploy.setDeployInflight(true);
modulesInUse = {}; RED.projects.settings.switchProject(name);
sendRequest({ sendRequest({
url: "projects/"+name, url: "projects/"+name,
type: "PUT", type: "PUT",
@ -497,7 +442,7 @@ RED.projects = (function() {
console.log(err); console.log(err);
}).always(function() { }).always(function() {
var delta = Date.now() - start; var delta = Date.now() - start;
delta = Math.max(0,1000-delta); delta = Math.max(0,500-delta);
setTimeout(function() { setTimeout(function() {
// dialogBody.show(); // dialogBody.show();
$(".projects-dialog-spinner").hide(); $(".projects-dialog-spinner").hide();
@ -967,7 +912,6 @@ function refresh() {
// updateProjectSummary(); // updateProjectSummary();
// updateProjectDescription(); // updateProjectDescription();
// updateProjectDependencies(); // updateProjectDependencies();
console.log("project triggering a info refresh for ",activeProject)
RED.sidebar.info.refresh(); RED.sidebar.info.refresh();
}); });
} }
@ -987,19 +931,13 @@ function refresh() {
selectProject: function() { selectProject: function() {
show('open') show('open')
}, },
showCredentialsPrompt: function() { showCredentialsPrompt: function() { //TODO: rename this function
show('credentialSecret'); RED.projects.settings.show('settings');
}, },
// showSidebar: showSidebar, // showSidebar: showSidebar,
refresh: refresh, refresh: refresh,
editProject: function() { editProject: function() {
RED.projects.settings.show(); RED.projects.settings.show();
// RED.editor.editProject({
// project: activeProject,
// complete: function(result) {
// console.log(result);
// }
// });
}, },
getActiveProject: function() { getActiveProject: function() {
return activeProject; return activeProject;

View File

@ -109,7 +109,6 @@ RED.sidebar.info = (function() {
return el; return el;
} }
function refresh(node) { function refresh(node) {
console.log('sidebar.info.refresh');
if (node === undefined) { if (node === undefined) {
refreshSelection(); refreshSelection();
return; return;

View File

@ -196,6 +196,10 @@
text-decoration: underline; text-decoration: underline;
} }
.form-warning {
border-color: #d6615f;
}
.node-text-editor { .node-text-editor {
border:1px solid #ccc; border:1px solid #ccc;
border-radius:5px; border-radius:5px;

View File

@ -48,26 +48,26 @@
text-decoration: none; text-decoration: none;
cursor:pointer; cursor:pointer;
&.disabled { &.disabled, &:disabled {
cursor: default; cursor: default;
color: $workspace-button-color-disabled !important; color: $workspace-button-color-disabled !important;
} }
&:hover, &:focus { &:hover, &:focus {
text-decoration: none; text-decoration: none;
} }
&:not(.disabled):hover { &:not(.disabled):not(:disabled):hover, {
color: $workspace-button-color-hover !important; color: $workspace-button-color-hover !important;
background: $workspace-button-background-hover; background: $workspace-button-background-hover;
} }
&:not(.disabled):focus { &:not(.disabled):not(:disabled):focus {
color: $workspace-button-color-focus !important; color: $workspace-button-color-focus !important;
} }
&:not(.disabled):active { &:not(.disabled):not(:disabled):active {
color: $workspace-button-color-active !important; color: $workspace-button-color-active !important;
background: $workspace-button-background-active; background: $workspace-button-background-active;
text-decoration: none; text-decoration: none;
} }
&.selected:not(.disabled) { &.selected:not(.disabled):not(:disabled) {
color: $workspace-button-color-selected !important; color: $workspace-button-color-selected !important;
background: $workspace-button-background-active; background: $workspace-button-background-active;
} }

View File

@ -234,21 +234,6 @@
</div> </div>
</div> </div>
</script> </script>
<script type="text/x-red" data-template-name="_project">
<div class="form-row">
<label>Project name</label>
<input type="text" id="node-input-project-name">
</div>
<div class="form-row">
<label>Summary</label>
<input type="text" id="node-input-project-summary">
</div>
<div class="form-row">
<label>Credential key</label>
<input type="password" id="node-input-project-key">
</div>
</script>
<script src="vendor/vendor.js"></script> <script src="vendor/vendor.js"></script>
<script src="vendor/jsonata/jsonata.min.js"></script> <script src="vendor/jsonata/jsonata.min.js"></script>
<script src="vendor/ace/ace.js"></script> <script src="vendor/ace/ace.js"></script>

View File

@ -49,7 +49,6 @@ module.exports = {
app.post("/", function(req,res) { app.post("/", function(req,res) {
// Create project // Create project
runtime.storage.projects.createProject(req.body).then(function(name) { runtime.storage.projects.createProject(req.body).then(function(name) {
console.log("Created project",name);
runtime.storage.projects.getProject(name).then(function(data) { runtime.storage.projects.getProject(name).then(function(data) {
res.json(data); res.json(data);
}); });

View File

@ -38,6 +38,28 @@ function decryptCredentials(key,credentials) {
return JSON.parse(decrypted); return JSON.parse(decrypted);
} }
function markProjectSecretValid(project,valid) {
try {
var projects = settings.get('projects');
if (projects) {
if (valid) {
if (!projects.projects[project].credentialSecretInvalid) {
return when.resolve();
} else {
delete projects.projects[project].credentialSecretInvalid;
}
} else {
projects.projects[project].credentialSecretInvalid = true;
}
return settings.set('projects',projects);
} else {
return when.resolve();
}
} catch(err) {
return when.resolve();
}
}
var api = module.exports = { var api = module.exports = {
init: function(runtime) { init: function(runtime) {
log = runtime.log; log = runtime.log;
@ -75,16 +97,17 @@ var api = module.exports = {
} }
var projectKey = false; var projectKey = false;
var activeProject;
try { try {
var projects = settings.get('projects'); var projects = settings.get('projects');
if (projects && projects.activeProject) { if (projects && projects.activeProject) {
activeProject = projects.activeProject;
projectKey = projects.projects[projects.activeProject].credentialSecret; projectKey = projects.projects[projects.activeProject].credentialSecret;
} }
} catch(err) { } catch(err) {
} }
if (projectKey) { if (projectKey) {
log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key"); log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key");
console.log(projectKey);
userKey = projectKey; userKey = projectKey;
} }
@ -171,21 +194,32 @@ var api = module.exports = {
encryptedCredentials = credentials; encryptedCredentials = credentials;
} }
return setupEncryptionPromise.then(function() { return setupEncryptionPromise.then(function() {
var clearInvalidFlag = false;
if (credentials.hasOwnProperty("$")) { if (credentials.hasOwnProperty("$")) {
// These are encrypted credentials // These are encrypted credentials
try { try {
credentialCache = decryptCredentials(encryptionKey,credentials) credentialCache = decryptCredentials(encryptionKey,credentials)
clearInvalidFlag = true;
} catch(err) { } catch(err) {
credentialCache = {}; credentialCache = {};
dirty = true; dirty = true;
log.warn(log._("nodes.credentials.error",{message:err.toString()})) log.warn(log._("nodes.credentials.error",{message:err.toString()}))
var error = new Error("Failed to decrypt credentials"); var error = new Error("Failed to decrypt credentials");
error.code = "credentials_load_failed"; error.code = "credentials_load_failed";
if (projectKey) {
// This is a project with a bad key. Mark it as invalid
return markProjectSecretValid(activeProject,false).then(function() {
return when.reject(error);
})
}
return when.reject(error); return when.reject(error);
} }
} else { } else {
credentialCache = credentials; credentialCache = credentials;
} }
if (clearInvalidFlag) {
return markProjectSecretValid(activeProject,true)
}
}); });
}, },
@ -222,6 +256,10 @@ var api = module.exports = {
dirty = true; dirty = true;
}, },
clear: function() {
credentialCache = {};
dirty = true;
},
/** /**
* Deletes any credentials for nodes that no longer exist * Deletes any credentials for nodes that no longer exist
* @param config a flow config * @param config a flow config
@ -321,9 +359,14 @@ var api = module.exports = {
dirty: function() { dirty: function() {
return dirty; return dirty;
}, },
setKey: function(key) {
encryptionKey = crypto.createHash('sha256').update(key).digest();
encryptionEnabled = true;
dirty = true;
},
export: function() { export: function() {
var result = credentialCache; var result = credentialCache;
if (encryptionEnabled) { if (encryptionEnabled) {
if (dirty) { if (dirty) {
try { try {

View File

@ -231,7 +231,6 @@ function handleStatus(node,statusMessage) {
function start(type,diff,muteLog) { function start(type,diff,muteLog) {
console.log("START----")
//dumpActiveNodes(); //dumpActiveNodes();
type = type||"full"; type = type||"full";
started = true; started = true;

View File

@ -172,5 +172,8 @@ module.exports = {
addCredentials: credentials.add, addCredentials: credentials.add,
getCredentials: credentials.get, getCredentials: credentials.get,
deleteCredentials: credentials.delete, deleteCredentials: credentials.delete,
getCredentialDefinition: credentials.getDefinition getCredentialDefinition: credentials.getDefinition,
setCredentialSecret: credentials.setKey,
clearCredentials: credentials.clear,
exportCredentials: credentials.export
}; };

View File

@ -84,16 +84,16 @@ var storageModuleInterface = {
return credentialSavePromise.then(function() { return credentialSavePromise.then(function() {
return storageModule.saveFlows(flows).then(function() { return storageModule.saveFlows(flows).then(function() {
return crypto.createHash('md5').update(JSON.stringify(config)).digest("hex"); return crypto.createHash('md5').update(JSON.stringify(config.flows)).digest("hex");
}) })
}); });
}, },
// getCredentials: function() { // getCredentials: function() {
// return storageModule.getCredentials(); // return storageModule.getCredentials();
// }, // },
// saveCredentials: function(credentials) { saveCredentials: function(credentials) {
// return storageModule.saveCredentials(credentials); return storageModule.saveCredentials(credentials);
// }, },
getSettings: function() { getSettings: function() {
if (settingsAvailable) { if (settingsAvailable) {
return storageModule.getSettings(); return storageModule.getSettings();

View File

@ -131,10 +131,20 @@ function getProject(project) {
projectSettings = globalProjectSettings.projects[project]||{}; projectSettings = globalProjectSettings.projects[project]||{};
} }
// console.log(projectSettings);
var projectData = { var projectData = {
name: project name: project,
settings: {}
}; };
if (typeof projectSettings.credentialSecret === "string") {
projectData.settings.credentialsEncrypted = true;
} else {
projectData.settings.credentialsEncrypted = false;
}
if (projectSettings.credentialSecretInvalid) {
projectData.settings.credentialsInvalid = true;
}
var promises = []; var promises = [];
checkProjectFiles(project).then(function(missingFiles) { checkProjectFiles(project).then(function(missingFiles) {
if (missingFiles.length > 0) { if (missingFiles.length > 0) {
@ -158,17 +168,6 @@ function getProject(project) {
when.settle(promises).then(function() { when.settle(promises).then(function() {
resolve(projectData); resolve(projectData);
}) })
// if (missingFiles.indexOf('flow_cred.json') === -1) {
// promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"flow_cred.json"),"utf8").then(function(creds) {
// var credentials = util.parseJSON(creds);
// if (credentials.hasOwnProperty('$')) {
// // try {
// // decryptCredentials
// // }
// }
// }));
// }
}); });
// fs.stat(projectPath,function(err,stat) { // fs.stat(projectPath,function(err,stat) {
@ -185,22 +184,45 @@ function getProject(project) {
}); });
} }
var encryptionAlgorithm = "aes-256-ctr"; function setCredentialSecret(project,data) { //existingSecret,secret) {
function decryptCredentials(key,credentials) { var existingSecret = data.currentCredentialSecret;
var creds = credentials["$"]; var isReset = data.resetCredentialSecret;
var initVector = new Buffer(creds.substring(0, 32),'hex'); var secret = data.credentialSecret;
creds = creds.substring(32); var wasInvalid = false;
var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
return JSON.parse(decrypted);
}
function setCredentialSecret(project,secret) {
var globalProjectSettings = settings.get("projects"); var globalProjectSettings = settings.get("projects");
globalProjectSettings.projects = globalProjectSettings.projects || {}; globalProjectSettings.projects = globalProjectSettings.projects || {};
globalProjectSettings.projects[project] = globalProjectSettings.projects[project] || {}; if (globalProjectSettings.projects.hasOwnProperty(project)) {
if (!isReset &&
globalProjectSettings.projects[project].credentialSecret &&
!globalProjectSettings.projects[project].credentialSecretInvalid &&
globalProjectSettings.projects[project].credentialSecret !== existingSecret) {
var e = new Error("Cannot change credentialSecret without current key");
e.code = "missing_current_credential_key";
throw e;
}
} else {
globalProjectSettings.projects[project] = {};
}
globalProjectSettings.projects[project].credentialSecret = secret; globalProjectSettings.projects[project].credentialSecret = secret;
return settings.set("projects",globalProjectSettings); wasInvalid = globalProjectSettings.projects[project].credentialSecretInvalid;
delete globalProjectSettings.projects[project].credentialSecretInvalid;
return settings.set("projects",globalProjectSettings).then(function() {
if (isReset || !wasInvalid) {
if (isReset) {
runtime.nodes.clearCredentials();
}
runtime.nodes.setCredentialSecret(secret);
return runtime.nodes.exportCredentials()
.then(runtime.storage.saveCredentials)
.then(function() {
return wasInvalid;
});
}
return wasInvalid;
});
} }
function createProject(metadata) { function createProject(metadata) {
@ -218,7 +240,7 @@ function createProject(metadata) {
} }
createProjectDirectory(project).then(function() { createProjectDirectory(project).then(function() {
if (metadata.credentialSecret) { if (metadata.credentialSecret) {
return setCredentialSecret(project,metadata.credentialSecret); return setCredentialSecret(project,{credentialSecret: credentialSecret});
} }
return when.resolve(); return when.resolve();
}).then(function() { }).then(function() {
@ -422,8 +444,10 @@ function updateProject(project,data) {
return checkProjectExists(project).then(function() { return checkProjectExists(project).then(function() {
if (data.credentialSecret) { if (data.credentialSecret) {
// TODO: this path assumes we aren't trying to migrate the secret // TODO: this path assumes we aren't trying to migrate the secret
return setCredentialSecret(project,data.credentialSecret).then(function() { return setCredentialSecret(project,data).then(function(wasInvalid) {
if (wasInvalid) {
return reloadActiveProject(project); return reloadActiveProject(project);
}
}) })
} else if (data.hasOwnProperty('description')) { } else if (data.hasOwnProperty('description')) {
var projectPath = fspath.join(projectsDir,project); var projectPath = fspath.join(projectsDir,project);