From 4a54a7bd6a1e5ca1682be644ef2c040c75ab9df9 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Mon, 27 Jul 2020 19:00:45 +0800 Subject: [PATCH 01/13] Add group vc to user-profile --- src/webportal/src/app/user/fabric/conn.js | 4 + .../src/app/user/fabric/user-profile.jsx | 11 ++ .../user/fabric/user-profile/group-list.jsx | 100 ++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 src/webportal/src/app/user/fabric/user-profile/group-list.jsx diff --git a/src/webportal/src/app/user/fabric/conn.js b/src/webportal/src/app/user/fabric/conn.js index afa48ce0f2..90c9fceee3 100644 --- a/src/webportal/src/app/user/fabric/conn.js +++ b/src/webportal/src/app/user/fabric/conn.js @@ -159,6 +159,10 @@ export const getTokenRequest = async () => { return wrapper(() => client.token.getTokens()); }; +export const getGroupsRequest = async () => { + return wrapper(() => client.group.getAllGroup()); +}; + export const revokeTokenRequest = async token => { await wrapper(() => client.token.deleteToken(token)); if (token === checkToken()) { diff --git a/src/webportal/src/app/user/fabric/user-profile.jsx b/src/webportal/src/app/user/fabric/user-profile.jsx index 5c871d5924..90adb65995 100644 --- a/src/webportal/src/app/user/fabric/user-profile.jsx +++ b/src/webportal/src/app/user/fabric/user-profile.jsx @@ -20,6 +20,7 @@ import { updateUserPasswordRequest, updateUserEmailRequest, listStorageDetailRequest, + getGroupsRequest, } from './conn'; import t from '../../components/tachyons.scss'; @@ -27,6 +28,7 @@ import { VirtualClusterDetailsList } from '../../home/home/virtual-cluster-stati import TokenList from './user-profile/token-list'; import UserProfileHeader from './user-profile/header'; import StorageList from './user-profile/storage-list'; +import GroupList from './user-profile/group-list'; const UserProfileCard = ({ title, children, headerButton }) => { const { spacing } = getTheme(); @@ -58,6 +60,7 @@ const UserProfile = () => { const [virtualClusters, setVirtualClusters] = useState(null); const [tokens, setTokens] = useState(null); const [storageDetails, setStorageDetails] = useState(null); + const [groups, setGroups] = useState(null); const [processing, setProcessing] = useState(false); @@ -67,11 +70,13 @@ const UserProfile = () => { const vcPromise = getAllVcsRequest(); const tokenPromise = getTokenRequest(); const storageDetailPromise = listStorageDetailRequest(); + const groupsPromise = getGroupsRequest(); await Promise.all([ userPromise, vcPromise, tokenPromise, storageDetailPromise, + groupsPromise, ]).catch(err => { alert(err); throw err; @@ -96,6 +101,9 @@ const UserProfile = () => { const storageDetails = await storageDetailPromise; setStorageDetails(storageDetails); setLoading(false); + // group + const groups = await groupsPromise; + setGroups(groups); }; fetchData(); }, []); @@ -163,6 +171,9 @@ const UserProfile = () => { + + + ); diff --git a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx new file mode 100644 index 0000000000..9e46004102 --- /dev/null +++ b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import c from 'classnames'; +import React, { useMemo } from 'react'; +import PropTypes from 'prop-types'; +import { + DetailsList, + DetailsListLayoutMode, + SelectionMode, +} from 'office-ui-fabric-react'; + +import t from '../../../components/tachyons.scss'; +import CopyButton from '../../../components/copy-button'; + +const GroupList = ({ groups }) => { + const groupItems = useMemo(() => { + return groups; + }, [groups]); + + const groupColumns = [ + { + key: 'groupname', + minWidth: 120, + maxWidth: 150, + name: 'Group Name', + isResizable: true, + onRender(group) { + return ( +
+ {group.groupname} +
+ ); + }, + }, + { + key: 'externalname', + minWidth: 150, + maxWidth: 150, + name: 'External Name', + isResizable: true, + onRender(group) { + return ( +
+ {group.externalName} +
+ ); + }, + }, + { + key: 'virtualClusters', + minWidth: 150, + maxWidth: 150, + name: 'Virtual Clusters', + isResizable: true, + onRender(group) { + return ( +
+ {group.extension.acls.virtualClusters.join()} +
+ ); + }, + }, + { + key: 'description', + minWidth: 150, + name: 'Description', + isResizable: true, + onRender(group) { + return ( +
+ {group.description} +
+ ); + }, + }, + ]; + + return ( +
+ +
+ ); +}; + +GroupList.defaultProps = { + groups: [], +}; + +GroupList.propTypes = { + groups: PropTypes.arrayOf(PropTypes.object), +}; + +export default GroupList; From b62099b37a89b7323db84d28a1633f412933b119 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 4 Aug 2020 15:01:17 +0800 Subject: [PATCH 02/13] update --- src/webportal/src/app/user/fabric/user-profile/group-list.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx index 9e46004102..5fd2b6bd74 100644 --- a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx +++ b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx @@ -11,7 +11,6 @@ import { } from 'office-ui-fabric-react'; import t from '../../../components/tachyons.scss'; -import CopyButton from '../../../components/copy-button'; const GroupList = ({ groups }) => { const groupItems = useMemo(() => { From c8e403cc01da479c0f275e88fcb53b4fcbb94eba Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 11 Aug 2020 04:31:38 +0800 Subject: [PATCH 03/13] update webportal --- src/webportal/src/app/home/index.jsx | 25 +++++- .../src/app/home/index/guestWarning.jsx | 82 +++++++++++++++++++ .../src/app/user/fabric/user-profile.jsx | 2 +- .../user/fabric/user-profile/group-list.jsx | 30 ++++++- .../user/fabric/user-profile/storage-list.jsx | 6 ++ 5 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 src/webportal/src/app/home/index/guestWarning.jsx diff --git a/src/webportal/src/app/home/index.jsx b/src/webportal/src/app/home/index.jsx index 944eb91f3e..5d7ba56fba 100644 --- a/src/webportal/src/app/home/index.jsx +++ b/src/webportal/src/app/home/index.jsx @@ -35,15 +35,30 @@ import { checkToken } from '../user/user-auth/user-auth.component'; import config from '../config/webportal.config'; import { SpinnerLoading } from '../components/loading'; import t from 'tachyons-sass/tachyons.scss'; +import GuestWarning from './index/guestWarning'; let loginTarget = '/home.html'; const query = new URLSearchParams(window.location.search); +const guest = { + open: false, + groups: [], + forbiddenMessage: '', + onClose: () => { + location.href = '/index.html'; + }, +}; if (query.has('errorMessage')) { const errorMessage = query.get('errorMessage'); - alert(errorMessage); - location.href = '/index.html'; + if (errorMessage.includes('AAD-Groups:')) { + guest.forbiddenMessage = errorMessage.split('AAD-Groups:')[0]; + guest.groups = JSON.parse(errorMessage.split('AAD-Groups:')[1]); + guest.open = true; + } else { + alert(errorMessage); + location.href = '/index.html'; + } } const from = query.get('from'); @@ -125,6 +140,12 @@ const Index = () => { FontClassNames.medium, )} > + {/* top */}
{ + onClose(); + }; + + return ( +
, + subText:
{forbiddenMessage}
, + }} + > +
+
Group name
+
External name
+
Description
+
+
+ ( +
+
{group.groupname}
+
{group.externalName}
+
{group.description}
+
+ +
+
+ )} + /> + + + + + ); +} + +GuestWarning.propTypes = { + onClose: PropTypes.func.isRequired, + forbiddenMessage: PropTypes.string.isRequired, + open: PropTypes.bool.isRequired, + groups: PropTypes.array.isRequired, +}; diff --git a/src/webportal/src/app/user/fabric/user-profile.jsx b/src/webportal/src/app/user/fabric/user-profile.jsx index 90adb65995..ab8dff1285 100644 --- a/src/webportal/src/app/user/fabric/user-profile.jsx +++ b/src/webportal/src/app/user/fabric/user-profile.jsx @@ -172,7 +172,7 @@ const UserProfile = () => { - + diff --git a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx index 5fd2b6bd74..60a66c7842 100644 --- a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx +++ b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx @@ -8,16 +8,42 @@ import { DetailsList, DetailsListLayoutMode, SelectionMode, + DefaultButton, } from 'office-ui-fabric-react'; import t from '../../../components/tachyons.scss'; -const GroupList = ({ groups }) => { +const GroupList = ({ userInfo, groups }) => { const groupItems = useMemo(() => { return groups; }, [groups]); const groupColumns = [ + { + key: 'status', + minWidth: 60, + maxWidth: 80, + name: 'Status', + isResizable: true, + onRender(group) { + const membership = userInfo.grouplist.includes(group.groupname); + return ( +
+ +
+ ); + }, + }, { key: 'groupname', minWidth: 120, @@ -89,10 +115,12 @@ const GroupList = ({ groups }) => { }; GroupList.defaultProps = { + userInfo: {}, groups: [], }; GroupList.propTypes = { + userInfo: PropTypes.object, groups: PropTypes.arrayOf(PropTypes.object), }; diff --git a/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx b/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx index 418d0ef75d..672e8fa719 100644 --- a/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx +++ b/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx @@ -45,6 +45,12 @@ function getStorageServerUri(storageDetail) { ); case 'hdfs': return `hdfs://${data.namenode}:${data.port}`; + case 'dshuttle': + return ( + <> + {'Dshuttle'} + + ); default: throw new Error('Invalid storage server type'); } From 55126a97afe3b192f4f0b3c27b35e7539ea500f0 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 11 Aug 2020 04:32:28 +0800 Subject: [PATCH 04/13] update rest-server --- src/rest-server/src/controllers/v2/user.js | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/rest-server/src/controllers/v2/user.js b/src/rest-server/src/controllers/v2/user.js index 481b39532b..5a7d30b0e6 100644 --- a/src/rest-server/src/controllers/v2/user.js +++ b/src/rest-server/src/controllers/v2/user.js @@ -78,6 +78,24 @@ const getAllUser = async (req, res, next) => { } }; +const showForGuest = (group, guestGroups) => { + if (group && group.extension && group.extension.acls && group.extension.acls.showForGuest) { + guestGroups.push(group); + } +}; + +const getGuestGroups = () => { + const guestGroups = []; + showForGuest(authConfig.groupConfig.adminGroup, guestGroups); + showForGuest(authConfig.groupConfig.defaultGroup, guestGroups); + + for (const groupItem of authConfig.groupConfig.grouplist) { + showForGuest(groupItem, guestGroups); + } + + return guestGroups; +}; + // OIDC const createUserIfUserNotExist = async (req, res, next) => { try { @@ -95,7 +113,11 @@ const createUserIfUserNotExist = async (req, res, next) => { if (grouplist && grouplist.length === 0) { let forbiddenMessage = `User ${userData.username} is not in configured groups.`; if (authConfig.groupConfig.groupDataSource === 'ms-graph') { - forbiddenMessage = forbiddenMessage + `Please contact your admin, and join the AAD group named [ ${authConfig.groupConfig.defaultGroup.externalName} ].`; + forbiddenMessage = forbiddenMessage + `Please contact your admin, and join the AAD group.`; + const guestGroups = getGuestGroups(); + if (guestGroups.length > 0) { + forbiddenMessage = forbiddenMessage + `AAD-Groups:${JSON.stringify(guestGroups)}`; + } } let forbiddenError = createError('Forbidden', 'ForbiddenUserError', forbiddenMessage); forbiddenError.targetURI = req.returnBackURI; From 60112fb45eed53e9200477194c42e93f2a1322dd Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 11 Aug 2020 10:42:53 +0800 Subject: [PATCH 05/13] update --- src/webportal/src/app/home/index/guestWarning.jsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/webportal/src/app/home/index/guestWarning.jsx b/src/webportal/src/app/home/index/guestWarning.jsx index 79c8a11af3..68f9d3a569 100644 --- a/src/webportal/src/app/home/index/guestWarning.jsx +++ b/src/webportal/src/app/home/index/guestWarning.jsx @@ -26,8 +26,8 @@ export default function GuestWarning(props) { onDismiss={handleClose} hidden={!open} title='Forbidden Error' - minWidth={500} - maxWidth={600} + minWidth={600} + maxWidth={700} dialogContentProps={{ showCloseButton: false, type: DialogType.normal, @@ -41,7 +41,8 @@ export default function GuestWarning(props) {
Group name
External name
-
Description
+
Description
+
virtual clusters

{group.groupname}
{group.externalName}
-
{group.description}
-
+
{group.description}
+
+ {group.extension.acls.virtualClusters.join()} +
+
Date: Sun, 16 Aug 2020 14:30:14 +0800 Subject: [PATCH 06/13] add groups to vc card --- .../home/home/virtual-cluster-statistics.jsx | 280 ++++++++++-------- .../src/app/user/fabric/user-profile.jsx | 5 +- 2 files changed, 160 insertions(+), 125 deletions(-) diff --git a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx index 9030465a27..67d819f97a 100644 --- a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx +++ b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx @@ -1,19 +1,5 @@ -// Copyright (c) Microsoft Corporation -// All rights reserved. -// -// MIT License -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and -// to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING -// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. import c from 'classnames'; import PropTypes from 'prop-types'; @@ -46,117 +32,162 @@ const getResouceUtilization = (used, guaranteed) => { }; const isAdmin = cookies.get('admin') === 'true'; -const vcListColumns = [ - { - key: 'name', - minWidth: 45, - maxWidth: 100, - name: 'Name', - isResizable: true, - onRender(vc) { - return ( - - {isAdmin ? ( - - {vc.dedicated ? vc.name + ' (dedicated)' : vc.name} - - ) : ( -
- {vc.dedicated ? vc.name + ' (dedicated)' : vc.name} -
- )} -
- ); +const vcListColumns = probs => { + const columns = [ + { + key: 'name', + minWidth: 45, + maxWidth: 100, + name: 'Name', + isResizable: true, + onRender(vc) { + return ( + + {isAdmin ? ( + + {vc.dedicated ? vc.name + ' (dedicated)' : vc.name} + + ) : ( +
+ {vc.dedicated ? vc.name + ' (dedicated)' : vc.name} +
+ )} +
+ ); + }, }, - }, - { - key: 'allocation', - minWidth: 80, - maxWidth: 132, - name: 'Allocation', - isResizable: true, - className: zeroPaddingClass, - onRender(vc) { - const { resourcesUsed, resourcesTotal } = vc; - const resourcesGuaranteed = vc.resourcesGuaranteed || resourcesTotal; + { + key: 'allocation', + minWidth: 80, + maxWidth: 132, + name: 'Allocation', + isResizable: true, + className: zeroPaddingClass, + onRender(vc) { + const { resourcesUsed, resourcesTotal } = vc; + const resourcesGuaranteed = vc.resourcesGuaranteed || resourcesTotal; - const resouceUtilization = Math.max( - getResouceUtilization(resourcesUsed.GPUs, resourcesGuaranteed.GPUs), - getResouceUtilization(resourcesUsed.memory, resourcesGuaranteed.memory), - getResouceUtilization(resourcesUsed.vCores, resourcesGuaranteed.vCores), - ); - return ( - - - - ); + const resouceUtilization = Math.max( + getResouceUtilization(resourcesUsed.GPUs, resourcesGuaranteed.GPUs), + getResouceUtilization( + resourcesUsed.memory, + resourcesGuaranteed.memory, + ), + getResouceUtilization( + resourcesUsed.vCores, + resourcesGuaranteed.vCores, + ), + ); + return ( + + + + ); + }, }, - }, - { - key: 'detail', - minWidth: 200, - name: 'Detail: Used / (Total - Bad)', - isResizable: true, - onRender(vc) { - const { resourcesUsed, resourcesTotal } = vc; - const resourcesGuaranteed = vc.resourcesGuaranteed || resourcesTotal; - return ( - - - - - - - - - - - - ); + { + key: 'detail', + minWidth: 200, + name: 'Detail: Used / (Total - Bad)', + isResizable: true, + onRender(vc) { + const { resourcesUsed, resourcesTotal } = vc; + const resourcesGuaranteed = vc.resourcesGuaranteed || resourcesTotal; + return ( + + + + + + + + + + + + ); + }, }, - }, -]; + ]; + + if (probs.groups) { + columns.push({ + key: 'groups', + minWidth: 85, + name: 'Groups', + isResizable: true, + onRender(vc) { + const groups = []; + probs.groups.forEach(group => { + const virtualClusters = group.extension.acls.virtualClusters; + if (virtualClusters && virtualClusters.includes(vc.name)) { + groups.push(group); + } + }); + const style = { + listStyleType: 'none', + paddingLeft: 0, + }; + return ( + +
    + {groups.map((group, index) => { + return ( +
  • + {group.groupname} +
  • + ); + })} +
+
+ ); + }, + }); + } + + return columns; +}; export const VirtualClusterDetailsList = props => { const virtualClusters = props.virtualClusters; @@ -164,12 +195,13 @@ export const VirtualClusterDetailsList = props => { ...props, }; delete otherProps.virtualClusters; + delete otherProps.groups; const vcList = Object.entries(virtualClusters).map(([key, val]) => { return { name: key, ...val }; }); return ( { - + From f09ac8b45747abb699e7ed895dcc7505da46f922 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Mon, 17 Aug 2020 03:20:47 +0800 Subject: [PATCH 07/13] update --- src/webportal/package.json | 1 + .../home/home/virtual-cluster-statistics.jsx | 41 +++++- .../src/app/user/fabric/user-profile.jsx | 4 - .../user/fabric/user-profile/group-list.jsx | 127 ------------------ src/webportal/yarn.lock | 13 ++ 5 files changed, 50 insertions(+), 136 deletions(-) delete mode 100644 src/webportal/src/app/user/fabric/user-profile/group-list.jsx diff --git a/src/webportal/package.json b/src/webportal/package.json index 9b4677afa9..57a79c7997 100644 --- a/src/webportal/package.json +++ b/src/webportal/package.json @@ -76,6 +76,7 @@ "react-monaco-editor": "^0.25.1", "react-responsive": "^8.0.1", "react-router-dom": "^5.0.1", + "react-tooltip": "^4.2.8", "regenerator-runtime": "^0.13.2", "serve-favicon": "~2.5.0", "sshpk": "^1.16.1", diff --git a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx index 67d819f97a..a7156b2284 100644 --- a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx +++ b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx @@ -14,6 +14,7 @@ import { Link, } from 'office-ui-fabric-react'; import React, { useMemo } from 'react'; +import ReactTooltip from 'react-tooltip'; import Card from '../../components/card'; import { UtilizationChart } from './utilization-chart'; @@ -151,7 +152,7 @@ const vcListColumns = probs => { }, ]; - if (probs.groups) { + if (isAdmin && probs.groups) { columns.push({ key: 'groups', minWidth: 85, @@ -170,13 +171,43 @@ const vcListColumns = probs => { paddingLeft: 0, }; return ( - +
    {groups.map((group, index) => { return ( -
  • - {group.groupname} -
  • +
    + + {group.groupname} + + +

    + + {group.groupname} + +   + ( + {group.externalName} + ) +

    +

    Description:

    +

    {group.description}

    +
    +
    ); })}
diff --git a/src/webportal/src/app/user/fabric/user-profile.jsx b/src/webportal/src/app/user/fabric/user-profile.jsx index 0ef5a1ea01..96f40fd597 100644 --- a/src/webportal/src/app/user/fabric/user-profile.jsx +++ b/src/webportal/src/app/user/fabric/user-profile.jsx @@ -28,7 +28,6 @@ import { VirtualClusterDetailsList } from '../../home/home/virtual-cluster-stati import TokenList from './user-profile/token-list'; import UserProfileHeader from './user-profile/header'; import StorageList from './user-profile/storage-list'; -import GroupList from './user-profile/group-list'; const UserProfileCard = ({ title, children, headerButton }) => { const { spacing } = getTheme(); @@ -174,9 +173,6 @@ const UserProfile = () => { groups={groups} />
- - -
); diff --git a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx b/src/webportal/src/app/user/fabric/user-profile/group-list.jsx deleted file mode 100644 index 60a66c7842..0000000000 --- a/src/webportal/src/app/user/fabric/user-profile/group-list.jsx +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import c from 'classnames'; -import React, { useMemo } from 'react'; -import PropTypes from 'prop-types'; -import { - DetailsList, - DetailsListLayoutMode, - SelectionMode, - DefaultButton, -} from 'office-ui-fabric-react'; - -import t from '../../../components/tachyons.scss'; - -const GroupList = ({ userInfo, groups }) => { - const groupItems = useMemo(() => { - return groups; - }, [groups]); - - const groupColumns = [ - { - key: 'status', - minWidth: 60, - maxWidth: 80, - name: 'Status', - isResizable: true, - onRender(group) { - const membership = userInfo.grouplist.includes(group.groupname); - return ( -
- -
- ); - }, - }, - { - key: 'groupname', - minWidth: 120, - maxWidth: 150, - name: 'Group Name', - isResizable: true, - onRender(group) { - return ( -
- {group.groupname} -
- ); - }, - }, - { - key: 'externalname', - minWidth: 150, - maxWidth: 150, - name: 'External Name', - isResizable: true, - onRender(group) { - return ( -
- {group.externalName} -
- ); - }, - }, - { - key: 'virtualClusters', - minWidth: 150, - maxWidth: 150, - name: 'Virtual Clusters', - isResizable: true, - onRender(group) { - return ( -
- {group.extension.acls.virtualClusters.join()} -
- ); - }, - }, - { - key: 'description', - minWidth: 150, - name: 'Description', - isResizable: true, - onRender(group) { - return ( -
- {group.description} -
- ); - }, - }, - ]; - - return ( -
- -
- ); -}; - -GroupList.defaultProps = { - userInfo: {}, - groups: [], -}; - -GroupList.propTypes = { - userInfo: PropTypes.object, - groups: PropTypes.arrayOf(PropTypes.object), -}; - -export default GroupList; diff --git a/src/webportal/yarn.lock b/src/webportal/yarn.lock index b56cbc9394..f0a55432ef 100644 --- a/src/webportal/yarn.lock +++ b/src/webportal/yarn.lock @@ -8704,6 +8704,14 @@ react-router@5.0.1: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-tooltip@^4.2.8: + version "4.2.8" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.8.tgz#270858fee46fab73b66de316271aa94145f7446b" + integrity sha512-pDWa0/khTAgIfldp95tHgyuYyBhWNlfaU2LF9ubAKxpoqNe15uyf+uLlnhK/Lstb6FU8E8/SL28Wp6oEO9xw3g== + dependencies: + prop-types "^15.7.2" + uuid "^7.0.3" + react@^16.8.3: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" @@ -10454,6 +10462,11 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + uuid@^8.1.0: version "8.2.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e" From 10b344be109904271edca49622df0f0ce6fd5bc0 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Mon, 17 Aug 2020 03:24:10 +0800 Subject: [PATCH 08/13] update --- src/rest-server/src/controllers/v2/user.js | 24 +----- src/webportal/src/app/home/index.jsx | 25 +----- .../src/app/home/index/guestWarning.jsx | 86 ------------------- 3 files changed, 3 insertions(+), 132 deletions(-) delete mode 100644 src/webportal/src/app/home/index/guestWarning.jsx diff --git a/src/rest-server/src/controllers/v2/user.js b/src/rest-server/src/controllers/v2/user.js index 5a7d30b0e6..481b39532b 100644 --- a/src/rest-server/src/controllers/v2/user.js +++ b/src/rest-server/src/controllers/v2/user.js @@ -78,24 +78,6 @@ const getAllUser = async (req, res, next) => { } }; -const showForGuest = (group, guestGroups) => { - if (group && group.extension && group.extension.acls && group.extension.acls.showForGuest) { - guestGroups.push(group); - } -}; - -const getGuestGroups = () => { - const guestGroups = []; - showForGuest(authConfig.groupConfig.adminGroup, guestGroups); - showForGuest(authConfig.groupConfig.defaultGroup, guestGroups); - - for (const groupItem of authConfig.groupConfig.grouplist) { - showForGuest(groupItem, guestGroups); - } - - return guestGroups; -}; - // OIDC const createUserIfUserNotExist = async (req, res, next) => { try { @@ -113,11 +95,7 @@ const createUserIfUserNotExist = async (req, res, next) => { if (grouplist && grouplist.length === 0) { let forbiddenMessage = `User ${userData.username} is not in configured groups.`; if (authConfig.groupConfig.groupDataSource === 'ms-graph') { - forbiddenMessage = forbiddenMessage + `Please contact your admin, and join the AAD group.`; - const guestGroups = getGuestGroups(); - if (guestGroups.length > 0) { - forbiddenMessage = forbiddenMessage + `AAD-Groups:${JSON.stringify(guestGroups)}`; - } + forbiddenMessage = forbiddenMessage + `Please contact your admin, and join the AAD group named [ ${authConfig.groupConfig.defaultGroup.externalName} ].`; } let forbiddenError = createError('Forbidden', 'ForbiddenUserError', forbiddenMessage); forbiddenError.targetURI = req.returnBackURI; diff --git a/src/webportal/src/app/home/index.jsx b/src/webportal/src/app/home/index.jsx index 5d7ba56fba..944eb91f3e 100644 --- a/src/webportal/src/app/home/index.jsx +++ b/src/webportal/src/app/home/index.jsx @@ -35,30 +35,15 @@ import { checkToken } from '../user/user-auth/user-auth.component'; import config from '../config/webportal.config'; import { SpinnerLoading } from '../components/loading'; import t from 'tachyons-sass/tachyons.scss'; -import GuestWarning from './index/guestWarning'; let loginTarget = '/home.html'; const query = new URLSearchParams(window.location.search); -const guest = { - open: false, - groups: [], - forbiddenMessage: '', - onClose: () => { - location.href = '/index.html'; - }, -}; if (query.has('errorMessage')) { const errorMessage = query.get('errorMessage'); - if (errorMessage.includes('AAD-Groups:')) { - guest.forbiddenMessage = errorMessage.split('AAD-Groups:')[0]; - guest.groups = JSON.parse(errorMessage.split('AAD-Groups:')[1]); - guest.open = true; - } else { - alert(errorMessage); - location.href = '/index.html'; - } + alert(errorMessage); + location.href = '/index.html'; } const from = query.get('from'); @@ -140,12 +125,6 @@ const Index = () => { FontClassNames.medium, )} > - {/* top */}
{ - onClose(); - }; - - return ( -
, - subText:
{forbiddenMessage}
, - }} - > -
-
Group name
-
External name
-
Description
-
virtual clusters
-
-
- ( -
-
{group.groupname}
-
{group.externalName}
-
{group.description}
-
- {group.extension.acls.virtualClusters.join()} -
-
- -
-
- )} - /> - - - - - ); -} - -GuestWarning.propTypes = { - onClose: PropTypes.func.isRequired, - forbiddenMessage: PropTypes.string.isRequired, - open: PropTypes.bool.isRequired, - groups: PropTypes.array.isRequired, -}; From bc373a72b6ed775c3cdb72afea805f3b36afddac Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Mon, 24 Aug 2020 16:35:15 +0800 Subject: [PATCH 09/13] update --- src/webportal/package.json | 1 - .../home/home/virtual-cluster-statistics.jsx | 108 ++++++++---------- .../user/fabric/user-profile/storage-list.jsx | 4 +- src/webportal/yarn.lock | 13 --- 4 files changed, 52 insertions(+), 74 deletions(-) diff --git a/src/webportal/package.json b/src/webportal/package.json index 57a79c7997..9b4677afa9 100644 --- a/src/webportal/package.json +++ b/src/webportal/package.json @@ -76,7 +76,6 @@ "react-monaco-editor": "^0.25.1", "react-responsive": "^8.0.1", "react-router-dom": "^5.0.1", - "react-tooltip": "^4.2.8", "regenerator-runtime": "^0.13.2", "serve-favicon": "~2.5.0", "sshpk": "^1.16.1", diff --git a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx index a7156b2284..667b99b09f 100644 --- a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx +++ b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx @@ -12,9 +12,10 @@ import { StackItem, FontClassNames, Link, + DefaultButton, + Dialog, } from 'office-ui-fabric-react'; -import React, { useMemo } from 'react'; -import ReactTooltip from 'react-tooltip'; +import React, { useMemo, useState } from 'react'; import Card from '../../components/card'; import { UtilizationChart } from './utilization-chart'; @@ -32,8 +33,17 @@ const getResouceUtilization = (used, guaranteed) => { return used / guaranteed; }; +const getGrantedGroupsDescription = groups => { + const str = groups.map(group => group.groupname).join(); + if (str.length < 25) { + return str; + } else { + return str.substring(0, 22) + '...'; + } +}; + const isAdmin = cookies.get('admin') === 'true'; -const vcListColumns = probs => { +const vcListColumns = props => { const columns = [ { key: 'name', @@ -91,7 +101,7 @@ const vcListColumns = probs => { }, { key: 'detail', - minWidth: 200, + minWidth: 100, name: 'Detail: Used / (Total - Bad)', isResizable: true, onRender(vc) { @@ -152,65 +162,37 @@ const vcListColumns = probs => { }, ]; - if (isAdmin && probs.groups) { + if (isAdmin && props.groups) { columns.push({ key: 'groups', - minWidth: 85, - name: 'Groups', + minWidth: 135, + name: 'Granted groups', isResizable: true, onRender(vc) { const groups = []; - probs.groups.forEach(group => { + props.groups.forEach(group => { const virtualClusters = group.extension.acls.virtualClusters; if (virtualClusters && virtualClusters.includes(vc.name)) { groups.push(group); } }); - const style = { - listStyleType: 'none', - paddingLeft: 0, - }; return ( -
    - {groups.map((group, index) => { - return ( -
    - - {group.groupname} - - -

    - - {group.groupname} - -   - ( - {group.externalName} - ) -

    -

    Description:

    -

    {group.description}

    -
    -
    - ); - })} -
+
+

{getGrantedGroupsDescription(groups)}

+
+ { + console.log('click details'); + props.setHideGroupDetails(true); + }} + />
); }, @@ -230,15 +212,25 @@ export const VirtualClusterDetailsList = props => { const vcList = Object.entries(virtualClusters).map(([key, val]) => { return { name: key, ...val }; }); + const [hideGroupDetails, setHideGroupDetails] = useState(true); + return ( - + <> + + + ); }; diff --git a/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx b/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx index 672e8fa719..3612770eea 100644 --- a/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx +++ b/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx @@ -45,10 +45,10 @@ function getStorageServerUri(storageDetail) { ); case 'hdfs': return `hdfs://${data.namenode}:${data.port}`; - case 'dshuttle': + case 'unknown': return ( <> - {'Dshuttle'} + {'Unknown'} ); default: diff --git a/src/webportal/yarn.lock b/src/webportal/yarn.lock index f0a55432ef..b56cbc9394 100644 --- a/src/webportal/yarn.lock +++ b/src/webportal/yarn.lock @@ -8704,14 +8704,6 @@ react-router@5.0.1: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-tooltip@^4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.8.tgz#270858fee46fab73b66de316271aa94145f7446b" - integrity sha512-pDWa0/khTAgIfldp95tHgyuYyBhWNlfaU2LF9ubAKxpoqNe15uyf+uLlnhK/Lstb6FU8E8/SL28Wp6oEO9xw3g== - dependencies: - prop-types "^15.7.2" - uuid "^7.0.3" - react@^16.8.3: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" @@ -10462,11 +10454,6 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - uuid@^8.1.0: version "8.2.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e" From 71d66e1257a8103551d7e0b9a1fa1eede5022cf3 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Mon, 24 Aug 2020 20:07:36 +0800 Subject: [PATCH 10/13] update --- .../home/home/virtual-cluster-statistics.jsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx index 667b99b09f..2d8ea1e0da 100644 --- a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx +++ b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx @@ -15,7 +15,7 @@ import { DefaultButton, Dialog, } from 'office-ui-fabric-react'; -import React, { useMemo, useState } from 'react'; +import React, { useMemo, useState, useContext } from 'react'; import Card from '../../components/card'; import { UtilizationChart } from './utilization-chart'; @@ -43,7 +43,7 @@ const getGrantedGroupsDescription = groups => { }; const isAdmin = cookies.get('admin') === 'true'; -const vcListColumns = props => { +const vcListColumns = colProps => { const columns = [ { key: 'name', @@ -162,7 +162,7 @@ const vcListColumns = props => { }, ]; - if (isAdmin && props.groups) { + if (isAdmin && colProps.groups) { columns.push({ key: 'groups', minWidth: 135, @@ -170,7 +170,7 @@ const vcListColumns = props => { isResizable: true, onRender(vc) { const groups = []; - props.groups.forEach(group => { + colProps.groups.forEach(group => { const virtualClusters = group.extension.acls.virtualClusters; if (virtualClusters && virtualClusters.includes(vc.name)) { groups.push(group); @@ -189,10 +189,15 @@ const vcListColumns = props => { { - console.log('click details'); - props.setHideGroupDetails(true); + colProps.setHideGroupDetails(false); }} /> + ); }, @@ -217,19 +222,16 @@ export const VirtualClusterDetailsList = props => { return ( <> - ); }; From ac67445b78d6be6ad2ddc09557fd610cd8267a5a Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 25 Aug 2020 02:27:49 +0800 Subject: [PATCH 11/13] update --- .../src/app/components/copy-button.jsx | 8 +- .../home/home/virtual-cluster-statistics.jsx | 153 ++++++++++++++---- .../user/fabric/user-profile/storage-list.jsx | 1 - 3 files changed, 128 insertions(+), 34 deletions(-) diff --git a/src/webportal/src/app/components/copy-button.jsx b/src/webportal/src/app/components/copy-button.jsx index 4834ac6c98..414251f721 100644 --- a/src/webportal/src/app/components/copy-button.jsx +++ b/src/webportal/src/app/components/copy-button.jsx @@ -22,7 +22,7 @@ import { IconButton, FontSizes, TooltipHost } from 'office-ui-fabric-react'; const COPIED_TOOLTIP_CLOSE_DELAY = 1000; -const CopyButton = ({ value }) => { +const CopyButton = ({ value, hideTooltip, callback }) => { const ref = useRef(null); return (
@@ -35,9 +35,13 @@ const CopyButton = ({ value }) => { setTimeout(() => { ref.current && ref.current.dismiss(); }, COPIED_TOOLTIP_CLOSE_DELAY); + if (callback) { + callback(); + } }} />
+ ); + }, + }, + { + key: 'alias', + minWidth: 180, + maxWidth: 250, + name: 'Group alias', + isResizable: true, + onRender(group) { + return ( +
+
+ {group.externalName} +
+
+ { + colProps.setCopied(group.externalName); + }} + /> + +
+
+ ); + }, + }, + { + key: 'description', + minWidth: 180, + name: 'Description', + isResizable: true, + onRender(group) { + return ( +
+ {group.description} +
+ ); + }, + }, + ]} + disableSelectionZone + items={groups} + layoutMode={DetailsListLayoutMode.justified} + selectionMode={SelectionMode.none} + /> - + ); }, }); @@ -218,21 +308,20 @@ export const VirtualClusterDetailsList = props => { return { name: key, ...val }; }); const [hideGroupDetails, setHideGroupDetails] = useState(true); + const [copied, setCopied] = useState(null); return ( - <> - - + ); }; diff --git a/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx b/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx index 3612770eea..79a912e9ba 100644 --- a/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx +++ b/src/webportal/src/app/user/fabric/user-profile/storage-list.jsx @@ -57,7 +57,6 @@ function getStorageServerUri(storageDetail) { } const StorageList = ({ storageDetails }) => { - console.log(storageDetails); const [items, groups] = useMemo(() => { const items = []; const groups = []; From 30fbca43889c02a4225b9d7985e29ed3eb28d8a6 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 25 Aug 2020 16:51:17 +0800 Subject: [PATCH 12/13] update --- .../src/app/home/home/groupDetailDialog.jsx | 124 +++++++++++++++++ .../home/home/virtual-cluster-statistics.jsx | 127 +++--------------- 2 files changed, 139 insertions(+), 112 deletions(-) create mode 100644 src/webportal/src/app/home/home/groupDetailDialog.jsx diff --git a/src/webportal/src/app/home/home/groupDetailDialog.jsx b/src/webportal/src/app/home/home/groupDetailDialog.jsx new file mode 100644 index 0000000000..17e96cab47 --- /dev/null +++ b/src/webportal/src/app/home/home/groupDetailDialog.jsx @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { FontClassNames } from '@uifabric/styling'; +import c from 'classnames'; +import PropTypes from 'prop-types'; +import React, { useContext } from 'react'; +import { + Dialog, + DetailsList, + DetailsListLayoutMode, + SelectionMode, +} from 'office-ui-fabric-react'; +import CopyButton from '../../components/copy-button'; + +import t from '../../../../components/tachyons.scss'; + +const CopySucceeded = props => + props.copied ?

Copied succeeded!

: null; + +CopySucceeded.propTypes = { + copied: PropTypes.bool, +}; + +export default function GroupDetailDialog(props) { + const { hideDialog, setHideDialog, vc, groups } = props; + + return ( + + ); +} + +GroupDetailDialog.propTypes = { + hideDialog: PropTypes.bool.isRequired, + setHideDialog: PropTypes.func.isRequired, + vc: PropTypes.object, + groups: PropTypes.array, +}; diff --git a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx index 51243c77a3..f8ed475a63 100644 --- a/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx +++ b/src/webportal/src/app/home/home/virtual-cluster-statistics.jsx @@ -13,7 +13,6 @@ import { FontClassNames, Link, DefaultButton, - Dialog, } from 'office-ui-fabric-react'; import React, { useMemo, useState } from 'react'; @@ -22,7 +21,6 @@ import { UtilizationChart } from './utilization-chart'; import { zeroPaddingClass } from './util'; import { Header } from './header'; import config from '../../config/webportal.config'; -import CopyButton from '../../components/copy-button'; import t from '../../components/tachyons.scss'; import { ResourceBar } from './resource-bar'; @@ -164,12 +162,6 @@ const vcListColumns = colProps => { ]; if (isAdmin && colProps.groups) { - const CopySucceeded = props => - props.copied ?

Copied succeeded!

: null; - CopySucceeded.propTypes = { - copied: PropTypes.bool, - }; - columns.push({ key: 'groups', minWidth: 250, @@ -184,111 +176,22 @@ const vcListColumns = colProps => { } }); return ( - <> - -
-

{getGrantedGroupsDescription(groups)}

-
- { - colProps.setHideGroupDetails(false); - }} - /> -
- - + /> + ); }, }); From 95ea05bca89a965367f3f541dc53bff180bad688 Mon Sep 17 00:00:00 2001 From: Yi Yi Date: Tue, 25 Aug 2020 17:27:49 +0800 Subject: [PATCH 13/13] fix --- .../src/app/home/home/groupDetailDialog.jsx | 29 +++++++------- .../home/home/virtual-cluster-statistics.jsx | 38 ++++++++++++------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/webportal/src/app/home/home/groupDetailDialog.jsx b/src/webportal/src/app/home/home/groupDetailDialog.jsx index 17e96cab47..57af8072bf 100644 --- a/src/webportal/src/app/home/home/groupDetailDialog.jsx +++ b/src/webportal/src/app/home/home/groupDetailDialog.jsx @@ -4,7 +4,7 @@ import { FontClassNames } from '@uifabric/styling'; import c from 'classnames'; import PropTypes from 'prop-types'; -import React, { useContext } from 'react'; +import React, { useState } from 'react'; import { Dialog, DetailsList, @@ -13,7 +13,7 @@ import { } from 'office-ui-fabric-react'; import CopyButton from '../../components/copy-button'; -import t from '../../../../components/tachyons.scss'; +import t from '../../components/tachyons.scss'; const CopySucceeded = props => props.copied ?

Copied succeeded!

: null; @@ -23,16 +23,19 @@ CopySucceeded.propTypes = { }; export default function GroupDetailDialog(props) { - const { hideDialog, setHideDialog, vc, groups } = props; + const { groupDetails, setGroupDetails } = props; + const [copiedName, setCopiedName] = useState(null); return (