mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Allow credSecret to be managed via project settings
This commit is contained in:
@@ -49,7 +49,6 @@ module.exports = {
|
||||
app.post("/", function(req,res) {
|
||||
// Create project
|
||||
runtime.storage.projects.createProject(req.body).then(function(name) {
|
||||
console.log("Created project",name);
|
||||
runtime.storage.projects.getProject(name).then(function(data) {
|
||||
res.json(data);
|
||||
});
|
||||
|
@@ -38,6 +38,28 @@ function decryptCredentials(key,credentials) {
|
||||
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 = {
|
||||
init: function(runtime) {
|
||||
log = runtime.log;
|
||||
@@ -75,16 +97,17 @@ var api = module.exports = {
|
||||
}
|
||||
|
||||
var projectKey = false;
|
||||
var activeProject;
|
||||
try {
|
||||
var projects = settings.get('projects');
|
||||
if (projects && projects.activeProject) {
|
||||
activeProject = projects.activeProject;
|
||||
projectKey = projects.projects[projects.activeProject].credentialSecret;
|
||||
}
|
||||
} catch(err) {
|
||||
}
|
||||
if (projectKey) {
|
||||
log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key");
|
||||
console.log(projectKey);
|
||||
userKey = projectKey;
|
||||
}
|
||||
|
||||
@@ -171,21 +194,32 @@ var api = module.exports = {
|
||||
encryptedCredentials = credentials;
|
||||
}
|
||||
return setupEncryptionPromise.then(function() {
|
||||
var clearInvalidFlag = false;
|
||||
if (credentials.hasOwnProperty("$")) {
|
||||
// These are encrypted credentials
|
||||
try {
|
||||
credentialCache = decryptCredentials(encryptionKey,credentials)
|
||||
clearInvalidFlag = true;
|
||||
} catch(err) {
|
||||
credentialCache = {};
|
||||
dirty = true;
|
||||
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||
var error = new Error("Failed to decrypt credentials");
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
credentialCache = credentials;
|
||||
}
|
||||
if (clearInvalidFlag) {
|
||||
return markProjectSecretValid(activeProject,true)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -222,6 +256,10 @@ var api = module.exports = {
|
||||
dirty = true;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
credentialCache = {};
|
||||
dirty = true;
|
||||
},
|
||||
/**
|
||||
* Deletes any credentials for nodes that no longer exist
|
||||
* @param config a flow config
|
||||
@@ -321,9 +359,14 @@ var api = module.exports = {
|
||||
dirty: function() {
|
||||
return dirty;
|
||||
},
|
||||
|
||||
setKey: function(key) {
|
||||
encryptionKey = crypto.createHash('sha256').update(key).digest();
|
||||
encryptionEnabled = true;
|
||||
dirty = true;
|
||||
},
|
||||
export: function() {
|
||||
var result = credentialCache;
|
||||
|
||||
if (encryptionEnabled) {
|
||||
if (dirty) {
|
||||
try {
|
||||
|
@@ -231,7 +231,6 @@ function handleStatus(node,statusMessage) {
|
||||
|
||||
|
||||
function start(type,diff,muteLog) {
|
||||
console.log("START----")
|
||||
//dumpActiveNodes();
|
||||
type = type||"full";
|
||||
started = true;
|
||||
|
@@ -172,5 +172,8 @@ module.exports = {
|
||||
addCredentials: credentials.add,
|
||||
getCredentials: credentials.get,
|
||||
deleteCredentials: credentials.delete,
|
||||
getCredentialDefinition: credentials.getDefinition
|
||||
getCredentialDefinition: credentials.getDefinition,
|
||||
setCredentialSecret: credentials.setKey,
|
||||
clearCredentials: credentials.clear,
|
||||
exportCredentials: credentials.export
|
||||
};
|
||||
|
@@ -84,16 +84,16 @@ var storageModuleInterface = {
|
||||
|
||||
return credentialSavePromise.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() {
|
||||
// return storageModule.getCredentials();
|
||||
// },
|
||||
// saveCredentials: function(credentials) {
|
||||
// return storageModule.saveCredentials(credentials);
|
||||
// },
|
||||
saveCredentials: function(credentials) {
|
||||
return storageModule.saveCredentials(credentials);
|
||||
},
|
||||
getSettings: function() {
|
||||
if (settingsAvailable) {
|
||||
return storageModule.getSettings();
|
||||
|
@@ -131,10 +131,20 @@ function getProject(project) {
|
||||
projectSettings = globalProjectSettings.projects[project]||{};
|
||||
}
|
||||
|
||||
// console.log(projectSettings);
|
||||
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 = [];
|
||||
checkProjectFiles(project).then(function(missingFiles) {
|
||||
if (missingFiles.length > 0) {
|
||||
@@ -158,17 +168,6 @@ function getProject(project) {
|
||||
when.settle(promises).then(function() {
|
||||
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) {
|
||||
@@ -185,22 +184,45 @@ function getProject(project) {
|
||||
});
|
||||
}
|
||||
|
||||
var encryptionAlgorithm = "aes-256-ctr";
|
||||
function decryptCredentials(key,credentials) {
|
||||
var creds = credentials["$"];
|
||||
var initVector = new Buffer(creds.substring(0, 32),'hex');
|
||||
creds = creds.substring(32);
|
||||
var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
|
||||
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
|
||||
return JSON.parse(decrypted);
|
||||
}
|
||||
function setCredentialSecret(project,data) { //existingSecret,secret) {
|
||||
var existingSecret = data.currentCredentialSecret;
|
||||
var isReset = data.resetCredentialSecret;
|
||||
var secret = data.credentialSecret;
|
||||
var wasInvalid = false;
|
||||
|
||||
function setCredentialSecret(project,secret) {
|
||||
var globalProjectSettings = settings.get("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;
|
||||
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) {
|
||||
@@ -218,7 +240,7 @@ function createProject(metadata) {
|
||||
}
|
||||
createProjectDirectory(project).then(function() {
|
||||
if (metadata.credentialSecret) {
|
||||
return setCredentialSecret(project,metadata.credentialSecret);
|
||||
return setCredentialSecret(project,{credentialSecret: credentialSecret});
|
||||
}
|
||||
return when.resolve();
|
||||
}).then(function() {
|
||||
@@ -422,8 +444,10 @@ function updateProject(project,data) {
|
||||
return checkProjectExists(project).then(function() {
|
||||
if (data.credentialSecret) {
|
||||
// TODO: this path assumes we aren't trying to migrate the secret
|
||||
return setCredentialSecret(project,data.credentialSecret).then(function() {
|
||||
return reloadActiveProject(project);
|
||||
return setCredentialSecret(project,data).then(function(wasInvalid) {
|
||||
if (wasInvalid) {
|
||||
return reloadActiveProject(project);
|
||||
}
|
||||
})
|
||||
} else if (data.hasOwnProperty('description')) {
|
||||
var projectPath = fspath.join(projectsDir,project);
|
||||
|
Reference in New Issue
Block a user