From bb11c05914869028a39a685cf5bf2ec347f299a1 Mon Sep 17 00:00:00 2001 From: Laurence Maultsby Date: Thu, 6 Apr 2017 10:42:55 -0400 Subject: [PATCH] RBAC React Component, displays/hides components based on role Signed-off-by: Laurence Maultsby --- .../core/modules/api/projectManagementAPI.js | 16 ++- .../core/modules/api/userManagementAPI.js | 85 +++++++++++++- .../core/modules/navigation_manager.js | 8 ++ .../core/modules/routes/projectManagement.js | 1 - .../core/modules/routes/userManagement.js | 7 ++ skyquake/framework/utils/roleConstants.js | 16 +++ .../widgets/skyquake_nav/skyquakeNav.jsx | 5 +- .../widgets/skyquake_rbac/skyquakeRBAC.jsx | 108 ++++++++++++++++++ .../src/dashboard/dashboard.jsx | 24 ++-- .../src/dashboard/projectMgmt.scss | 5 +- .../src/dashboard/projectMgmtStore.js | 4 +- skyquake/plugins/user_management/config.json | 3 +- .../src/dashboard/dashboard.jsx | 19 ++- .../src/dashboard/userMgmt.scss | 5 +- 14 files changed, 275 insertions(+), 31 deletions(-) create mode 100644 skyquake/framework/utils/roleConstants.js create mode 100644 skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx diff --git a/skyquake/framework/core/modules/api/projectManagementAPI.js b/skyquake/framework/core/modules/api/projectManagementAPI.js index 3238aec91..07e873d23 100644 --- a/skyquake/framework/core/modules/api/projectManagementAPI.js +++ b/skyquake/framework/core/modules/api/projectManagementAPI.js @@ -178,14 +178,18 @@ ProjectManagement.delete = function(req) { } -ProjectManagement.getPlatform = function(req) { +ProjectManagement.getPlatform = function(req, userId) { var self = this; var api_server = req.query['api_server']; - + var user = req.params['userId'] || userId; return new Promise(function(resolve, reject) { + var url = utils.confdPort(api_server) + '/api/operational/rbac-platform-config'; + if(user) { + url = url + '/user/' + user; + } Promise.all([ rp({ - uri: utils.confdPort(api_server) + '/api/operational/rbac-platform-config', + uri: url, method: 'GET', headers: _.extend({}, constants.HTTP_HEADERS.accept.data, { 'Authorization': req.session && req.session.authorization @@ -198,7 +202,11 @@ ProjectManagement.getPlatform = function(req) { var response = {}; response['data'] = {}; if (result[0].body) { - response['data']['platform'] = JSON.parse(result[0].body)['rw-rbac-platform:rbac-platform-config']; + if(user) { + response['data']['platform'] = JSON.parse(result[0].body)['rw-rbac-platform:user']; + } else { + response['data']['platform'] = JSON.parse(result[0].body)['rw-rbac-platform:rbac-platform-config']; + } } response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK diff --git a/skyquake/framework/core/modules/api/userManagementAPI.js b/skyquake/framework/core/modules/api/userManagementAPI.js index 0608c5db6..873ab1933 100644 --- a/skyquake/framework/core/modules/api/userManagementAPI.js +++ b/skyquake/framework/core/modules/api/userManagementAPI.js @@ -25,6 +25,7 @@ var Promise = require('promise'); var constants = require('../../api_utils/constants'); var utils = require('../../api_utils/utils'); var _ = require('lodash'); +var ProjectManagementAPI = require('./projectManagementAPI.js'); UserManagement.get = function(req) { var self = this; @@ -63,20 +64,96 @@ UserManagement.get = function(req) { }); }; + UserManagement.getProfile = function(req) { var self = this; var api_server = req.query['api_server']; return new Promise(function(resolve, reject) { var response = {}; - response['data'] = { - userId: req.session.userdata.username, + var userId = req.session.userdata.username + response['data'] = { + userId: userId, projectId: req.session.projectId }; - response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK + UserManagement.getUserInfo(req, userId).then(function(result) { + response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK; + response.data.data =result.data + resolve(response); + }, function(error) { + console.log('Error retrieving getUserInfo'); + response.statusCode = constants.HTTP_RESPONSE_CODES.ERROR.INTERNAL_SERVER_ERROR; + reject(response); + }) - resolve(response); }); }; +UserManagement.getUserInfo = function(req, userId, domain) { + var self = this; + var api_server = req.query['api_server']; + var id = req.params['userId'] || userId; + var domain = req.params['domainId'] || domain; + var response = {}; + return new Promise(function(resolve, reject) { + if (id) { + var getProjects = ProjectManagementAPI.get(req) + var getPlatformUser = ProjectManagementAPI.getPlatform(req, id) + Promise.all([ + getProjects, + getPlatformUser + ]).then(function(result) { + var userData = { + platform: { + role: { + + } + }, + project: { + /** + * [projectId] : { + * data: [project object], + * role: { + * [roleId]: true + * } + * } + */ + } + } + //Build project roles + var projects = result[0].data.project; + var userProjects = []; + projects && projects.map(function(p, i) { + var users = p['project-config'] && p['project-config'].user; + users && users.map(function(u) { + if(u['user-name'] == id) { + userData.project[p.name] = { + data: p, + role: {} + } + u.role.map(function(r) { + userData.project[p.name].role[r.role] = true + }); + } + }) + }); + //Build platform roles + var platformRoles = result[1].data.platform && result[1].data.platform.role; + platformRoles && platformRoles.map(function(r) { + userData.platform.role[r.role] = true + }); + response.data = userData; + response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK + resolve(response); + }) + } else { + var errorMsg = 'userId not specified in UserManagement.getUserInfo'; + console.error(errorMsg); + response.statusCode = constants.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST; + response.error = errorMsg; + reject(response) + } + + }) +} UserManagement.create = function(req) { var self = this; var api_server = req.query['api_server']; diff --git a/skyquake/framework/core/modules/navigation_manager.js b/skyquake/framework/core/modules/navigation_manager.js index 6ed1e49a3..f6903591c 100644 --- a/skyquake/framework/core/modules/navigation_manager.js +++ b/skyquake/framework/core/modules/navigation_manager.js @@ -68,6 +68,13 @@ function addLabel(plugin_name, label) { NAVIGATION[plugin_name].label = label || 'RW.UI Plugin'; } +function addAllow(plugin_name, allow) { + if (!NAVIGATION[plugin_name]) { + NAVIGATION[plugin_name] = {}; + } + NAVIGATION[plugin_name].allow = allow || '*'; +} + function getNavigation() { return NAVIGATION; } @@ -81,6 +88,7 @@ function onNavigationDiscovered(plugin_name, plugin) { addOrder(plugin_name, plugin.order); addPriority(plugin_name, plugin.priority); addLabel(plugin_name, plugin.name); + addAllow(plugin_name, plugin.allow); } function init() { diff --git a/skyquake/framework/core/modules/routes/projectManagement.js b/skyquake/framework/core/modules/routes/projectManagement.js index ef52ba305..c106f30e3 100644 --- a/skyquake/framework/core/modules/routes/projectManagement.js +++ b/skyquake/framework/core/modules/routes/projectManagement.js @@ -1,4 +1,3 @@ - /* * * Copyright 2016 RIFT.IO Inc diff --git a/skyquake/framework/core/modules/routes/userManagement.js b/skyquake/framework/core/modules/routes/userManagement.js index 4e272d058..22a2d74e0 100644 --- a/skyquake/framework/core/modules/routes/userManagement.js +++ b/skyquake/framework/core/modules/routes/userManagement.js @@ -50,6 +50,13 @@ Router.get('/user-profile', cors(), function(req, res) { utils.sendErrorResponse(error, res); }); }); +Router.get('/user-data/:userId/:domain?', cors(), function(req, res) { + UserManagementAPI.getUserInfo(req).then(function(response) { + utils.sendSuccessResponse(response, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); Router.post('/user', cors(), function(req, res) { UserManagementAPI.create(req).then(function(response) { utils.sendSuccessResponse(response, res); diff --git a/skyquake/framework/utils/roleConstants.js b/skyquake/framework/utils/roleConstants.js new file mode 100644 index 000000000..95e36fcb1 --- /dev/null +++ b/skyquake/framework/utils/roleConstants.js @@ -0,0 +1,16 @@ +var c = {}; + +c.PLATFORM = { + OPER: "rw-rbac-platform:platform-oper", + ADMIN: "rw-rbac-platform:platform-admin", + SUPER: "rw-rbac-platform:super-admin" +} + +c.PROJECT = { + MANO_OPER: "rw-project-mano:mano-oper", + MANO_ADMIN: "rw-project-mano:mano-admin", + PROJECT_ADMIN: "rw-project:project-admin", + PROJECT_OPER: "rw-project:project-oper", +} + +module.exports = c; diff --git a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx index 51a21a021..a752bf665 100644 --- a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx +++ b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx @@ -25,6 +25,7 @@ import 'style/common.scss'; import './skyquakeNav.scss'; import SelectOption from '../form_controls/selectOption.jsx'; import {FormSection} from '../form_controls/formControls.jsx'; +import SkyquakeRBAC from 'widgets/skyquake_rbac/skyquakeRBAC.jsx'; //Temporary, until api server is on same port as webserver var rw = require('utils/rw.js'); @@ -298,14 +299,14 @@ export function buildNav(nav, currentPlugin, props) { navItem.priority = nav[k].priority; navItem.order = nav[k].order; navItem.html = ( -
+

{dashboardLink} {self.hasSubNav[k] ? : ''}

    { NavList }
-
+ ); navList.push(navItem) } diff --git a/skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx b/skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx new file mode 100644 index 000000000..e00e67254 --- /dev/null +++ b/skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx @@ -0,0 +1,108 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +import React from 'react'; +import ROLES from 'utils/roleConstants.js'; +const PLATFORM = ROLES.PLATFORM; + +export default class SkyquakeRBAC extends React.Component { + constructor(props, context) { + super(props); + } + render() { + const User = this.context.userProfile.data; + let HTML = null; + // If user object has platform property then it has been populated by the back end. + if(User) { + const PlatformRole = User.platform.role; + const isPlatformSuper = PlatformRole[PLATFORM.SUPER]; + const isPlatformAdmin = PlatformRole[PLATFORM.ADMIN]; + const isPlatformOper = PlatformRole[PLATFORM.OPER]; + const hasRoleAccess = checkForRoleAccess(User.project[this.props.project], PlatformRole, this.props.allow)//false//(this.props.roles.indexOf(userProfile.projectRole) > -1) + if (isPlatformSuper) { + HTML = this.props.children; + } else { + if (hasRoleAccess) { + HTML = this.props.children; + } + } + } + return (
{HTML}
) + } +} +SkyquakeRBAC.defaultProps = { + allow: [] +} +SkyquakeRBAC.contextTypes = { + userProfile: React.PropTypes.object +} + +function checkForRoleAccess(project, PlatformRole, allow) { + if (allow.indexOf('*') > -1) return true; + for (let i = 0; i; +// const isPlatformSuper = PlatformRole[PLATFORM.SUPER]; +// const isPlatformAdmin = PlatformRole[PLATFORM.ADMIN]; +// const isPlatformOper = PlatformRole[PLATFORM.OPER]; +// const hasRoleAccess = false//(this.props.roles.indexOf(userProfile.projectRole) > -1) +// if (isPlatformSuper || isPlatformOper || isPlatformAdmin) { +// return HTML +// } else { +// if (hasRoleAccess) { +// return HTML +// } else { +// return null; +// } +// } +// } +// else { +// return null; + +// } +// } +// } +// SkyquakeRBAC.defaultProps = { + +// } +// SkyquakeRBAC.contextTypes = { +// userProfile: React.PropTypes.object, +// allowedRoles: [] +// }; +// return SkyquakeRBAC; +// } diff --git a/skyquake/plugins/project_management/src/dashboard/dashboard.jsx b/skyquake/plugins/project_management/src/dashboard/dashboard.jsx index 8cda80269..f1fca657c 100644 --- a/skyquake/plugins/project_management/src/dashboard/dashboard.jsx +++ b/skyquake/plugins/project_management/src/dashboard/dashboard.jsx @@ -7,6 +7,7 @@ import ReactDOM from 'react-dom'; import AppHeader from 'widgets/header/header.jsx'; import ProjectManagementStore from './projectMgmtStore.js'; import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx'; +import SkyquakeRBAC from 'widgets/skyquake_rbac/skyquakeRBAC.jsx'; import 'style/layout.scss'; import './projectMgmt.scss'; import {Panel, PanelWrapper} from 'widgets/panel/panel'; @@ -20,6 +21,10 @@ import 'widgets/form_controls/formControls.scss'; import imgAdd from '../../node_modules/open-iconic/svg/plus.svg' import imgRemove from '../../node_modules/open-iconic/svg/trash.svg' +import ROLES from 'utils/roleConstants.js'; +const PROJECT_ROLES = ROLES.PROJECT; +const PLATFORM = ROLES.PLATFORM; + class ProjectManagementDashboard extends React.Component { constructor(props) { super(props); @@ -103,7 +108,6 @@ class ProjectManagementDashboard extends React.Component { 'name': projectName, 'description': self.state.description, 'project-config' : { - 'name-ref': projectName, 'user': cleanUsers } }); @@ -121,7 +125,6 @@ class ProjectManagementDashboard extends React.Component { 'name': projectName, 'description': self.state.description, 'project-config' : { - 'name-ref': projectName, 'user': cleanUsers } })); @@ -214,9 +217,9 @@ class ProjectManagementDashboard extends React.Component { let state = this.state; let passwordSectionHTML = null; let formButtonsHTML = ( - -