mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Better permission handling in editor
This commit is contained in:
		| @@ -104,42 +104,48 @@ | ||||
|                             } | ||||
|                             if (notificationId === "runtime-state") { | ||||
|                                 if (msg.error === "credentials_load_failed") { | ||||
|                                     options.buttons = [ | ||||
|                                         { | ||||
|                                             text: "Setup credentials", | ||||
|                                             click: function() { | ||||
|                                                 RED.projects.showCredentialsPrompt(); | ||||
|                                     if (RED.user.hasPermission("projects.write")) { | ||||
|                                         options.buttons = [ | ||||
|                                             { | ||||
|                                                 text: "Setup credentials", | ||||
|                                                 click: function() { | ||||
|                                                     RED.projects.showCredentialsPrompt(); | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         } | ||||
|                                     ] | ||||
|                                         ] | ||||
|                                     } | ||||
|                                 } else if (msg.error === "missing_flow_file") { | ||||
|                                     options.buttons = [ | ||||
|                                         { | ||||
|                                             text: "Setup project files", | ||||
|                                             click: function() { | ||||
|                                                 persistentNotifications[notificationId].close(); | ||||
|                                                 delete persistentNotifications[notificationId]; | ||||
|                                                 RED.projects.showFilesPrompt(); | ||||
|                                     if (RED.user.hasPermission("projects.write")) { | ||||
|                                         options.buttons = [ | ||||
|                                             { | ||||
|                                                 text: "Setup project files", | ||||
|                                                 click: function() { | ||||
|                                                     persistentNotifications[notificationId].close(); | ||||
|                                                     delete persistentNotifications[notificationId]; | ||||
|                                                     RED.projects.showFilesPrompt(); | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         } | ||||
|                                     ] | ||||
|                                         ] | ||||
|                                     } | ||||
|                                 } else if (msg.error === "project_empty") { | ||||
|                                     options.buttons = [ | ||||
|                                         { | ||||
|                                             text: "No thanks", | ||||
|                                             click: function() { | ||||
|                                                 persistentNotifications[notificationId].close(); | ||||
|                                                 delete persistentNotifications[notificationId]; | ||||
|                                     if (RED.user.hasPermission("projects.write")) { | ||||
|                                         options.buttons = [ | ||||
|                                             { | ||||
|                                                 text: "No thanks", | ||||
|                                                 click: function() { | ||||
|                                                     persistentNotifications[notificationId].close(); | ||||
|                                                     delete persistentNotifications[notificationId]; | ||||
|                                                 } | ||||
|                                             },                                        { | ||||
|                                                 text: "Create default project files", | ||||
|                                                 click: function() { | ||||
|                                                     persistentNotifications[notificationId].close(); | ||||
|                                                     delete persistentNotifications[notificationId]; | ||||
|                                                     RED.projects.createDefaultFileSet(); | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         },                                        { | ||||
|                                             text: "Create default project files", | ||||
|                                             click: function() { | ||||
|                                                 persistentNotifications[notificationId].close(); | ||||
|                                                 delete persistentNotifications[notificationId]; | ||||
|                                                 RED.projects.createDefaultFileSet(); | ||||
|                                             } | ||||
|                                         } | ||||
|                                     ] | ||||
|                                         ] | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             if (!persistentNotifications.hasOwnProperty(notificationId)) { | ||||
|   | ||||
| @@ -63,7 +63,7 @@ RED.settings = (function () { | ||||
|         if (!hasLocalStorage()) { | ||||
|             return; | ||||
|         } | ||||
|         if (key === "auth_tokens") { | ||||
|         if (key === "auth-tokens") { | ||||
|             localStorage.removeItem(key); | ||||
|         } else { | ||||
|             delete userSettings[key]; | ||||
| @@ -161,23 +161,25 @@ RED.settings = (function () { | ||||
|     } | ||||
|  | ||||
|     function saveUserSettings() { | ||||
|         if (pendingSave) { | ||||
|             clearTimeout(pendingSave); | ||||
|         if (RED.user.hasPermission("settings.write")) { | ||||
|             if (pendingSave) { | ||||
|                 clearTimeout(pendingSave); | ||||
|             } | ||||
|             pendingSave = setTimeout(function() { | ||||
|                 pendingSave = null; | ||||
|                 $.ajax({ | ||||
|                     method: 'POST', | ||||
|                     contentType: 'application/json', | ||||
|                     url: 'settings/user', | ||||
|                     data: JSON.stringify(userSettings), | ||||
|                     success: function (data) { | ||||
|                     }, | ||||
|                     error: function(jqXHR,textStatus,errorThrown) { | ||||
|                         console.log("Unexpected error saving user settings:",jqXHR.status,textStatus); | ||||
|                     } | ||||
|                 }); | ||||
|             },300); | ||||
|         } | ||||
|         pendingSave = setTimeout(function() { | ||||
|             pendingSave = null; | ||||
|             $.ajax({ | ||||
|                 method: 'POST', | ||||
|                 contentType: 'application/json', | ||||
|                 url: 'settings/user', | ||||
|                 data: JSON.stringify(userSettings), | ||||
|                 success: function (data) { | ||||
|                 }, | ||||
|                 error: function(jqXHR,textStatus,errorThrown) { | ||||
|                     console.log("Unexpected error saving user settings:",jqXHR.status,textStatus); | ||||
|                 } | ||||
|             }); | ||||
|         },300); | ||||
|     } | ||||
|  | ||||
|     function theme(property,defaultValue) { | ||||
|   | ||||
| @@ -286,6 +286,10 @@ RED.deploy = (function() { | ||||
|  | ||||
|     function save(skipValidation,force) { | ||||
|         if (!$("#btn-deploy").hasClass("disabled")) { | ||||
|             if (!RED.user.hasPermission("flows.write")) { | ||||
|                 RED.notify(RED._("user.errors.deploy"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             if (!skipValidation) { | ||||
|                 var hasUnknown = false; | ||||
|                 var hasInvalid = false; | ||||
|   | ||||
| @@ -40,6 +40,11 @@ RED.projects.settings = (function() { | ||||
|         if (settingsVisible) { | ||||
|             return; | ||||
|         } | ||||
|         if (!RED.user.hasPermission("projects.write")) { | ||||
|             RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         settingsVisible = true; | ||||
|         var tabContainer; | ||||
|  | ||||
| @@ -226,12 +231,14 @@ RED.projects.settings = (function() { | ||||
|         var summary = $('<div style="position: relative">').appendTo(pane); | ||||
|         var summaryContent = $('<div></div>',{style:"color: #999"}).appendTo(summary); | ||||
|         updateProjectSummary(activeProject.summary, summaryContent); | ||||
|         $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') | ||||
|             .prependTo(summary) | ||||
|             .click(function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 editSummary(activeProject, activeProject.summary, summaryContent); | ||||
|             }); | ||||
|         if (RED.user.hasPermission("projects.write")) { | ||||
|             $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') | ||||
|                 .prependTo(summary) | ||||
|                 .click(function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     editSummary(activeProject, activeProject.summary, summaryContent); | ||||
|                 }); | ||||
|         } | ||||
|         $('<hr>').appendTo(pane); | ||||
|  | ||||
|         var description = $('<div class="node-help" style="position: relative"></div>').appendTo(pane); | ||||
| @@ -239,13 +246,14 @@ RED.projects.settings = (function() { | ||||
|  | ||||
|         updateProjectDescription(activeProject, descriptionContent); | ||||
|  | ||||
|         $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') | ||||
|             .prependTo(description) | ||||
|             .click(function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 editDescription(activeProject, descriptionContent); | ||||
|             }); | ||||
|  | ||||
|         if (RED.user.hasPermission("projects.write")) { | ||||
|             $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') | ||||
|                 .prependTo(description) | ||||
|                 .click(function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     editDescription(activeProject, descriptionContent); | ||||
|                 }); | ||||
|         } | ||||
|         return pane; | ||||
|     } | ||||
|     function updateProjectDependencies(activeProject,depsList) { | ||||
| @@ -349,12 +357,14 @@ RED.projects.settings = (function() { | ||||
|  | ||||
|     function createDependenciesPane(activeProject) { | ||||
|         var pane = $('<div id="project-settings-tab-deps" class="project-settings-tab-pane node-help"></div>'); | ||||
|         $('<button class="editor-button editor-button-small" style="margin-top:10px;float: right;">edit</button>') | ||||
|             .appendTo(pane) | ||||
|             .click(function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 editDependencies(activeProject,null,pane,depsList) | ||||
|             }); | ||||
|         if (RED.user.hasPermission("projects.write")) { | ||||
|             $('<button class="editor-button editor-button-small" style="margin-top:10px;float: right;">edit</button>') | ||||
|                 .appendTo(pane) | ||||
|                 .click(function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     editDependencies(activeProject,null,pane,depsList) | ||||
|                 }); | ||||
|         } | ||||
|         var depsList = $("<ol>",{style:"position: absolute;top: 60px;bottom: 20px;left: 20px;right: 20px;"}).appendTo(pane); | ||||
|         depsList.editableList({ | ||||
|             addButton: false, | ||||
| @@ -368,28 +378,30 @@ RED.projects.settings = (function() { | ||||
|                         row.parent().addClass("palette-module-section"); | ||||
|                     } | ||||
|                     headerRow.text(entry.label); | ||||
|                     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) { | ||||
|                             evt.preventDefault(); | ||||
|                             var deps = $.extend(true, {}, activeProject.dependencies); | ||||
|                             for (var m in modulesInUse) { | ||||
|                                 if (modulesInUse.hasOwnProperty(m) && !modulesInUse[m].known) { | ||||
|                                     deps[m] = modulesInUse[m].version; | ||||
|                     if (RED.user.hasPermission("projects.write")) { | ||||
|                         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) { | ||||
|                                 evt.preventDefault(); | ||||
|                                 var deps = $.extend(true, {}, activeProject.dependencies); | ||||
|                                 for (var m in modulesInUse) { | ||||
|                                     if (modulesInUse.hasOwnProperty(m) && !modulesInUse[m].known) { | ||||
|                                         deps[m] = modulesInUse[m].version; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList); | ||||
|                         }); | ||||
|                     } else if (entry.index === 3) { | ||||
|                         var removeButton = $('<button class="editor-button editor-button-small palette-module-button">remove from project</button>').appendTo(headerRow).click(function(evt) { | ||||
|                             evt.preventDefault(); | ||||
|                             var deps = $.extend(true, {}, activeProject.dependencies); | ||||
|                             for (var m in activeProject.dependencies) { | ||||
|                                 if (activeProject.dependencies.hasOwnProperty(m) && !modulesInUse.hasOwnProperty(m)) { | ||||
|                                     delete deps[m]; | ||||
|                                 editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList); | ||||
|                             }); | ||||
|                         } else if (entry.index === 3) { | ||||
|                             var removeButton = $('<button class="editor-button editor-button-small palette-module-button">remove from project</button>').appendTo(headerRow).click(function(evt) { | ||||
|                                 evt.preventDefault(); | ||||
|                                 var deps = $.extend(true, {}, activeProject.dependencies); | ||||
|                                 for (var m in activeProject.dependencies) { | ||||
|                                     if (activeProject.dependencies.hasOwnProperty(m) && !modulesInUse.hasOwnProperty(m)) { | ||||
|                                         delete deps[m]; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList); | ||||
|                         }); | ||||
|                                 editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList); | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     headerRow.addClass("palette-module-header"); | ||||
| @@ -630,26 +642,27 @@ RED.projects.settings = (function() { | ||||
|     function createFilesSection(activeProject,pane) { | ||||
|         var title = $('<h3></h3>').text("Files").appendTo(pane); | ||||
|         var filesContainer = $('<div class="user-settings-section"></div>').appendTo(pane); | ||||
|         var editFilesButton = $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') | ||||
|             .appendTo(title) | ||||
|             .click(function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 formButtons.show(); | ||||
|                 editFilesButton.hide(); | ||||
|                 flowFileLabelText.hide(); | ||||
|                 flowFileInput.show(); | ||||
|                 flowFileInputSearch.show(); | ||||
|                 credFileLabel.hide(); | ||||
|                 credFileInput.show(); | ||||
|                 flowFileInput.focus(); | ||||
|                 // credentialStateLabel.parent().hide(); | ||||
|                 credentialStateLabel.addClass("uneditable-input"); | ||||
|                 $(".user-settings-row-credentials").show(); | ||||
|                 credentialStateLabel.css('height','auto'); | ||||
|                 credentialFormRows.hide(); | ||||
|                 credentialSecretButtons.show(); | ||||
|             }); | ||||
|  | ||||
|         if (RED.user.hasPermission("projects.write")) { | ||||
|             var editFilesButton = $('<button class="editor-button editor-button-small" style="float: right;">edit</button>') | ||||
|                 .appendTo(title) | ||||
|                 .click(function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     formButtons.show(); | ||||
|                     editFilesButton.hide(); | ||||
|                     flowFileLabelText.hide(); | ||||
|                     flowFileInput.show(); | ||||
|                     flowFileInputSearch.show(); | ||||
|                     credFileLabel.hide(); | ||||
|                     credFileInput.show(); | ||||
|                     flowFileInput.focus(); | ||||
|                     // credentialStateLabel.parent().hide(); | ||||
|                     credentialStateLabel.addClass("uneditable-input"); | ||||
|                     $(".user-settings-row-credentials").show(); | ||||
|                     credentialStateLabel.css('height','auto'); | ||||
|                     credentialFormRows.hide(); | ||||
|                     credentialSecretButtons.show(); | ||||
|                 }); | ||||
|         } | ||||
|         var row; | ||||
|  | ||||
|         // Flow files | ||||
|   | ||||
| @@ -1753,6 +1753,10 @@ RED.projects = (function() { | ||||
|         } else if (!activeProject.empty) { | ||||
|             throw new Error("Cannot create default file set on a non-empty project"); | ||||
|         } | ||||
|         if (!RED.user.hasPermission("projects.write")) { | ||||
|             RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|             return; | ||||
|         } | ||||
|         createProjectOptions = {}; | ||||
|         show('default-files',{existingProject: true}); | ||||
|         // var payload = { | ||||
| @@ -1805,11 +1809,18 @@ RED.projects = (function() { | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         _show: show, | ||||
|         showStartup: function() { | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             show('welcome'); | ||||
|         }, | ||||
|         newProject: function() { | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             if (!activeProject) { | ||||
|                 show('welcome'); | ||||
|             } else { | ||||
| @@ -1817,15 +1828,31 @@ RED.projects = (function() { | ||||
|             } | ||||
|         }, | ||||
|         selectProject: function() { | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             show('open') | ||||
|         }, | ||||
|         deleteProject: function() { | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             show('delete') | ||||
|         }, | ||||
|         showCredentialsPrompt: function() { //TODO: rename this function | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             RED.projects.settings.show('settings'); | ||||
|         }, | ||||
|         showFilesPrompt: function() { //TODO: rename this function | ||||
|             if (!RED.user.hasPermission("projects.write")) { | ||||
|                 RED.notify(RED._("user.errors.notAuthorized"),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             RED.projects.settings.show('settings'); | ||||
|         }, | ||||
|         createDefaultFileSet: createDefaultFileSet, | ||||
|   | ||||
| @@ -283,7 +283,10 @@ RED.sidebar.versionControl = (function() { | ||||
|                     refreshFiles(result); | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         }); | ||||
|         RED.events.on("login",function() { | ||||
|             refresh(true); | ||||
|         }); | ||||
|         sidebarContent = $('<div>', {class:"sidebar-version-control"}); | ||||
|         var stackContainer = $("<div>",{class:"sidebar-version-control-stack"}).appendTo(sidebarContent); | ||||
|         sections = RED.stack.create({ | ||||
| @@ -1175,6 +1178,10 @@ RED.sidebar.versionControl = (function() { | ||||
|             stagedChangesList.editableList('empty'); | ||||
|             unmergedChangesList.editableList('empty'); | ||||
|         } | ||||
|         if (!RED.user.hasPermission("projects.write")) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         refreshInProgress = true; | ||||
|         refreshLocalCommits(); | ||||
|   | ||||
| @@ -29,6 +29,10 @@ RED.userSettings = (function() { | ||||
|         if (settingsVisible) { | ||||
|             return; | ||||
|         } | ||||
|         if (!RED.user.hasPermission("settings.write")) { | ||||
|             RED.notify(RED._("user.errors.settings"),"error"); | ||||
|             return; | ||||
|         } | ||||
|         settingsVisible = true; | ||||
|         var tabContainer; | ||||
|  | ||||
|   | ||||
| @@ -188,6 +188,7 @@ RED.user = (function() { | ||||
|                         RED.settings.load(function() { | ||||
|                             RED.notify(RED._("user.loggedInAs",{name:RED.settings.user.username}),"success"); | ||||
|                             updateUserMenu(); | ||||
|                             RED.events.emit("login",RED.settings.user.username); | ||||
|                         }); | ||||
|                     }); | ||||
|                 } | ||||
| @@ -230,10 +231,66 @@ RED.user = (function() { | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     var readRE = /^((.+)\.)?read$/ | ||||
|     var writeRE = /^((.+)\.)?write$/ | ||||
|  | ||||
|     function hasPermission(permission) { | ||||
|         if (permission === "") { | ||||
|             return true; | ||||
|         } | ||||
|         if (!RED.settings.user) { | ||||
|             return true; | ||||
|         } | ||||
|         return checkPermission(RED.settings.user.permissions||"",permission); | ||||
|     } | ||||
|     function checkPermission(userScope,permission) { | ||||
|         if (permission === "") { | ||||
|             return true; | ||||
|         } | ||||
|         var i; | ||||
|  | ||||
|         if (Array.isArray(permission)) { | ||||
|             // Multiple permissions requested - check each one | ||||
|             for (i=0;i<permission.length;i++) { | ||||
|                 if (!checkPermission(userScope,permission[i])) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|             // All permissions check out | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (Array.isArray(userScope)) { | ||||
|             if (userScope.length === 0) { | ||||
|                 return false; | ||||
|             } | ||||
|             for (i=0;i<userScope.length;i++) { | ||||
|                 if (checkPermission(userScope[i],permission)) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (userScope === "*" || userScope === permission) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (userScope === "read" || userScope === "*.read") { | ||||
|             return readRE.test(permission); | ||||
|         } else if (userScope === "write" || userScope === "*.write") { | ||||
|             return writeRE.test(permission); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         login: login, | ||||
|         logout: logout | ||||
|         logout: logout, | ||||
|         hasPermission: hasPermission | ||||
|     } | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -23,6 +23,10 @@ | ||||
| #project-settings-tab-settings { | ||||
|     overflow-y: scroll; | ||||
| } | ||||
| .sidebar-version-control-shade { | ||||
|     background: #f3f3f3; | ||||
| } | ||||
|  | ||||
| .projects-edit-form form { | ||||
|     margin: 0; | ||||
|     .form-row { | ||||
|   | ||||
| @@ -76,7 +76,12 @@ | ||||
|         "password": "Password", | ||||
|         "login": "Login", | ||||
|         "loginFailed": "Login failed", | ||||
|         "notAuthorized": "Not authorized" | ||||
|         "notAuthorized": "Not authorized", | ||||
|         "errors": { | ||||
|             "settings": "You must be logged in to access settings", | ||||
|             "deploy": "You must be logged in to deploy changes", | ||||
|             "notAuthorized": "You must be logged in to perform this action" | ||||
|         } | ||||
|     }, | ||||
|     "notification": { | ||||
|         "warning": "<strong>Warning</strong>: __message__", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user