forked from KelvinTegelaar/CIPP
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'KelvinTegelaar:main' into main
Showing
61 changed files
with
3,391 additions
and
870 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Write-Host "Starting CIPP Dev Emulators" | ||
$Path = (Get-Item $PSScriptRoot).Parent.Parent.FullName | ||
wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c azurite`; new-tab --title 'FunctionApp' -d $Path\CIPP-API pwsh -c func start`; new-tab --title 'CIPP Frontend' -d $Path\CIPP pwsh -c npm run start`; new-tab --title 'SWA' -d $Path\CIPP pwsh -c npm run start-swa | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[ | ||
{ | ||
"value": "disabled", | ||
"label": "Disabled" | ||
}, | ||
{ | ||
"value": "enabled", | ||
"label": "Enabled" | ||
}, | ||
{ | ||
"value": "enforced", | ||
"label": "Enforced" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
5.7.0 | ||
5.9.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
import React, { useState } from 'react' | ||
import PropTypes from 'prop-types' | ||
import { | ||
CButton, | ||
CCallout, | ||
CCard, | ||
CCardBody, | ||
CCardHeader, | ||
CCol, | ||
CForm, | ||
CRow, | ||
CSpinner, | ||
CTooltip, | ||
} from '@coreui/react' | ||
import { CippOffcanvas, TenantSelector } from '.' | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import { Field, Form, FormSpy } from 'react-final-form' | ||
import arrayMutators from 'final-form-arrays' | ||
import { | ||
RFFCFormInput, | ||
RFFCFormInputArray, | ||
RFFCFormSwitch, | ||
RFFSelectSearch, | ||
} from 'src/components/forms' | ||
import { useSelector } from 'react-redux' | ||
import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' | ||
import DatePicker from 'react-datepicker' | ||
import 'react-datepicker/dist/react-datepicker.css' | ||
|
||
export default function CippScheduleOffcanvas({ | ||
state: visible, | ||
hideFunction, | ||
title, | ||
placement, | ||
...props | ||
}) { | ||
const currentDate = new Date() | ||
const [startDate, setStartDate] = useState(currentDate) | ||
const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) | ||
const [refreshState, setRefreshState] = useState(false) | ||
const taskName = `Scheduled Task ${currentDate.toLocaleString()}` | ||
const { data: availableCommands = [], isLoading: isLoadingcmd } = useGenericGetRequestQuery({ | ||
path: 'api/ListFunctionParameters?Module=CIPPCore', | ||
}) | ||
|
||
const recurrenceOptions = [ | ||
{ value: '0', name: 'Only once' }, | ||
{ value: '1', name: 'Every 1 day' }, | ||
{ value: '7', name: 'Every 7 days' }, | ||
{ value: '30', name: 'Every 30 days' }, | ||
{ value: '365', name: 'Every 365 days' }, | ||
] | ||
|
||
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() | ||
const onSubmit = (values) => { | ||
const unixTime = Math.floor(startDate.getTime() / 1000) | ||
const shippedValues = { | ||
TenantFilter: tenantDomain, | ||
Name: values.taskName, | ||
Command: values.command, | ||
Parameters: values.parameters, | ||
ScheduledTime: unixTime, | ||
Recurrence: values.Recurrence, | ||
AdditionalProperties: values.additional, | ||
PostExecution: { | ||
Webhook: values.webhook, | ||
Email: values.email, | ||
PSA: values.psa, | ||
}, | ||
} | ||
genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => { | ||
setRefreshState(res.requestId) | ||
if (props.submitFunction) { | ||
props.submitFunction() | ||
} | ||
}) | ||
} | ||
|
||
return ( | ||
<CippOffcanvas | ||
placement={placement} | ||
title={title} | ||
visible={visible} | ||
hideFunction={hideFunction} | ||
> | ||
<CCard> | ||
<CCardHeader></CCardHeader> | ||
<CCardBody> | ||
<Form | ||
onSubmit={onSubmit} | ||
mutators={{ | ||
...arrayMutators, | ||
}} | ||
initialValues={{ ...props.initialValues }} | ||
render={({ handleSubmit, submitting, values }) => { | ||
return ( | ||
<CForm onSubmit={handleSubmit}> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<label>Tenant</label> | ||
<Field name="tenantFilter">{(props) => <TenantSelector />}</Field> | ||
</CCol> | ||
</CRow> | ||
<CRow> | ||
<CCol> | ||
<RFFCFormInput | ||
type="text" | ||
name="taskName" | ||
label="Task Name" | ||
firstValue={`Task ${currentDate.toLocaleString()}`} | ||
/> | ||
</CCol> | ||
</CRow> | ||
<CRow> | ||
<CCol> | ||
<label>Scheduled Date</label> | ||
<DatePicker | ||
className="form-control mb-3" | ||
selected={startDate} | ||
showTimeSelect | ||
timeFormat="HH:mm" | ||
timeIntervals={15} | ||
dateFormat="Pp" | ||
onChange={(date) => setStartDate(date)} | ||
/> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFSelectSearch | ||
values={recurrenceOptions} | ||
name="Recurrence" | ||
placeholder="Select a recurrence" | ||
label="Recurrence" | ||
/> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFSelectSearch | ||
values={availableCommands.map((cmd) => ({ | ||
value: cmd.Function, | ||
name: cmd.Function, | ||
}))} | ||
name="command" | ||
placeholder={ | ||
isLoadingcmd ? ( | ||
<CSpinner size="sm" /> | ||
) : ( | ||
'Select a command or report to execute.' | ||
) | ||
} | ||
label="Command to execute" | ||
/> | ||
</CCol> | ||
</CRow> | ||
<FormSpy> | ||
{/* eslint-disable react/prop-types */} | ||
{(props) => { | ||
const selectedCommand = availableCommands.find( | ||
(cmd) => cmd.Function === props.values.command?.value, | ||
) | ||
return ( | ||
<CRow className="mb-3"> | ||
<CCol>{selectedCommand?.Synopsis}</CCol> | ||
</CRow> | ||
) | ||
}} | ||
</FormSpy> | ||
<CRow> | ||
<FormSpy> | ||
{/* eslint-disable react/prop-types */} | ||
{(props) => { | ||
const selectedCommand = availableCommands.find( | ||
(cmd) => cmd.Function === props.values.command?.value, | ||
) | ||
let paramblock = null | ||
if (selectedCommand) { | ||
//if the command parameter type is boolean we use <RFFCFormCheck /> else <RFFCFormInput />. | ||
const parameters = selectedCommand.Parameters | ||
if (parameters.length > 0) { | ||
paramblock = parameters.map((param, idx) => ( | ||
<CRow key={idx} className="mb-3"> | ||
<CTooltip | ||
content={ | ||
param?.Description !== null | ||
? param.Description | ||
: 'No Description' | ||
} | ||
placement="left" | ||
> | ||
<CCol> | ||
{param.Type === 'System.Boolean' || | ||
param.Type === | ||
'System.Management.Automation.SwitchParameter' ? ( | ||
<> | ||
<label>{param.Name}</label> | ||
<RFFCFormSwitch | ||
initialValue={false} | ||
name={`parameters.${param.Name}`} | ||
label={`True`} | ||
/> | ||
</> | ||
) : ( | ||
<> | ||
{param.Type === 'System.Collections.Hashtable' ? ( | ||
<RFFCFormInputArray | ||
name={`parameters.${param.Name}`} | ||
label={`${param.Name}`} | ||
key={idx} | ||
/> | ||
) : ( | ||
<RFFCFormInput | ||
type="text" | ||
key={idx} | ||
name={`parameters.${param.Name}`} | ||
label={`${param.Name}`} | ||
/> | ||
)} | ||
</> | ||
)} | ||
</CCol> | ||
</CTooltip> | ||
</CRow> | ||
)) | ||
} | ||
} | ||
return paramblock | ||
}} | ||
</FormSpy> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFCFormInputArray name={`additional`} label="Additional Properties" /> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<label>Send results to</label> | ||
<RFFCFormSwitch name="webhook" label="Webhook" /> | ||
<RFFCFormSwitch name="email" label="E-mail" /> | ||
<RFFCFormSwitch name="psa" label="PSA" /> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol md={6}> | ||
<CButton type="submit" disabled={submitting}> | ||
Add Schedule | ||
{postResults.isFetching && ( | ||
<FontAwesomeIcon icon="circle-notch" spin className="ms-2" size="1x" /> | ||
)} | ||
</CButton> | ||
</CCol> | ||
</CRow> | ||
{postResults.isSuccess && ( | ||
<CCallout color="success"> | ||
<li>{postResults.data.Results}</li> | ||
</CCallout> | ||
)} | ||
</CForm> | ||
) | ||
}} | ||
/> | ||
</CCardBody> | ||
</CCard> | ||
</CippOffcanvas> | ||
) | ||
} | ||
|
||
CippScheduleOffcanvas.propTypes = { | ||
groups: PropTypes.array, | ||
placement: PropTypes.string.isRequired, | ||
title: PropTypes.string.isRequired, | ||
state: PropTypes.bool, | ||
hideFunction: PropTypes.func.isRequired, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import React, { useRef, useState } from 'react' | ||
import { | ||
CButton, | ||
CCardText, | ||
CCol, | ||
CForm, | ||
CNav, | ||
CNavItem, | ||
CRow, | ||
CTabContent, | ||
CTabPane, | ||
} from '@coreui/react' | ||
import { CippCallout, CippPage } from 'src/components/layout' | ||
import { CippLazy } from 'src/components/utilities' | ||
import { useNavigate } from 'react-router-dom' | ||
import useQuery from 'src/hooks/useQuery.jsx' | ||
import Extensions from 'src/data/Extensions.json' | ||
import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' | ||
import CippButtonCard from 'src/components/contentcards/CippButtonCard.jsx' | ||
import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms/RFFComponents.jsx' | ||
import { Form } from 'react-final-form' | ||
import { SettingsExtensionMappings } from './app-settings/SettingsExtensionMappings' | ||
|
||
export default function CIPPExtensions() { | ||
const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() | ||
const inputRef = useRef(null) | ||
const [setExtensionconfig, extensionConfigResult] = useLazyGenericPostRequestQuery() | ||
const [execTestExtension, listExtensionTestResult] = useLazyGenericGetRequestQuery() | ||
const [execSyncExtension, listSyncExtensionResult] = useLazyGenericGetRequestQuery() | ||
|
||
const onSubmitTest = (integrationName) => { | ||
execTestExtension({ | ||
path: 'api/ExecExtensionTest?extensionName=' + integrationName, | ||
}) | ||
} | ||
const onSubmit = (values) => { | ||
setExtensionconfig({ | ||
path: 'api/ExecExtensionsConfig', | ||
values: values, | ||
}) | ||
} | ||
|
||
const ButtonGenerate = (integrationType, forceSync) => ( | ||
<> | ||
<CButton className="me-2" form={integrationType} type="submit"> | ||
{extensionConfigResult.isFetching && ( | ||
<FontAwesomeIcon icon={faCircleNotch} spin className="me-2" size="1x" /> | ||
)} | ||
Set Extension Settings | ||
</CButton> | ||
<CButton onClick={() => onSubmitTest(integrationType)} className="me-2"> | ||
{listExtensionTestResult.isFetching && ( | ||
<FontAwesomeIcon icon={faCircleNotch} spin className="me-2" size="1x" /> | ||
)} | ||
Test Extension | ||
</CButton> | ||
{forceSync && ( | ||
<CButton | ||
onClick={() => | ||
execSyncExtension({ | ||
path: 'api/ExecExtensionSync?Extension=' + integrationType, | ||
}) | ||
} | ||
className="me-2" | ||
> | ||
{listSyncExtensionResult.isFetching && ( | ||
<FontAwesomeIcon icon={faCircleNotch} spin className="me-2" size="1x" /> | ||
)} | ||
Force Sync | ||
</CButton> | ||
)} | ||
</> | ||
) | ||
const queryString = useQuery() | ||
const navigate = useNavigate() | ||
|
||
const tab = queryString.get('tab') | ||
const [active, setActiveTab] = useState(tab ? parseInt(tab) : 0) | ||
const setActive = (tab) => { | ||
setActiveTab(tab) | ||
queryString.set('tab', tab.toString()) | ||
navigate(`${location.pathname}?${queryString}`) | ||
} | ||
|
||
return ( | ||
<CippPage title="Settings" tenantSelector={false}> | ||
{listBackendResult.isUninitialized && listBackend({ path: 'api/ListExtensionsConfig' })} | ||
<CNav variant="tabs" role="tablist"> | ||
{Extensions.map((integration, idx) => ( | ||
<CNavItem | ||
key={`tab-${idx}`} | ||
active={active === idx} | ||
onClick={() => setActive(idx)} | ||
href="#" | ||
> | ||
{integration.name} | ||
</CNavItem> | ||
))} | ||
</CNav> | ||
<CTabContent> | ||
{Extensions.map((integration, idx) => ( | ||
<CTabPane key={`pane-${idx}`} visible={active === idx} className="mt-3"> | ||
<CippLazy visible={active === idx}> | ||
<CRow className="mb-3"> | ||
<CCol sm={12} md={integration.mappingRequired ? 4 : 12} className="mb-3"> | ||
<CippButtonCard | ||
title={integration.name} | ||
titleType="big" | ||
isFetching={listBackendResult.isFetching} | ||
CardButton={ButtonGenerate(integration.type, integration.forceSync)} | ||
key={idx} | ||
> | ||
<p>{integration.helpText}</p> | ||
<Form | ||
onSubmit={onSubmit} | ||
initialValues={listBackendResult.data} | ||
render={({ handleSubmit, submitting, values }) => { | ||
return ( | ||
<CForm id={integration.type} onSubmit={handleSubmit}> | ||
<CCardText> | ||
<CCol className="mb-3"> | ||
{integration.SettingOptions.map( | ||
(integrationOptions, idx) => | ||
integrationOptions.type === 'input' && ( | ||
<CCol key={`${idx}-${integrationOptions.name}`}> | ||
<RFFCFormInput | ||
type={integrationOptions.fieldtype} | ||
name={integrationOptions.name} | ||
label={integrationOptions.label} | ||
placeholder={integrationOptions.placeholder} | ||
/> | ||
</CCol> | ||
), | ||
)} | ||
{integration.SettingOptions.map( | ||
(integrationOptions, idx) => | ||
integrationOptions.type === 'checkbox' && ( | ||
<CCol key={`${integrationOptions.name}-${idx}`}> | ||
<RFFCFormSwitch | ||
name={integrationOptions.name} | ||
label={integrationOptions.label} | ||
value={false} | ||
/> | ||
</CCol> | ||
), | ||
)} | ||
<input | ||
ref={inputRef} | ||
type="hidden" | ||
name="type" | ||
value={integration.type} | ||
/> | ||
</CCol> | ||
</CCardText> | ||
</CForm> | ||
) | ||
}} | ||
/> | ||
{extensionConfigResult?.data?.Results && ( | ||
<CippCallout color={extensionConfigResult.isSuccess ? 'success' : 'danger'}> | ||
{extensionConfigResult?.data?.Results} | ||
</CippCallout> | ||
)} | ||
{listExtensionTestResult?.data?.Results && ( | ||
<CippCallout color={listExtensionTestResult.isSuccess ? 'success' : 'danger'}> | ||
{listExtensionTestResult?.data?.Results} | ||
{listExtensionTestResult?.data?.Link && ( | ||
<a | ||
href={listExtensionTestResult?.data?.Link} | ||
target="_blank" | ||
rel="noreferrer" | ||
className="ms-2" | ||
> | ||
Link | ||
</a> | ||
)} | ||
</CippCallout> | ||
)} | ||
</CippButtonCard> | ||
</CCol> | ||
<CCol sm={12} md={8}> | ||
<SettingsExtensionMappings type={integration.type} /> | ||
</CCol> | ||
</CRow> | ||
</CippLazy> | ||
</CTabPane> | ||
))} | ||
</CTabContent> | ||
</CippPage> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
619 changes: 319 additions & 300 deletions
619
src/views/cipp/app-settings/SettingsExtensionMappings.jsx
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
489 changes: 489 additions & 0 deletions
489
src/views/cipp/app-settings/components/SettingsCustomRoles.jsx
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
import React, { useState } from 'react' | ||
import { CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' | ||
import { useSelector } from 'react-redux' | ||
import { Field, Form, FormSpy } from 'react-final-form' | ||
import { | ||
Condition, | ||
RFFCFormInput, | ||
RFFCFormRadioList, | ||
RFFCFormSwitch, | ||
RFFSelectSearch, | ||
} from 'src/components/forms' | ||
import { | ||
useGenericGetRequestQuery, | ||
useLazyGenericGetRequestQuery, | ||
useLazyGenericPostRequestQuery, | ||
} from 'src/store/api/app' | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' | ||
import { CippContentCard, CippPage, CippPageList } from 'src/components/layout' | ||
import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' | ||
import 'react-datepicker/dist/react-datepicker.css' | ||
import { TenantSelector } from 'src/components/utilities' | ||
import arrayMutators from 'final-form-arrays' | ||
import DatePicker from 'react-datepicker' | ||
import 'react-datepicker/dist/react-datepicker.css' | ||
import { useListUsersQuery } from 'src/store/api/users' | ||
import GDAPRoles from 'src/data/GDAPRoles' | ||
import { CippDatatable, cellDateFormatter } from 'src/components/tables' | ||
|
||
const DeployJITAdmin = () => { | ||
const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() | ||
const currentDate = new Date() | ||
const [startDate, setStartDate] = useState(currentDate) | ||
const [endDate, setEndDate] = useState(currentDate) | ||
|
||
const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) | ||
const [refreshState, setRefreshState] = useState(false) | ||
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() | ||
|
||
const onSubmit = (values) => { | ||
const startTime = Math.floor(startDate.getTime() / 1000) | ||
const endTime = Math.floor(endDate.getTime() / 1000) | ||
const shippedValues = { | ||
TenantFilter: tenantDomain, | ||
UserId: values.UserId?.value, | ||
UserPrincipalName: values.UserPrincipalName, | ||
FirstName: values.FirstName, | ||
LastName: values.LastName, | ||
useraction: values.useraction, | ||
AdminRoles: values.AdminRoles?.map((role) => role.value), | ||
StartDate: startTime, | ||
UseTAP: values.useTap, | ||
EndDate: endTime, | ||
ExpireAction: values.expireAction.value, | ||
PostExecution: { | ||
Webhook: values.webhook, | ||
Email: values.email, | ||
PSA: values.psa, | ||
}, | ||
} | ||
genericPostRequest({ path: '/api/ExecJITAdmin', values: shippedValues }).then((res) => { | ||
setRefreshState(res.requestId) | ||
}) | ||
} | ||
|
||
const { | ||
data: users = [], | ||
isFetching: usersIsFetching, | ||
error: usersError, | ||
} = useGenericGetRequestQuery({ | ||
path: '/api/ListGraphRequest', | ||
params: { | ||
TenantFilter: tenantDomain, | ||
Endpoint: 'users', | ||
$select: 'id,displayName,userPrincipalName,accountEnabled', | ||
$count: true, | ||
$top: 999, | ||
$orderby: 'displayName', | ||
}, | ||
}) | ||
|
||
return ( | ||
<CippPage title={`Add JIT Admin`} tenantSelector={false}> | ||
<> | ||
<CRow className="mb-3"> | ||
<CCol lg={4} md={12}> | ||
<CippContentCard title="Add JIT Admin" icon={faEdit}> | ||
<Form | ||
onSubmit={onSubmit} | ||
mutators={{ | ||
...arrayMutators, | ||
}} | ||
render={({ handleSubmit, submitting, values }) => { | ||
return ( | ||
<CForm onSubmit={handleSubmit}> | ||
<p> | ||
JIT Admin creates an account that is usable for a specific period of time. | ||
Enter a username, select admin roles, date range and expiration action. | ||
</p> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<label className="mb-2">Tenant</label> | ||
<Field name="tenantFilter"> | ||
{(props) => <TenantSelector showAllTenantSelector={false} />} | ||
</Field> | ||
</CCol> | ||
</CRow> | ||
<CRow> | ||
<hr /> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFCFormRadioList | ||
name="useraction" | ||
options={[ | ||
{ value: 'create', label: 'New User' }, | ||
{ value: 'select', label: 'Existing User' }, | ||
]} | ||
validate={false} | ||
inline={true} | ||
className="" | ||
/> | ||
</CCol> | ||
</CRow> | ||
<Condition when="useraction" is="create"> | ||
<CRow> | ||
<CCol> | ||
<RFFCFormInput label="First Name" name="FirstName" /> | ||
</CCol> | ||
</CRow> | ||
<CRow> | ||
<CCol> | ||
<RFFCFormInput label="Last Name" name="LastName" /> | ||
</CCol> | ||
</CRow> | ||
<CRow> | ||
<CCol> | ||
<RFFCFormInput label="User Principal Name" name="UserPrincipalName" /> | ||
</CCol> | ||
</CRow> | ||
</Condition> | ||
<Condition when="useraction" is="select"> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFSelectSearch | ||
label={'Users in ' + tenantDomain} | ||
values={users?.Results?.map((user) => ({ | ||
value: user.id, | ||
name: `${user.displayName} <${user.userPrincipalName}>`, | ||
}))} | ||
placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} | ||
name="UserId" | ||
isLoading={usersIsFetching} | ||
/> | ||
<FormSpy subscription={{ values: true }}> | ||
{({ values }) => { | ||
return users?.Results?.map((user, key) => { | ||
if ( | ||
user.id === values?.UserId?.value && | ||
user.accountEnabled === false | ||
) { | ||
return ( | ||
<CCallout color="warning" key={key} className="mt-3"> | ||
This user is currently disabled, they will automatically be | ||
enabled when JIT is executed. | ||
</CCallout> | ||
) | ||
} | ||
}) | ||
}} | ||
</FormSpy> | ||
</CCol> | ||
</CRow> | ||
</Condition> | ||
<hr /> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFSelectSearch | ||
label="Administrative Roles" | ||
values={GDAPRoles?.map((role) => ({ | ||
value: role.ObjectId, | ||
name: role.Name, | ||
}))} | ||
multi={true} | ||
placeholder="Select Roles" | ||
name="AdminRoles" | ||
/> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<label>Scheduled Start Date</label> | ||
<DatePicker | ||
className="form-control" | ||
selected={startDate} | ||
showTimeSelect | ||
timeFormat="HH:mm" | ||
timeIntervals={15} | ||
dateFormat="Pp" | ||
onChange={(date) => setStartDate(date)} | ||
/> | ||
</CCol> | ||
<CCol> | ||
<label>Scheduled End Date</label> | ||
<DatePicker | ||
className="form-control" | ||
selected={endDate} | ||
showTimeSelect | ||
timeFormat="HH:mm" | ||
timeIntervals={15} | ||
dateFormat="Pp" | ||
onChange={(date) => setEndDate(date)} | ||
/> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<RFFSelectSearch | ||
label="Expiration Action" | ||
values={[ | ||
{ value: 'RemoveRoles', name: 'Remove Admin Roles' }, | ||
{ value: 'DisableUser', name: 'Disable User' }, | ||
{ value: 'DeleteUser', name: 'Delete User' }, | ||
]} | ||
placeholder="Select action for when JIT expires" | ||
name="expireAction" | ||
/> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<CTooltip content="Generate a Temporary Access Password for the JIT Admin account if enabled for the tenant. This applies to both New and Existing users. The start time coincides with the scheduled time."> | ||
<div> | ||
<RFFCFormSwitch name="useTap" label="Generate TAP" /> | ||
</div> | ||
</CTooltip> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol> | ||
<label>Send results to</label> | ||
<RFFCFormSwitch name="webhook" label="Webhook" /> | ||
<RFFCFormSwitch name="email" label="E-mail" /> | ||
<RFFCFormSwitch name="psa" label="PSA" /> | ||
</CCol> | ||
</CRow> | ||
<CRow className="mb-3"> | ||
<CCol md={6}> | ||
<CButton type="submit" disabled={submitting}> | ||
Add JIT Admin | ||
{postResults.isFetching && ( | ||
<FontAwesomeIcon | ||
icon={faCircleNotch} | ||
spin | ||
className="ms-2" | ||
size="1x" | ||
/> | ||
)} | ||
</CButton> | ||
</CCol> | ||
</CRow> | ||
{postResults.isSuccess && ( | ||
<CCallout color="success"> | ||
{postResults.data?.Results.map((result, idx) => ( | ||
<li key={idx}>{result}</li> | ||
))} | ||
</CCallout> | ||
)} | ||
{getResults.isFetching && ( | ||
<CCallout color="info"> | ||
<CSpinner>Loading</CSpinner> | ||
</CCallout> | ||
)} | ||
{getResults.isSuccess && ( | ||
<CCallout color="info">{getResults.data?.Results}</CCallout> | ||
)} | ||
{getResults.isError && ( | ||
<CCallout color="danger"> | ||
Could not connect to API: {getResults.error.message} | ||
</CCallout> | ||
)} | ||
</CForm> | ||
) | ||
}} | ||
/> | ||
</CippContentCard> | ||
</CCol> | ||
<CCol lg={8} md={12}> | ||
<CippContentCard title="JIT Admins" icon="user-shield"> | ||
<CippDatatable | ||
title="JIT Admins" | ||
path="/api/ExecJITAdmin?Action=List" | ||
params={{ TenantFilter: tenantDomain, refreshState }} | ||
columns={[ | ||
{ | ||
name: 'User', | ||
selector: (row) => row['userPrincipalName'], | ||
sortable: true, | ||
cell: cellGenericFormatter(), | ||
exportSelector: 'userPrincipalName', | ||
}, | ||
{ | ||
name: 'Account Enabled', | ||
selector: (row) => row['accountEnabled'], | ||
sortable: true, | ||
cell: cellGenericFormatter(), | ||
exportSelector: 'accountEnabled', | ||
}, | ||
{ | ||
name: 'JIT Enabled', | ||
selector: (row) => row['jitAdminEnabled'], | ||
sortable: true, | ||
cell: cellGenericFormatter(), | ||
exportSelector: 'jitAdminEnabled', | ||
}, | ||
{ | ||
name: 'JIT Expires', | ||
selector: (row) => row['jitAdminExpiration'], | ||
sortable: true, | ||
cell: cellDateFormatter({ format: 'short' }), | ||
exportSelector: 'jitAdminExpiration', | ||
}, | ||
{ | ||
name: 'Admin Roles', | ||
selector: (row) => row['memberOf'], | ||
sortable: false, | ||
cell: cellGenericFormatter(), | ||
exportSelector: 'memberOf', | ||
}, | ||
]} | ||
/> | ||
</CippContentCard> | ||
</CCol> | ||
</CRow> | ||
</> | ||
</CippPage> | ||
) | ||
} | ||
|
||
export default DeployJITAdmin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { useSelector } from 'react-redux' | ||
import { CippPageList } from 'src/components/layout' | ||
|
||
const columns = [ | ||
{ | ||
name: 'Risk Last Updated Date', | ||
selector: (row) => row['riskLastUpdatedDateTime'], | ||
sortable: true, | ||
exportSelector: 'riskLastUpdatedDateTime', | ||
}, | ||
{ | ||
name: 'User Principal Name', | ||
selector: (row) => row['userPrincipalName'], | ||
sortable: true, | ||
exportSelector: 'userPrincipalName', | ||
}, | ||
{ | ||
name: 'Risk Level', | ||
selector: (row) => row['riskLevel'], | ||
sortable: true, | ||
exportSelector: 'riskLevel', | ||
}, | ||
{ | ||
name: 'Risk State', | ||
selector: (row) => row['riskState'], | ||
sortable: true, | ||
exportSelector: 'riskState', | ||
}, | ||
{ | ||
name: 'Risk Detail', | ||
selector: (row) => row['riskDetail'], | ||
sortable: true, | ||
exportSelector: 'riskDetail', | ||
}, | ||
{ | ||
name: 'isProcessing', | ||
selector: (row) => row['isProcessing'], | ||
sortable: true, | ||
exportSelector: 'isProcessing', | ||
}, | ||
{ | ||
name: 'isDeleted', | ||
selector: (row) => row['isDeleted'], | ||
sortable: true, | ||
exportSelector: 'isDeleted', | ||
}, | ||
] | ||
|
||
const RiskyUsers = () => { | ||
const tenant = useSelector((state) => state.app.currentTenant) | ||
|
||
return ( | ||
<> | ||
<CippPageList | ||
title="Risky Users" | ||
capabilities={{ allTenants: true, helpContext: 'https://google.com' }} | ||
datatable={{ | ||
filterlist: [ | ||
{ | ||
filterName: 'State: none', | ||
filter: 'Complex: riskState eq none', | ||
}, | ||
{ | ||
filterName: 'State: atRisk', | ||
filter: 'Complex: riskState eq atRisk', | ||
}, | ||
{ | ||
filterName: 'State: confirmedCompromised', | ||
filter: 'Complex: riskState eq confirmedCompromised', | ||
}, | ||
{ | ||
filterName: 'State: confirmedSafe', | ||
filter: 'Complex: riskState eq confirmedSafe', | ||
}, | ||
{ | ||
filterName: 'State: dismissed', | ||
filter: 'Complex: riskState eq dismissed', | ||
}, | ||
{ | ||
filterName: 'State: remediated', | ||
filter: 'Complex: riskState eq remediated', | ||
}, | ||
{ | ||
filterName: 'State: unknownFutureValue', | ||
filter: 'Complex: riskState eq unknownFutureValue', | ||
}, | ||
], | ||
columns: columns, | ||
path: `api/ListGraphRequest`, | ||
reportName: `${tenant?.defaultDomainName}-ListRiskyUsers`, | ||
params: { | ||
TenantFilter: tenant?.defaultDomainName, | ||
Endpoint: `identityProtection/riskyUsers`, | ||
$count: true, | ||
$orderby: 'riskLastUpdatedDateTime', | ||
}, | ||
}} | ||
/> | ||
</> | ||
) | ||
} | ||
|
||
export default RiskyUsers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { useSelector } from 'react-redux' | ||
import { CippPageList } from 'src/components/layout' | ||
import { CellTip } from 'src/components/tables' | ||
|
||
const columns = [ | ||
{ | ||
name: 'Detected Date', | ||
selector: (row) => row['detectedDateTime'], | ||
sortable: true, | ||
exportSelector: 'detectedDateTime', | ||
}, | ||
{ | ||
name: 'User Principal Name', | ||
selector: (row) => row['userPrincipalName'], | ||
sortable: true, | ||
exportSelector: 'userPrincipalName', | ||
}, | ||
{ | ||
name: 'Location', | ||
selector: (row) => `${row.location?.city} - ${row.location?.countryOrRegion}`, | ||
sortable: true, | ||
exportSelector: 'Location', | ||
cell: (row) => CellTip(`${row.location?.city} - ${row.location?.countryOrRegion}`), | ||
}, | ||
{ | ||
name: 'IP Address', | ||
selector: (row) => row['ipAddress'], | ||
sortable: true, | ||
exportSelector: 'ipAddress', | ||
}, | ||
{ | ||
name: 'Risk State', | ||
selector: (row) => row['riskState'], | ||
sortable: true, | ||
exportSelector: 'riskState', | ||
}, | ||
{ | ||
name: 'Risk Detail', | ||
selector: (row) => row['riskDetail'], | ||
sortable: true, | ||
exportSelector: 'riskDetail', | ||
}, | ||
{ | ||
name: 'Risk Level', | ||
selector: (row) => row['riskLevel'], | ||
sortable: true, | ||
exportSelector: 'riskLevel', | ||
}, | ||
{ | ||
name: 'Risk Type', | ||
selector: (row) => row['riskType'], | ||
sortable: true, | ||
exportSelector: 'riskType', | ||
}, | ||
{ | ||
name: 'Risk Event Type', | ||
selector: (row) => row['riskEventType'], | ||
sortable: true, | ||
exportSelector: 'riskEventType', | ||
}, | ||
{ | ||
name: 'Detection Type', | ||
selector: (row) => row['detectionTimingType'], | ||
sortable: true, | ||
exportSelector: 'detectionTimingType', | ||
}, | ||
{ | ||
name: 'Activity', | ||
selector: (row) => row['activity'], | ||
sortable: true, | ||
exportSelector: 'activity', | ||
}, | ||
] | ||
|
||
const RiskDetections = () => { | ||
const tenant = useSelector((state) => state.app.currentTenant) | ||
|
||
return ( | ||
<> | ||
<CippPageList | ||
title="Risk Detection Report" | ||
capabilities={{ allTenants: true, helpContext: 'https://google.com' }} | ||
datatable={{ | ||
filterlist: [ | ||
{ | ||
filterName: 'State: atRisk', | ||
filter: 'Complex: riskState eq atRisk', | ||
}, | ||
{ | ||
filterName: 'State: confirmedCompromised', | ||
filter: 'Complex: riskState eq confirmedCompromised', | ||
}, | ||
{ | ||
filterName: 'State: confirmedSafe', | ||
filter: 'Complex: riskState eq confirmedSafe', | ||
}, | ||
{ | ||
filterName: 'State: dismissed', | ||
filter: 'Complex: riskState eq dismissed', | ||
}, | ||
{ | ||
filterName: 'State: remediated', | ||
filter: 'Complex: riskState eq remediated', | ||
}, | ||
{ | ||
filterName: 'State: unknownFutureValue', | ||
filter: 'Complex: riskState eq unknownFutureValue', | ||
}, | ||
], | ||
columns: columns, | ||
path: `api/ListGraphRequest`, | ||
reportName: `${tenant?.defaultDomainName}-RiskDetections-Report`, | ||
params: { | ||
TenantFilter: tenant?.defaultDomainName, | ||
Endpoint: `identityProtection/riskDetections`, | ||
$count: true, | ||
$orderby: 'detectedDateTime', | ||
}, | ||
}} | ||
/> | ||
</> | ||
) | ||
} | ||
|
||
export default RiskDetections |
Oops, something went wrong.