From ba19ffdfd442c7ebe66f482b90f6bc65a0e9b8be Mon Sep 17 00:00:00 2001 From: Laurence Maultsby Date: Tue, 7 Mar 2017 13:59:18 -0500 Subject: [PATCH] User Management: Create and delete. Initial pass. Signed-off-by: Laurence Maultsby --- skyquake/.storybook/config.js | 5 +- .../core/modules/api/userManagementAPI.js | 135 +++++++ .../core/modules/navigation_manager.js | 3 +- .../core/modules/routes/userManagement.js | 64 ++++ skyquake/framework/style/_colors.scss | 16 +- skyquake/framework/widgets/button/button.scss | 20 +- .../framework/widgets/button/sq-button.jsx | 24 +- .../widgets/form_controls/formControls.scss | 23 +- .../framework/widgets/form_controls/input.jsx | 96 +++++ .../widgets/form_controls/selectOption.jsx | 33 +- .../widgets/form_controls/textInput.jsx | 60 +--- skyquake/framework/widgets/panel/panel.jsx | 8 + skyquake/framework/widgets/panel/panel.scss | 13 + .../skyquake_container/skyquakeContainer.jsx | 2 +- .../EditDescriptorModelProperties.js | 2 +- skyquake/plugins/launchpad/package.json | 4 +- .../plugins/user-management/CMakeLists.txt | 38 ++ skyquake/plugins/user-management/api/ro.js | 94 +++++ skyquake/plugins/user-management/config.json | 14 + skyquake/plugins/user-management/package.json | 54 +++ skyquake/plugins/user-management/routes.js | 27 ++ .../plugins/user-management/scripts/build.sh | 18 + .../user-management/scripts/install.sh | 34 ++ skyquake/plugins/user-management/server.js | 51 +++ .../src/dashboard/dashboard.jsx | 337 ++++++++++++++++++ .../src/dashboard/userMgmt.scss | 193 ++++++++++ .../src/dashboard/userMgmtActions.js | 24 ++ .../src/dashboard/userMgmtSource.js | 152 ++++++++ .../src/dashboard/userMgmtStore.js | 197 ++++++++++ skyquake/plugins/user-management/src/main.js | 15 + .../webpack.production.config.js | 61 ++++ skyquake/skyquake.js | 4 + 32 files changed, 1737 insertions(+), 84 deletions(-) create mode 100644 skyquake/framework/core/modules/api/userManagementAPI.js create mode 100644 skyquake/framework/core/modules/routes/userManagement.js create mode 100644 skyquake/framework/widgets/form_controls/input.jsx create mode 100644 skyquake/plugins/user-management/CMakeLists.txt create mode 100644 skyquake/plugins/user-management/api/ro.js create mode 100644 skyquake/plugins/user-management/config.json create mode 100644 skyquake/plugins/user-management/package.json create mode 100644 skyquake/plugins/user-management/routes.js create mode 100755 skyquake/plugins/user-management/scripts/build.sh create mode 100755 skyquake/plugins/user-management/scripts/install.sh create mode 100644 skyquake/plugins/user-management/server.js create mode 100644 skyquake/plugins/user-management/src/dashboard/dashboard.jsx create mode 100644 skyquake/plugins/user-management/src/dashboard/userMgmt.scss create mode 100644 skyquake/plugins/user-management/src/dashboard/userMgmtActions.js create mode 100644 skyquake/plugins/user-management/src/dashboard/userMgmtSource.js create mode 100644 skyquake/plugins/user-management/src/dashboard/userMgmtStore.js create mode 100644 skyquake/plugins/user-management/src/main.js create mode 100644 skyquake/plugins/user-management/webpack.production.config.js diff --git a/skyquake/.storybook/config.js b/skyquake/.storybook/config.js index 42edf3cd4..04aee5093 100644 --- a/skyquake/.storybook/config.js +++ b/skyquake/.storybook/config.js @@ -6,8 +6,11 @@ function loadStories() { // require('../tests/stories/sshKeyCard'); // require('../tests/stories/button'); // require('../tests/stories/sq-input-slider'); - require('../tests/stories/catalogCard'); + // require('../tests/stories/catalogCard'); + require('../tests/stories/inputs'); // require as many stories as you need. } configure(loadStories, module); + + diff --git a/skyquake/framework/core/modules/api/userManagementAPI.js b/skyquake/framework/core/modules/api/userManagementAPI.js new file mode 100644 index 000000000..cf33313ef --- /dev/null +++ b/skyquake/framework/core/modules/api/userManagementAPI.js @@ -0,0 +1,135 @@ +/* + * + * 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. + * + */ +// DescriptorModelMeta API (NSD + VNFD) + + +var UserManagement = {}; +var Promise = require('bluebird'); +var rp = require('request-promise'); +var Promise = require('promise'); +var constants = require('../../api_utils/constants'); +var utils = require('../../api_utils/utils'); +var _ = require('lodash'); + +UserManagement.get = function(req) { + var self = this; + var api_server = req.query['api_server']; + + return new Promise(function(resolve, reject) { + Promise.all([ + rp({ + uri: utils.confdPort(api_server) + '/api/operational/user-config/users', + method: 'GET', + headers: _.extend({}, constants.HTTP_HEADERS.accept.data, { + 'Authorization': req.get('Authorization') + }), + forever: constants.FOREVER_ON, + rejectUnauthorized: false, + resolveWithFullResponse: true + }) + ]).then(function(result) { + var response = {}; + response['data'] = {}; + if (result[0].body) { + response['data']['users'] = JSON.parse(result[0].body)['rw-user:users']; + } + response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK + + resolve(response); + }).catch(function(error) { + var response = {}; + console.log('Problem with UserManagement.get', error); + response.statusCode = error.statusCode || 500; + response.errorMessage = { + error: 'Failed to get UserManagement' + error + }; + reject(response); + }); + }); +}; +UserManagement.create = function(req) { + var self = this; + var api_server = req.query['api_server']; + var data = req.body; + data = { + "users":[data] + } + return new Promise(function(resolve, reject) { + Promise.all([ + rp({ + uri: utils.confdPort(api_server) + '/api/config/user-config', + method: 'POST', + headers: _.extend({}, constants.HTTP_HEADERS.accept.data, { + 'Authorization': req.get('Authorization') + }), + forever: constants.FOREVER_ON, + json: data, + rejectUnauthorized: false, + resolveWithFullResponse: true + }) + ]).then(function(result) { + var response = {}; + response['data'] = {}; + if (result[0].body) { + response['data'] = result[0].body; + } + response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK + + resolve(response); + }).catch(function(error) { + var response = {}; + console.log('Problem with UserManagement.create', error); + response.statusCode = error.statusCode || 500; + response.errorMessage = { + error: 'Failed to create user' + error + }; + reject(response); + }); + }); +}; + +UserManagement.delete = function(req) { + var self = this; + var username = req.params.username; + var domain = req.params.domain; + var api_server = req.query["api_server"]; + var requestHeaders = {}; + var url = `${utils.confdPort(api_server)}/api/config/user-config/users/${username},${domain}` + return new Promise(function(resolve, reject) { + _.extend(requestHeaders, + constants.HTTP_HEADERS.accept.data, + constants.HTTP_HEADERS.content_type.data, { + 'Authorization': req.get('Authorization') + }); + rp({ + url: url, + method: 'DELETE', + headers: requestHeaders, + forever: constants.FOREVER_ON, + rejectUnauthorized: false, + }, function(error, response, body) { + if (utils.validateResponse('UserManagement.DELETE', error, response, body, resolve, reject)) { + return resolve({ + statusCode: response.statusCode, + data: JSON.stringify(response.body) + }); + }; + }); + }) +} +module.exports = UserManagement; diff --git a/skyquake/framework/core/modules/navigation_manager.js b/skyquake/framework/core/modules/navigation_manager.js index c85eba618..6ed1e49a3 100644 --- a/skyquake/framework/core/modules/navigation_manager.js +++ b/skyquake/framework/core/modules/navigation_manager.js @@ -1,5 +1,5 @@ /* - * + * * Copyright 2016 RIFT.IO Inc * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,7 +42,6 @@ function addNavigation(plugin_name, routes) { if (!NAVIGATION[plugin_name]) { NAVIGATION[plugin_name] = {}; } - if (!NAVIGATION[plugin_name].routes) { NAVIGATION[plugin_name].routes = routes; } else { diff --git a/skyquake/framework/core/modules/routes/userManagement.js b/skyquake/framework/core/modules/routes/userManagement.js new file mode 100644 index 000000000..359d9855c --- /dev/null +++ b/skyquake/framework/core/modules/routes/userManagement.js @@ -0,0 +1,64 @@ + +/* + * + * 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. + * + */ + +/** + * inactivity routes module. Provides a RESTful API for this + * skyquake instance's inactivity state. + * @module framework/core/modules/routes/inactivity + * @author Laurence Maultsby + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); +var UserManagementAPI = require('../api/UserManagementAPI.js'); + +Router.use(bodyParser.json()); +Router.use(cors()); +Router.use(bodyParser.urlencoded({ + extended: true +})); + +Router.get('/user', cors(), function(req, res) { + UserManagementAPI.get(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); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); +Router.delete('/user/:username/:domain', cors(), function(req, res) { + UserManagementAPI.delete(req).then(function(response) { + utils.sendSuccessResponse(response, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +module.exports = Router; + + + diff --git a/skyquake/framework/style/_colors.scss b/skyquake/framework/style/_colors.scss index 9378984ba..353a10332 100644 --- a/skyquake/framework/style/_colors.scss +++ b/skyquake/framework/style/_colors.scss @@ -1,6 +1,6 @@ /* - * + * * Copyright 2016 RIFT.IO Inc * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +31,7 @@ $body-color:$lightest-gray; $error-red:#FF5F5F; -//PC +/*PC*/ $black: #000; $gray-lightest: #f1f1f1; @@ -42,9 +42,9 @@ $gray-dark: #999; $gray-darker: #666; $gray-darkest: #333; $white: #FFF; -// -// Brand Colors -// +/**/ +/* Brand Colors*/ +/**/ $brand-blue-light: #30baef; $brand-blue: #00acee; $brand-blue-dark: #147ca3; @@ -68,9 +68,9 @@ $neutral-light-5: hsl(360, 100%, 50%); $neutral-dark-1: hsl(360, 100%, 50%); $neutral-dark-2: hsl(360, 100%, 50%); -$neutral-dark-3: hsl(360, 100%, 50%); -$neutral-dark-4: hsl(360, 100%, 50%); -$neutral-dark-5: hsl(360, 100%, 50%); +$neutral-dark-3: hsl(0, 0%, 49.7%); +$neutral-dark-4: hsl(0, 0%, 42.7%); +$neutral-dark-5: hsl(0, 0%, 35.7%); $netral-black: hsl(0, 100%, 0%); diff --git a/skyquake/framework/widgets/button/button.scss b/skyquake/framework/widgets/button/button.scss index c972e147a..e4eb7a4a3 100644 --- a/skyquake/framework/widgets/button/button.scss +++ b/skyquake/framework/widgets/button/button.scss @@ -1,6 +1,6 @@ /* - * + * * Copyright 2016 RIFT.IO Inc * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -62,15 +62,18 @@ button{ ############################################################################ */ .SqButton { - align-items: center; + -ms-flex-align: center; + align-items: center; border-style: solid; border-radius: 3px; border-width: 0px; cursor: pointer; + display: -ms-inline-flexbox; display: inline-flex; font-size: 1rem; height: 50px; - justify-content: center; + -ms-flex-pack: center; + justify-content: center; margin: 0 10px; outline: none; padding: 0 15px; @@ -107,7 +110,7 @@ button{ /* Focus */ &:focus { - // box-shadow: $focus-shadow; + /* box-shadow: $focus-shadow;*/ border: 1px solid red; } @@ -256,11 +259,14 @@ button{ fill: $primaryForeground; } } - - } - +.sqButtonGroup { + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; +} diff --git a/skyquake/framework/widgets/button/sq-button.jsx b/skyquake/framework/widgets/button/sq-button.jsx index ae9312845..8d0ec7702 100644 --- a/skyquake/framework/widgets/button/sq-button.jsx +++ b/skyquake/framework/widgets/button/sq-button.jsx @@ -36,17 +36,35 @@ export default class SqButton extends React.Component { Class += " is-disabled"; } return ( -
+
- {svgHTML} -
{label}
+ {svgHTML} +
{label}
+
+ ) + } +} + +export class ButtonGroup extends React.Component { + render() { + let className = "sqButtonGroup"; + if (this.props.className) { + className = `${className} ${this.props.className}` + } + return ( +
+ {this.props.children}
) } } + SqButton.defaultProps = { + onClick: function(e) { + console.log('Clicked') + }, icon: false, primary: false, disabled: false, diff --git a/skyquake/framework/widgets/form_controls/formControls.scss b/skyquake/framework/widgets/form_controls/formControls.scss index 4a8843501..fcc08def8 100644 --- a/skyquake/framework/widgets/form_controls/formControls.scss +++ b/skyquake/framework/widgets/form_controls/formControls.scss @@ -1,5 +1,5 @@ /* - * + * * Copyright 2016 RIFT.IO Inc * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -57,4 +57,25 @@ border:0px; height: 100%; } + &.checkbox { + -ms-flex-direction:row; + flex-direction:row; + -ms-flex-align:center; + align-items:center; + margin-bottom:0; + >span { + -ms-flex-order: 1; + order: 1; + padding-left:1rem; + } + >input { + -ms-flex-order: 0; + order: 0; + + box-shadow:none; + height:25px; + } + } } + + diff --git a/skyquake/framework/widgets/form_controls/input.jsx b/skyquake/framework/widgets/form_controls/input.jsx new file mode 100644 index 000000000..b7582424b --- /dev/null +++ b/skyquake/framework/widgets/form_controls/input.jsx @@ -0,0 +1,96 @@ +/* + * + * 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 './formControls.scss'; +import SelectOption from 'widgets/form_controls/selectOption.jsx'; + +import React, {Component} from 'react'; + +export default class Input extends Component { + render() { + let {label, value, defaultValue, ...props} = this.props; + let inputProperties = { + value: value + } + let isRequired; + let inputType; + let className = `sqTextInput ${props.className}`; + + if(this.props.required) { + isRequired = * + } + if (defaultValue) { + inputProperties.defaultValue = defaultValue; + } + if (props.pattern) { + inputProperties.pattern = props.pattern; + } + if(props.hasOwnProperty('type') && (props.type.toLowerCase() == 'checkbox')) { + inputProperties.checked = props.checked; + className = `${className} checkbox`; + } + if (value == undefined) { + value = defaultValue; + } + switch(props.type) { + case 'textarea': + inputType =