Skip to content

Commit 33966f4

Browse files
authored
Merge pull request #2758 from JohnDuprey/dev
Message Viewer, CIPP-SAM Roles, and misc fixes
2 parents f79e0c8 + 06ba66b commit 33966f4

File tree

5 files changed

+166
-49
lines changed

5 files changed

+166
-49
lines changed

src/views/cipp/app-settings/SettingsSuperAdmin.jsx

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CippCallout } from 'src/components/layout/index.js'
77
import CippAccordionItem from 'src/components/contentcards/CippAccordionItem'
88
import SettingsCustomRoles from 'src/views/cipp/app-settings/components/SettingsCustomRoles'
99
import CippButtonCard from 'src/components/contentcards/CippButtonCard'
10+
import SettingsSAMRoles from './components/SettingsSAMRoles'
1011

1112
export function SettingsSuperAdmin() {
1213
const partnerConfig = useGenericGetRequestQuery({
@@ -65,46 +66,11 @@ export function SettingsSuperAdmin() {
6566
</p>
6667
</CCol>
6768
</CRow>
68-
<CRow>
69-
<CCol sm={12} md={12} className="mb-3">
70-
<p className="fw-lighter">Tenant Mode</p>
71-
<Form
72-
onSubmit={onSubmit}
73-
initialValues={partnerConfig.data}
74-
render={({ handleSubmit }) => (
75-
<>
76-
{partnerConfig.isFetching && <CSpinner size="sm" className="me-2" />}
77-
<CForm id="submitForm" onSubmit={handleSubmit}>
78-
<RFFCFormRadio
79-
name="TenantMode"
80-
label="Multi Tenant - GDAP Mode"
81-
value="default"
82-
/>
83-
<RFFCFormRadio
84-
name="TenantMode"
85-
label="Multi Tenant - Add Partner Tenant"
86-
value="PartnerTenantAvailable"
87-
/>
88-
<RFFCFormRadio
89-
name="TenantMode"
90-
label="Single Tenant - Own Tenant Mode"
91-
value="owntenant"
92-
/>
93-
</CForm>
94-
</>
95-
)}
96-
/>
97-
{webhookCreateResult.isSuccess && (
98-
<CippCallout color="info" dismissible>
99-
{webhookCreateResult?.data?.results}
100-
</CippCallout>
101-
)}
102-
</CCol>
103-
</CRow>
10469
</>
10570
</>
10671
</CippButtonCard>
10772
<SettingsCustomRoles />
73+
<SettingsSAMRoles />
10874
</>
10975
)
11076
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import React, { useEffect, useRef, useState } from 'react'
2+
import {
3+
CButton,
4+
CCallout,
5+
CCol,
6+
CForm,
7+
CRow,
8+
CAccordion,
9+
CAccordionHeader,
10+
CAccordionBody,
11+
CAccordionItem,
12+
} from '@coreui/react'
13+
import { Field, Form, FormSpy } from 'react-final-form'
14+
import { RFFCFormRadioList, RFFSelectSearch } from 'src/components/forms'
15+
import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app'
16+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
17+
import { TenantSelectorMultiple, ModalService, CippOffcanvas } from 'src/components/utilities'
18+
import PropTypes from 'prop-types'
19+
import { OnChange } from 'react-final-form-listeners'
20+
import { useListTenantsQuery } from 'src/store/api/tenants'
21+
import { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas'
22+
import CippButtonCard from 'src/components/contentcards/CippButtonCard'
23+
import GDAPRoles from 'src/data/GDAPRoles'
24+
25+
const SettingsSAMRoles = () => {
26+
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
27+
const [selectedTenant, setSelectedTenant] = useState([])
28+
const tenantSelectorRef = useRef()
29+
const {
30+
data: tenants = [],
31+
isFetching: tenantsFetching,
32+
isSuccess: tenantSuccess,
33+
} = useListTenantsQuery({
34+
showAllTenantSelector: true,
35+
})
36+
37+
const {
38+
data: cippSAMRoles = [],
39+
isFetching: roleListFetching,
40+
isSuccess: roleListSuccess,
41+
refetch: refetchRoleList,
42+
} = useGenericGetRequestQuery({
43+
path: 'api/ExecSAMRoles',
44+
})
45+
46+
const handleTenantChange = (e) => {
47+
setSelectedTenant(e)
48+
}
49+
50+
const handleSubmit = async (values) => {
51+
//filter on only objects that are 'true'
52+
genericPostRequest({
53+
path: '/api/ExecSAMRoles?Action=Update',
54+
values: {
55+
Roles: values.Roles,
56+
Tenants: selectedTenant.map((tenant) => tenant.value),
57+
},
58+
}).then(() => {
59+
refetchRoleList()
60+
})
61+
}
62+
63+
useEffect(() => {
64+
if (roleListSuccess && cippSAMRoles.Tenants.length > 0) {
65+
var selectedTenants = []
66+
tenants.map((tenant) => {
67+
if (cippSAMRoles.Tenants.includes(tenant.customerId)) {
68+
selectedTenants.push({ label: tenant.displayName, value: tenant.customerId })
69+
}
70+
})
71+
tenantSelectorRef.current.setValue(selectedTenants)
72+
}
73+
}, [cippSAMRoles, roleListSuccess, tenantSuccess, tenantSelectorRef, tenants])
74+
75+
return (
76+
<CippButtonCard title="CIPP-SAM Roles" titleType="big" isFetching={roleListFetching}>
77+
<>
78+
<p className="me-1">
79+
Add your CIPP-SAM application Service Principal directly to Admin Roles in the tenant.
80+
This is an advanced use case where you need access to additional Graph endpoints or
81+
Exchange Cmdlets otherwise unavailable via Delegated permissions.
82+
</p>
83+
<p className="small">
84+
<FontAwesomeIcon icon="triangle-exclamation" className="me-2" /> This functionality is in
85+
beta and should be treated as such. Roles are added during the Update Permissions process
86+
or a CPV refresh.
87+
</p>
88+
89+
{roleListSuccess && (
90+
<Form
91+
onSubmit={handleSubmit}
92+
initialValues={cippSAMRoles}
93+
render={({ handleSubmit, submitting, values }) => {
94+
return (
95+
<CForm onSubmit={handleSubmit}>
96+
<CRow className="mb-3">
97+
<CCol xl={8} md={12} className="mb-3">
98+
<div className="mb-3">
99+
<RFFSelectSearch
100+
name="Roles"
101+
label="Admin Roles"
102+
values={GDAPRoles.map((role) => ({
103+
name: role.Name,
104+
value: role.ObjectId,
105+
}))}
106+
multi={true}
107+
refreshFunction={() => refetchRoleList()}
108+
placeholder="Select admin roles"
109+
/>
110+
</div>
111+
<div className="mb-3">
112+
<h5>Selected Tenants</h5>
113+
<TenantSelectorMultiple
114+
ref={tenantSelectorRef}
115+
values={selectedTenant}
116+
AllTenants={true}
117+
valueIsDomain={true}
118+
onChange={(e) => handleTenantChange(e)}
119+
/>
120+
</div>
121+
</CCol>
122+
</CRow>
123+
<CRow className="me-3">
124+
{postResults.isSuccess && (
125+
<CCallout color="success">{postResults.data.Results}</CCallout>
126+
)}
127+
<CRow className="mb-3">
128+
<CCol xl={4} md={12}>
129+
<CButton className="me-2" type="submit" disabled={submitting}>
130+
<FontAwesomeIcon
131+
icon={postResults.isFetching ? 'circle-notch' : 'save'}
132+
spin={postResults.isFetching}
133+
className="me-2"
134+
/>
135+
Save
136+
</CButton>
137+
</CCol>
138+
</CRow>
139+
</CRow>
140+
</CForm>
141+
)
142+
}}
143+
/>
144+
)}
145+
</>
146+
</CippButtonCard>
147+
)
148+
}
149+
150+
export default SettingsSAMRoles

src/views/identity/administration/Users.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ const Users = (row) => {
589589
label: 'Revoke sessions',
590590
color: 'info',
591591
modal: true,
592-
modalUrl: `/api/ExecRevokeSessions?Enable=true&TenantFilter=!Tenant&ID=!userPrincipalName`,
592+
modalUrl: `/api/ExecRevokeSessions?Enable=true&TenantFilter=!Tenant&ID=!id&Username=!userPrincipalName`,
593593
modalMessage: 'Are you sure you want to revoke all sessions for these users?',
594594
},
595595
{

src/views/tenant/administration/SecureScore.jsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGeneric
2828
import { CippCallout } from 'src/components/layout'
2929
import CippPrettyCard from 'src/components/contentcards/CippPrettyCard'
3030
import { TableModalButton } from 'src/components/buttons'
31+
import DOMPurify from 'dompurify'
32+
import ReactHtmlParser from 'react-html-parser'
3133

3234
const SecureScore = () => {
3335
const textRef = useRef()
@@ -66,6 +68,12 @@ const SecureScore = () => {
6668
},
6769
})
6870

71+
const sanitizeHtml = (html) => {
72+
var sanitizedHtml = DOMPurify.sanitize(html)
73+
var parsedHtml = ReactHtmlParser(sanitizedHtml)
74+
return parsedHtml
75+
}
76+
6977
useEffect(() => {
7078
if (isSuccess) {
7179
setTranslatedData(securescore.Results[0])
@@ -341,23 +349,16 @@ const SecureScore = () => {
341349
<CCardText>
342350
<h5>Description</h5>
343351
<small className="text-medium-emphasis">
344-
<div
345-
dangerouslySetInnerHTML={{
346-
__html: `${info.description} ${info.implementationStatus}`,
347-
}}
348-
/>
352+
<div>
353+
{sanitizeHtml(`${info.description} ${info.implementationStatus}`)}
354+
</div>
349355
</small>
350356
</CCardText>
351357
{info.scoreInPercentage !== 100 && (
352358
<CCardText>
353359
<h5>Remediation Recommendation</h5>
354360
<small className="mb-3 text-medium-emphasis">
355-
{
356-
<div
357-
className="mb-3"
358-
dangerouslySetInnerHTML={{ __html: info.remediation }}
359-
/>
360-
}
361+
{<div className="mb-3">{sanitizeHtml(info.remediation)}</div>}
361362
</small>
362363
</CCardText>
363364
)}

staticwebapp.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
}
104104
},
105105
"globalHeaders": {
106-
"content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *"
106+
"content-security-policy": "default-src https: blob: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *"
107107
},
108108
"mimeTypes": {
109109
".json": "text/json"

0 commit comments

Comments
 (0)