From 0c49e6f0ded053c1f90c5a36d3b24ff6a2d995cb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 19 Jul 2023 20:07:55 -0400 Subject: [PATCH 1/2] GDAP Invites - New wizard to create invites - GDAP Relationship - Add button for manually kicking off group mapping - Fix bug with GDAP relationship offcanvas --- src/_nav.js | 5 + src/adminRoutes.js | 2 + src/components/tables/CippTable.js | 2 +- .../tenant/administration/GDAPInviteWizard.js | 144 ++++++++++++++++++ .../administration/ListGDAPRelationships.js | 36 ++++- 5 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 src/views/tenant/administration/GDAPInviteWizard.js diff --git a/src/_nav.js b/src/_nav.js index b6d536bb1976..6db81f2e2398 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -693,6 +693,11 @@ const _nav = [ name: 'GDAP Migration Status', to: '/tenant/administration/gdap-status', }, + { + component: CNavItem, + name: 'Invite Wizard', + to: '/tenant/administration/gdap-invite', + }, { component: CNavItem, name: 'GDAP Relationships', diff --git a/src/adminRoutes.js b/src/adminRoutes.js index 7fb235df0e0e..b57360064384 100644 --- a/src/adminRoutes.js +++ b/src/adminRoutes.js @@ -4,6 +4,7 @@ const Setup = React.lazy(() => import('src/views/cipp/Setup')) const ApplyStandard = React.lazy(() => import('src/views/tenant/standards/ApplyStandard')) const GDAPStatus = React.lazy(() => import('src/views/tenant/administration/ListGDAPQueue')) const GDAP = React.lazy(() => import('src/views/tenant/administration/GDAPWizard')) +const GDAPInvite = React.lazy(() => import('src/views/tenant/administration/GDAPInviteWizard')) const GDAPRoleWizard = React.lazy(() => import('src/views/tenant/administration/GDAPRoleWizard')) const GDAPRoles = React.lazy(() => import('src/views/tenant/administration/ListGDAPRoles')) const GDAPRelationships = React.lazy(() => @@ -17,6 +18,7 @@ const adminRoutes = [ { path: '/cipp/settings', name: 'Settings', component: CIPPSettings }, { path: '/cipp/setup', name: 'Setup', component: Setup }, { path: '/tenant/administration/gdap', name: 'GDAP Wizard', component: GDAP }, + { path: '/tenant/administration/gdap-invite', name: 'GDAP Invite Wizard', component: GDAPInvite }, { path: '/tenant/administration/gdap-role-wizard', name: 'GDAP Role Wizard', diff --git a/src/components/tables/CippTable.js b/src/components/tables/CippTable.js index c3cc3f0665f2..ee16566de436 100644 --- a/src/components/tables/CippTable.js +++ b/src/components/tables/CippTable.js @@ -344,7 +344,7 @@ export default function CippTable({ if (key.length > 1) { var property = obj for (var x = 0; x < key.length; x++) { - if (property[key[x]] !== null) { + if (property.hasOwnProperty(key[x]) && property[key[x]] !== null) { property = property[key[x]] } else { property = 'n/a' diff --git a/src/views/tenant/administration/GDAPInviteWizard.js b/src/views/tenant/administration/GDAPInviteWizard.js new file mode 100644 index 000000000000..9adb90081c62 --- /dev/null +++ b/src/views/tenant/administration/GDAPInviteWizard.js @@ -0,0 +1,144 @@ +import React from 'react' +import { CCol, CRow, CForm, CCallout, CSpinner, CButton } from '@coreui/react' +import { Field, FormSpy } from 'react-final-form' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' +import { CippWizard } from 'src/components/layout' +import { WizardTableField } from 'src/components/tables' +import { TitleButton } from 'src/components/buttons' +import PropTypes from 'prop-types' +import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' + +const Error = ({ name }) => ( + + touched && error ? ( + + + {error} + + ) : null + } + /> +) + +Error.propTypes = { + name: PropTypes.string.isRequired, +} + +const requiredArray = (value) => (value && value.length !== 0 ? undefined : 'Required') + +const GDAPInviteWizard = () => { + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() + + const handleSubmit = async (values) => { + genericPostRequest({ path: '/api/ExecGDAPInvite', values: values }) + } + + const formValues = {} + + return ( + + +
+

Step 1

+
+ Select which roles you want to add to GDAP relationship. +
+
+
+ + + CIPP will create a single relationship with all roles you've selected for the maximum + duration of 730 days using a GUID as a random name for the relationship. +
It is recommend to put CIPP user in the correct GDAP Role Groups to manage your + environment secure after deployment of GDAP. +
+
+ +
+ + {(props) => ( + row['RoleName'], + sortable: true, + exportselector: 'Name', + }, + { + name: 'Group', + selector: (row) => row['GroupName'], + sortable: true, + }, + ]} + fieldProps={props} + /> + )} + + +
+
+
+ +
+

Step 2

+
Confirm and apply
+
+
+ {!postResults.isSuccess && ( + + {(props) => { + return ( + <> + + + +
Roles and group names
+ + {props.values.gdapRoles.map((role, idx) => ( +
  • + {role.RoleName} - {role.GroupName} +
  • + ))} +
    +
    +
    + + ) + }} +
    + )} + {postResults.isFetching && ( + + Loading + + )} + {postResults.isSuccess && ( + + {postResults.data.Results.map((message, idx) => { + return
  • {message}
  • + })} +
    + )} +
    +
    +
    + ) +} + +export default GDAPInviteWizard diff --git a/src/views/tenant/administration/ListGDAPRelationships.js b/src/views/tenant/administration/ListGDAPRelationships.js index ae40d66ddd40..13c7b0e64e70 100644 --- a/src/views/tenant/administration/ListGDAPRelationships.js +++ b/src/views/tenant/administration/ListGDAPRelationships.js @@ -1,5 +1,10 @@ -import { CButton } from '@coreui/react' -import { faEllipsisV, faTrashAlt } from '@fortawesome/free-solid-svg-icons' +import { CSpinner, CButton } from '@coreui/react' +import { + faEllipsisV, + faTrashAlt, + faExclamationTriangle, + faCheck, +} from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import React, { useState } from 'react' import { useSelector } from 'react-redux' @@ -7,6 +12,30 @@ import { CippPageList } from 'src/components/layout' import { cellDateFormatter, cellNullTextFormatter } from 'src/components/tables' import { CippActionsOffcanvas } from 'src/components/utilities' import GDAPRoles from 'src/data/GDAPRoles' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' +import { ModalService } from 'src/components/utilities' + +const RefreshAction = () => { + const [execGdapInviteQueue, { isLoading, isSuccess, error }] = useLazyGenericGetRequestQuery() + + const showModal = () => + ModalService.confirm({ + body:
    Process recently approved GDAP relationships?
    , + onConfirm: () => + execGdapInviteQueue({ + path: 'api/ExecGDAPInviteApproved', + }), + }) + + return ( + + {isLoading && } + {error && } + {isSuccess && } + Map GDAP Groups + + ) +} const Actions = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) @@ -31,7 +60,7 @@ const Actions = (row, rowIndex, formatExtraData) => { { modalMessage: 'Are you sure you want to terminate these relationships?', }, ], + actions: [], }, keyField: 'id', columns, From cc8e6a0d5a765022634212fbcd4c8b6151182562 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 19 Jul 2023 20:45:03 -0400 Subject: [PATCH 2/2] Add invite URL to offcanvas --- .../administration/ListGDAPRelationships.js | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/views/tenant/administration/ListGDAPRelationships.js b/src/views/tenant/administration/ListGDAPRelationships.js index 13c7b0e64e70..8efe4f6795ee 100644 --- a/src/views/tenant/administration/ListGDAPRelationships.js +++ b/src/views/tenant/administration/ListGDAPRelationships.js @@ -14,6 +14,8 @@ import { CippActionsOffcanvas } from 'src/components/utilities' import GDAPRoles from 'src/data/GDAPRoles' import { useLazyGenericGetRequestQuery } from 'src/store/api/app' import { ModalService } from 'src/components/utilities' +import { constants } from 'buffer' +import Skeleton from 'react-loading-skeleton' const RefreshAction = () => { const [execGdapInviteQueue, { isLoading, isSuccess, error }] = useLazyGenericGetRequestQuery() @@ -39,10 +41,33 @@ const RefreshAction = () => { const Actions = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) + const [getGdapInvite, gdapInvite] = useLazyGenericGetRequestQuery() - const tenant = useSelector((state) => state.app.currentTenant) + function inviteProperty(gdapInvite, propertyName) { + return ( + <> + {gdapInvite.isFetching && } + {!gdapInvite.isFetching && + gdapInvite.isSuccess && + (gdapInvite.data[propertyName]?.toString() ?? ' ')} + + ) + } + function loadOffCanvasDetails(id) { + setOCVisible(true) + getGdapInvite({ + path: 'api/ListGDAPInvite', + params: { RelationshipId: id }, + }) + } var extendedInfo = [] + extendedInfo.push({ + label: 'Invite URL', + value: inviteProperty(gdapInvite, 'InviteUrl'), + }) + const tenant = useSelector((state) => state.app.currentTenant) + row?.accessDetails.unifiedRoles.map((role) => { for (var x = 0; x < GDAPRoles.length; x++) { if (GDAPRoles[x].ObjectId == role.roleDefinitionId) { @@ -54,9 +79,10 @@ const Actions = (row, rowIndex, formatExtraData) => { } } }) + return ( <> - setOCVisible(true)}> + loadOffCanvasDetails(row.id)}>