Skip to content

Commit

Permalink
Merge pull request KelvinTegelaar#8 from KelvinTegelaar/dev
Browse files Browse the repository at this point in the history
[pull] dev from KelvinTegelaar:dev
  • Loading branch information
BNWEIN authored Jul 20, 2023
2 parents c53a8bd + e77009b commit eaf09af
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 207 deletions.
5 changes: 5 additions & 0 deletions src/_nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 2 additions & 0 deletions src/adminRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(() =>
Expand All @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion src/components/tables/CippTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
66 changes: 66 additions & 0 deletions src/data/portals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[
{
"label": "M365 Portal",
"name": "M365_Portal",
"url": "https://portal.office.com/Partner/BeginClientSession.aspx?CTID=customerId&CSDEST=o365admincenter",
"variable": "customerId",
"target": "_blank",
"external": true
},
{
"label": "Exchange Portal",
"name": "Exchange_Portal",
"url": "https://admin.exchange.microsoft.com/?landingpage=homepage&form=mac_sidebar&delegatedOrg=defaultDomainName#",
"variable": "defaultDomainName",
"target": "_blank",
"external": true
},
{
"label": "Entra Portal",
"name": "Entra_Portal",
"url": "https://entra.microsoft.com/defaultDomainName",
"variable": "defaultDomainName",
"target": "_blank",
"external": true
},
{
"label": "Teams Portal",
"name": "Teams_Portal",
"url": "https://admin.teams.microsoft.com/?delegatedOrg=defaultDomainName",
"variable": "defaultDomainName",
"target": "_blank",
"external": true
},
{
"label": "Azure Portal",
"name": "Azure_Portal",
"url": "https://portal.azure.com/defaultDomainName",
"variable": "defaultDomainName",
"target": "_blank",
"external": true
},
{
"label": "Intune Portal",
"name": "Intune_Portal",
"url": "https://intune.microsoft.com/defaultDomainName",
"variable": "defaultDomainName",
"target": "_blank",
"external": true
},
{
"label": "Security Portal",
"name": "Security_Portal",
"url": "https://security.microsoft.com/?tid=customerId",
"variable": "customerId",
"target": "_blank",
"external": true
},
{
"label": "Sharepoint Admin",
"name": "Sharepoint_Admin",
"url": "https://admin.microsoft.com/Partner/beginclientsession.aspx?CTID=customerId&CSDEST=SharePoint",
"variable": "customerId",
"target": "_blank",
"external": true
}
]
144 changes: 144 additions & 0 deletions src/views/tenant/administration/GDAPInviteWizard.js
Original file line number Diff line number Diff line change
@@ -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 }) => (
<Field
name={name}
subscription={{ touched: true, error: true }}
render={({ meta: { touched, error } }) =>
touched && error ? (
<CCallout color="danger">
<FontAwesomeIcon icon={faExclamationTriangle} color="danger" />
{error}
</CCallout>
) : 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 (
<CippWizard
initialValues={{ ...formValues }}
onSubmit={handleSubmit}
wizardTitle="GDAP Invite Wizard"
>
<CippWizard.Page
title="Select which roles you want to add to GDAP relationship invite"
description="Choose from the mapped GDAP Roles"
>
<center>
<h3 className="text-primary">Step 1</h3>
<h5 className="card-title mb-4">
Select which roles you want to add to GDAP relationship.
</h5>
</center>
<hr className="my-4" />
<CForm onSubmit={handleSubmit}>
<CCallout color="info">
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.
<br /> It is recommend to put CIPP user in the correct GDAP Role Groups to manage your
environment secure after deployment of GDAP.
</CCallout>
<div className="mb-2">
<TitleButton href="/tenant/administration/gdap-role-wizard" title="Map GDAP Roles" />
</div>
<Field name="gdapRoles" validate={requiredArray}>
{(props) => (
<WizardTableField
reportName="gdaproles"
keyField="defaultDomainName"
path="/api/ListGDAPRoles"
columns={[
{
name: 'Name',
selector: (row) => row['RoleName'],
sortable: true,
exportselector: 'Name',
},
{
name: 'Group',
selector: (row) => row['GroupName'],
sortable: true,
},
]}
fieldProps={props}
/>
)}
</Field>
<Error name="gdapRoles" />
</CForm>
<hr className="my-4" />
</CippWizard.Page>
<CippWizard.Page title="Review and Confirm" description="Confirm the settings to apply">
<center>
<h3 className="text-primary">Step 2</h3>
<h5 className="card-title mb-4">Confirm and apply</h5>
</center>
<hr className="my-4" />
{!postResults.isSuccess && (
<FormSpy>
{(props) => {
return (
<>
<CRow>
<CCol md={3}></CCol>
<CCol md={6}>
<h5 className="mb-0">Roles and group names</h5>
<CCallout color="info">
{props.values.gdapRoles.map((role, idx) => (
<li key={idx}>
{role.RoleName} - {role.GroupName}
</li>
))}
</CCallout>
</CCol>
</CRow>
</>
)
}}
</FormSpy>
)}
{postResults.isFetching && (
<CCallout color="info">
<CSpinner>Loading</CSpinner>
</CCallout>
)}
{postResults.isSuccess && (
<CCallout color="success">
{postResults.data.Results.map((message, idx) => {
return <li key={idx}>{message}</li>
})}
</CCallout>
)}
<hr className="my-4" />
</CippWizard.Page>
</CippWizard>
)
}

export default GDAPInviteWizard
66 changes: 61 additions & 5 deletions src/views/tenant/administration/ListGDAPRelationships.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,73 @@
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'
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'
import { constants } from 'buffer'
import Skeleton from 'react-loading-skeleton'

const RefreshAction = () => {
const [execGdapInviteQueue, { isLoading, isSuccess, error }] = useLazyGenericGetRequestQuery()

const showModal = () =>
ModalService.confirm({
body: <div>Process recently approved GDAP relationships?</div>,
onConfirm: () =>
execGdapInviteQueue({
path: 'api/ExecGDAPInviteApproved',
}),
})

return (
<CButton onClick={showModal} size="sm" className="m-1">
{isLoading && <CSpinner size="sm" />}
{error && <FontAwesomeIcon icon={faExclamationTriangle} className="pe-1" />}
{isSuccess && <FontAwesomeIcon icon={faCheck} className="pe-1" />}
Map GDAP Groups
</CButton>
)
}

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 && <Skeleton count={1} width={150} />}
{!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) {
Expand All @@ -25,13 +79,14 @@ const Actions = (row, rowIndex, formatExtraData) => {
}
}
})

return (
<>
<CButton size="sm" color="link" onClick={() => setOCVisible(true)}>
<CButton size="sm" color="link" onClick={() => loadOffCanvasDetails(row.id)}>
<FontAwesomeIcon icon={faEllipsisV} />
</CButton>
<CippActionsOffcanvas
title={'GDAP - ' + row?.customer.displayName}
title={'GDAP - ' + row?.customer?.displayName}
extendedInfo={extendedInfo}
actions={[
{
Expand Down Expand Up @@ -122,6 +177,7 @@ const GDAPRelationships = () => {
modalMessage: 'Are you sure you want to terminate these relationships?',
},
],
actions: [<RefreshAction key="refresh-action-button" />],
},
keyField: 'id',
columns,
Expand Down
Loading

0 comments on commit eaf09af

Please sign in to comment.