Skip to content

Commit eaf09af

Browse files
authored
Merge pull request #8 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents c53a8bd + e77009b commit eaf09af

File tree

7 files changed

+306
-207
lines changed

7 files changed

+306
-207
lines changed

src/_nav.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,11 @@ const _nav = [
693693
name: 'GDAP Migration Status',
694694
to: '/tenant/administration/gdap-status',
695695
},
696+
{
697+
component: CNavItem,
698+
name: 'Invite Wizard',
699+
to: '/tenant/administration/gdap-invite',
700+
},
696701
{
697702
component: CNavItem,
698703
name: 'GDAP Relationships',

src/adminRoutes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const Setup = React.lazy(() => import('src/views/cipp/Setup'))
44
const ApplyStandard = React.lazy(() => import('src/views/tenant/standards/ApplyStandard'))
55
const GDAPStatus = React.lazy(() => import('src/views/tenant/administration/ListGDAPQueue'))
66
const GDAP = React.lazy(() => import('src/views/tenant/administration/GDAPWizard'))
7+
const GDAPInvite = React.lazy(() => import('src/views/tenant/administration/GDAPInviteWizard'))
78
const GDAPRoleWizard = React.lazy(() => import('src/views/tenant/administration/GDAPRoleWizard'))
89
const GDAPRoles = React.lazy(() => import('src/views/tenant/administration/ListGDAPRoles'))
910
const GDAPRelationships = React.lazy(() =>
@@ -17,6 +18,7 @@ const adminRoutes = [
1718
{ path: '/cipp/settings', name: 'Settings', component: CIPPSettings },
1819
{ path: '/cipp/setup', name: 'Setup', component: Setup },
1920
{ path: '/tenant/administration/gdap', name: 'GDAP Wizard', component: GDAP },
21+
{ path: '/tenant/administration/gdap-invite', name: 'GDAP Invite Wizard', component: GDAPInvite },
2022
{
2123
path: '/tenant/administration/gdap-role-wizard',
2224
name: 'GDAP Role Wizard',

src/components/tables/CippTable.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ export default function CippTable({
344344
if (key.length > 1) {
345345
var property = obj
346346
for (var x = 0; x < key.length; x++) {
347-
if (property[key[x]] !== null) {
347+
if (property.hasOwnProperty(key[x]) && property[key[x]] !== null) {
348348
property = property[key[x]]
349349
} else {
350350
property = 'n/a'

src/data/portals.json

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[
2+
{
3+
"label": "M365 Portal",
4+
"name": "M365_Portal",
5+
"url": "https://portal.office.com/Partner/BeginClientSession.aspx?CTID=customerId&CSDEST=o365admincenter",
6+
"variable": "customerId",
7+
"target": "_blank",
8+
"external": true
9+
},
10+
{
11+
"label": "Exchange Portal",
12+
"name": "Exchange_Portal",
13+
"url": "https://admin.exchange.microsoft.com/?landingpage=homepage&form=mac_sidebar&delegatedOrg=defaultDomainName#",
14+
"variable": "defaultDomainName",
15+
"target": "_blank",
16+
"external": true
17+
},
18+
{
19+
"label": "Entra Portal",
20+
"name": "Entra_Portal",
21+
"url": "https://entra.microsoft.com/defaultDomainName",
22+
"variable": "defaultDomainName",
23+
"target": "_blank",
24+
"external": true
25+
},
26+
{
27+
"label": "Teams Portal",
28+
"name": "Teams_Portal",
29+
"url": "https://admin.teams.microsoft.com/?delegatedOrg=defaultDomainName",
30+
"variable": "defaultDomainName",
31+
"target": "_blank",
32+
"external": true
33+
},
34+
{
35+
"label": "Azure Portal",
36+
"name": "Azure_Portal",
37+
"url": "https://portal.azure.com/defaultDomainName",
38+
"variable": "defaultDomainName",
39+
"target": "_blank",
40+
"external": true
41+
},
42+
{
43+
"label": "Intune Portal",
44+
"name": "Intune_Portal",
45+
"url": "https://intune.microsoft.com/defaultDomainName",
46+
"variable": "defaultDomainName",
47+
"target": "_blank",
48+
"external": true
49+
},
50+
{
51+
"label": "Security Portal",
52+
"name": "Security_Portal",
53+
"url": "https://security.microsoft.com/?tid=customerId",
54+
"variable": "customerId",
55+
"target": "_blank",
56+
"external": true
57+
},
58+
{
59+
"label": "Sharepoint Admin",
60+
"name": "Sharepoint_Admin",
61+
"url": "https://admin.microsoft.com/Partner/beginclientsession.aspx?CTID=customerId&CSDEST=SharePoint",
62+
"variable": "customerId",
63+
"target": "_blank",
64+
"external": true
65+
}
66+
]
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import React from 'react'
2+
import { CCol, CRow, CForm, CCallout, CSpinner, CButton } from '@coreui/react'
3+
import { Field, FormSpy } from 'react-final-form'
4+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
5+
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
6+
import { CippWizard } from 'src/components/layout'
7+
import { WizardTableField } from 'src/components/tables'
8+
import { TitleButton } from 'src/components/buttons'
9+
import PropTypes from 'prop-types'
10+
import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app'
11+
12+
const Error = ({ name }) => (
13+
<Field
14+
name={name}
15+
subscription={{ touched: true, error: true }}
16+
render={({ meta: { touched, error } }) =>
17+
touched && error ? (
18+
<CCallout color="danger">
19+
<FontAwesomeIcon icon={faExclamationTriangle} color="danger" />
20+
{error}
21+
</CCallout>
22+
) : null
23+
}
24+
/>
25+
)
26+
27+
Error.propTypes = {
28+
name: PropTypes.string.isRequired,
29+
}
30+
31+
const requiredArray = (value) => (value && value.length !== 0 ? undefined : 'Required')
32+
33+
const GDAPInviteWizard = () => {
34+
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
35+
const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery()
36+
37+
const handleSubmit = async (values) => {
38+
genericPostRequest({ path: '/api/ExecGDAPInvite', values: values })
39+
}
40+
41+
const formValues = {}
42+
43+
return (
44+
<CippWizard
45+
initialValues={{ ...formValues }}
46+
onSubmit={handleSubmit}
47+
wizardTitle="GDAP Invite Wizard"
48+
>
49+
<CippWizard.Page
50+
title="Select which roles you want to add to GDAP relationship invite"
51+
description="Choose from the mapped GDAP Roles"
52+
>
53+
<center>
54+
<h3 className="text-primary">Step 1</h3>
55+
<h5 className="card-title mb-4">
56+
Select which roles you want to add to GDAP relationship.
57+
</h5>
58+
</center>
59+
<hr className="my-4" />
60+
<CForm onSubmit={handleSubmit}>
61+
<CCallout color="info">
62+
CIPP will create a single relationship with all roles you've selected for the maximum
63+
duration of 730 days using a GUID as a random name for the relationship.
64+
<br /> It is recommend to put CIPP user in the correct GDAP Role Groups to manage your
65+
environment secure after deployment of GDAP.
66+
</CCallout>
67+
<div className="mb-2">
68+
<TitleButton href="/tenant/administration/gdap-role-wizard" title="Map GDAP Roles" />
69+
</div>
70+
<Field name="gdapRoles" validate={requiredArray}>
71+
{(props) => (
72+
<WizardTableField
73+
reportName="gdaproles"
74+
keyField="defaultDomainName"
75+
path="/api/ListGDAPRoles"
76+
columns={[
77+
{
78+
name: 'Name',
79+
selector: (row) => row['RoleName'],
80+
sortable: true,
81+
exportselector: 'Name',
82+
},
83+
{
84+
name: 'Group',
85+
selector: (row) => row['GroupName'],
86+
sortable: true,
87+
},
88+
]}
89+
fieldProps={props}
90+
/>
91+
)}
92+
</Field>
93+
<Error name="gdapRoles" />
94+
</CForm>
95+
<hr className="my-4" />
96+
</CippWizard.Page>
97+
<CippWizard.Page title="Review and Confirm" description="Confirm the settings to apply">
98+
<center>
99+
<h3 className="text-primary">Step 2</h3>
100+
<h5 className="card-title mb-4">Confirm and apply</h5>
101+
</center>
102+
<hr className="my-4" />
103+
{!postResults.isSuccess && (
104+
<FormSpy>
105+
{(props) => {
106+
return (
107+
<>
108+
<CRow>
109+
<CCol md={3}></CCol>
110+
<CCol md={6}>
111+
<h5 className="mb-0">Roles and group names</h5>
112+
<CCallout color="info">
113+
{props.values.gdapRoles.map((role, idx) => (
114+
<li key={idx}>
115+
{role.RoleName} - {role.GroupName}
116+
</li>
117+
))}
118+
</CCallout>
119+
</CCol>
120+
</CRow>
121+
</>
122+
)
123+
}}
124+
</FormSpy>
125+
)}
126+
{postResults.isFetching && (
127+
<CCallout color="info">
128+
<CSpinner>Loading</CSpinner>
129+
</CCallout>
130+
)}
131+
{postResults.isSuccess && (
132+
<CCallout color="success">
133+
{postResults.data.Results.map((message, idx) => {
134+
return <li key={idx}>{message}</li>
135+
})}
136+
</CCallout>
137+
)}
138+
<hr className="my-4" />
139+
</CippWizard.Page>
140+
</CippWizard>
141+
)
142+
}
143+
144+
export default GDAPInviteWizard

src/views/tenant/administration/ListGDAPRelationships.js

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,73 @@
1-
import { CButton } from '@coreui/react'
2-
import { faEllipsisV, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
1+
import { CSpinner, CButton } from '@coreui/react'
2+
import {
3+
faEllipsisV,
4+
faTrashAlt,
5+
faExclamationTriangle,
6+
faCheck,
7+
} from '@fortawesome/free-solid-svg-icons'
38
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
49
import React, { useState } from 'react'
510
import { useSelector } from 'react-redux'
611
import { CippPageList } from 'src/components/layout'
712
import { cellDateFormatter, cellNullTextFormatter } from 'src/components/tables'
813
import { CippActionsOffcanvas } from 'src/components/utilities'
914
import GDAPRoles from 'src/data/GDAPRoles'
15+
import { useLazyGenericGetRequestQuery } from 'src/store/api/app'
16+
import { ModalService } from 'src/components/utilities'
17+
import { constants } from 'buffer'
18+
import Skeleton from 'react-loading-skeleton'
19+
20+
const RefreshAction = () => {
21+
const [execGdapInviteQueue, { isLoading, isSuccess, error }] = useLazyGenericGetRequestQuery()
22+
23+
const showModal = () =>
24+
ModalService.confirm({
25+
body: <div>Process recently approved GDAP relationships?</div>,
26+
onConfirm: () =>
27+
execGdapInviteQueue({
28+
path: 'api/ExecGDAPInviteApproved',
29+
}),
30+
})
31+
32+
return (
33+
<CButton onClick={showModal} size="sm" className="m-1">
34+
{isLoading && <CSpinner size="sm" />}
35+
{error && <FontAwesomeIcon icon={faExclamationTriangle} className="pe-1" />}
36+
{isSuccess && <FontAwesomeIcon icon={faCheck} className="pe-1" />}
37+
Map GDAP Groups
38+
</CButton>
39+
)
40+
}
1041

1142
const Actions = (row, rowIndex, formatExtraData) => {
1243
const [ocVisible, setOCVisible] = useState(false)
44+
const [getGdapInvite, gdapInvite] = useLazyGenericGetRequestQuery()
1345

14-
const tenant = useSelector((state) => state.app.currentTenant)
46+
function inviteProperty(gdapInvite, propertyName) {
47+
return (
48+
<>
49+
{gdapInvite.isFetching && <Skeleton count={1} width={150} />}
50+
{!gdapInvite.isFetching &&
51+
gdapInvite.isSuccess &&
52+
(gdapInvite.data[propertyName]?.toString() ?? ' ')}
53+
</>
54+
)
55+
}
1556

57+
function loadOffCanvasDetails(id) {
58+
setOCVisible(true)
59+
getGdapInvite({
60+
path: 'api/ListGDAPInvite',
61+
params: { RelationshipId: id },
62+
})
63+
}
1664
var extendedInfo = []
65+
extendedInfo.push({
66+
label: 'Invite URL',
67+
value: inviteProperty(gdapInvite, 'InviteUrl'),
68+
})
69+
const tenant = useSelector((state) => state.app.currentTenant)
70+
1771
row?.accessDetails.unifiedRoles.map((role) => {
1872
for (var x = 0; x < GDAPRoles.length; x++) {
1973
if (GDAPRoles[x].ObjectId == role.roleDefinitionId) {
@@ -25,13 +79,14 @@ const Actions = (row, rowIndex, formatExtraData) => {
2579
}
2680
}
2781
})
82+
2883
return (
2984
<>
30-
<CButton size="sm" color="link" onClick={() => setOCVisible(true)}>
85+
<CButton size="sm" color="link" onClick={() => loadOffCanvasDetails(row.id)}>
3186
<FontAwesomeIcon icon={faEllipsisV} />
3287
</CButton>
3388
<CippActionsOffcanvas
34-
title={'GDAP - ' + row?.customer.displayName}
89+
title={'GDAP - ' + row?.customer?.displayName}
3590
extendedInfo={extendedInfo}
3691
actions={[
3792
{
@@ -122,6 +177,7 @@ const GDAPRelationships = () => {
122177
modalMessage: 'Are you sure you want to terminate these relationships?',
123178
},
124179
],
180+
actions: [<RefreshAction key="refresh-action-button" />],
125181
},
126182
keyField: 'id',
127183
columns,

0 commit comments

Comments
 (0)