diff --git a/deployment/AzureDeploymentTemplate.json b/deployment/AzureDeploymentTemplate.json index ff7e9a97bada..dc3222498ec6 100644 --- a/deployment/AzureDeploymentTemplate.json +++ b/deployment/AzureDeploymentTemplate.json @@ -200,7 +200,7 @@ "name": "[variables('funcStorageName')]", "location": "[resourceGroup().location]", "tags": { - "displayName": "funStorageName" + "displayName": "funcStorageName" }, "sku": { "name": "Standard_LRS" diff --git a/deployment/AzureDeploymentTemplate_regionoptions.json b/deployment/AzureDeploymentTemplate_regionoptions.json index bc2f24089d4d..ac6691593a0b 100644 --- a/deployment/AzureDeploymentTemplate_regionoptions.json +++ b/deployment/AzureDeploymentTemplate_regionoptions.json @@ -6,7 +6,7 @@ "defaultValue": "CIPP", "type": "string", "metadata": { - "description": "Name use as base-template to named the resources deployed in Azure." + "description": "Name used as base-template to name the resources deployed in Azure." } }, "GithubRepository": { @@ -20,7 +20,7 @@ "defaultValue": "GeneratedPassword", "type": "string", "metadata": { - "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" + "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } }, "GithubAPIRepository": { @@ -197,7 +197,7 @@ "name": "[variables('funcStorageName')]", "location": "[resourceGroup().location]", "tags": { - "displayName": "funStorageName" + "displayName": "funcStorageName" }, "sku": { "name": "Standard_LRS" diff --git a/deployment/DevAzureDeploymentTemplate.json b/deployment/DevAzureDeploymentTemplate.json index 8beefadd450c..5e6077beab41 100644 --- a/deployment/DevAzureDeploymentTemplate.json +++ b/deployment/DevAzureDeploymentTemplate.json @@ -6,7 +6,7 @@ "defaultValue": "CIPPDev", "type": "string", "metadata": { - "description": "Name use as base-template to named the resources deployed in Azure." + "description": "Name used as base-template to name the resources deployed in Azure." } }, "TenantID": { @@ -48,7 +48,7 @@ "defaultValue": "GeneratedPassword", "type": "string", "metadata": { - "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" + "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } }, "GithubAPIRepository": { @@ -250,7 +250,7 @@ "name": "[variables('funcStorageName')]", "location": "[resourceGroup().location]", "tags": { - "displayName": "funStorageName" + "displayName": "funcStorageName" }, "sku": { "name": "Standard_LRS" diff --git a/deployment/DevAzureDeploymentTemplate_regionoptions.json b/deployment/DevAzureDeploymentTemplate_regionoptions.json index 0fb300af06c9..f41e669076e5 100644 --- a/deployment/DevAzureDeploymentTemplate_regionoptions.json +++ b/deployment/DevAzureDeploymentTemplate_regionoptions.json @@ -6,7 +6,7 @@ "defaultValue": "CIPP", "type": "string", "metadata": { - "description": "Name use as base-template to named the resources deployed in Azure." + "description": "Name used as base-template to name the resources deployed in Azure." } }, "TenantID": { @@ -48,7 +48,7 @@ "defaultValue": "GeneratedPassword", "type": "string", "metadata": { - "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" + "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } }, "GithubAPIRepository": { diff --git a/package.json b/package.json index b74d149fe82e..2739d1d2801a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.3.0", + "version": "6.4.0", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/img/RoB-light.svg b/public/img/RoB-light.svg new file mode 100644 index 000000000000..0673b2a8a449 --- /dev/null +++ b/public/img/RoB-light.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/RoB.svg b/public/img/RoB.svg new file mode 100644 index 000000000000..d188e1eb541b --- /dev/null +++ b/public/img/RoB.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/img/datto.png b/public/img/datto.png deleted file mode 100644 index b0fad6f50233..000000000000 Binary files a/public/img/datto.png and /dev/null differ diff --git a/public/version_latest.txt b/public/version_latest.txt index 798e38995c4d..19b860c1872d 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.3.0 +6.4.0 diff --git a/src/components/layout/AppFooter.jsx b/src/components/layout/AppFooter.jsx index c720c8f5a35b..09087af3a00e 100644 --- a/src/components/layout/AppFooter.jsx +++ b/src/components/layout/AppFooter.jsx @@ -10,7 +10,7 @@ const AppFooter = () => { const isDark = currentTheme === 'impact' || (currentTheme === 'default' && preferredTheme === 'impact') - const datto = isDark ? '/img/datto.png' : '/img/datto.png' + const RoB = isDark ? '/img/RoB.svg' : '/img/RoB-light.svg' const huntress = isDark ? '/img/huntress_teal.png' : '/img/huntress_teal.png' const rewst = isDark ? '/img/rewst_dark.png' : '/img/rewst.png' const ninjaone = isDark ? '/img/ninjaone_dark.png' : '/img/ninjaone.png' @@ -24,8 +24,12 @@ const AppFooter = () => { - - + + diff --git a/src/components/tables/CippDatatable.jsx b/src/components/tables/CippDatatable.jsx index 125eeda3e00a..7d00d9730e82 100644 --- a/src/components/tables/CippDatatable.jsx +++ b/src/components/tables/CippDatatable.jsx @@ -14,16 +14,18 @@ export default function CippDatatable({ path, params, ...rest }) { refetch, } = useListDatatableQuery({ path, params: { $filter: graphFilter, ...params } }) - let anonimized = false // Assuming default value is false + let anonymized = false // Assuming default value is false const regex = new RegExp('^[A-Z0-9]+$') const principalNameOrUPN = data[0]?.userPrincipalName ?? data[0]?.UPN ?? + data[0]?.Owner ?? data.Results?.[0]?.upn ?? - data.Results?.[0]?.userPrincipalName + data.Results?.[0]?.userPrincipalName ?? + data.Results?.[0]?.Owner if (principalNameOrUPN && regex.test(principalNameOrUPN)) { - anonimized = true + anonymized = true } var defaultFilterText = '' @@ -32,7 +34,7 @@ export default function CippDatatable({ path, params, ...rest }) { } return ( <> - {anonimized && ( + {anonymized && ( This table might contain anonymized data. Please check this { +const CippAppPermissionBuilder = ({ + onSubmit, + currentPermissions = {}, + isSubmitting, + colSize = 8, + removePermissionConfirm = true, + appDisplayName = 'CIPP-SAM', +}) => { const [selectedApp, setSelectedApp] = useState([]) const [permissionsImported, setPermissionsImported] = useState(false) const [newPermissions, setNewPermissions] = useState({}) @@ -42,6 +49,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt const [manifestVisible, setManifestVisible] = useState(false) const currentTheme = useSelector((state) => state.app.currentTheme) const [calloutMessage, setCalloutMessage] = useState(null) + const [initialPermissions, setInitialPermissions] = useState() const { data: servicePrincipals = [], @@ -59,35 +67,49 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt var servicePrincipal = selectedApp.find((sp) => sp?.appId === appId) var newServicePrincipals = selectedApp.filter((sp) => sp?.appId !== appId) - ModalService.confirm({ - title: 'Remove Service Principal', - body: `Are you sure you want to remove ${servicePrincipal.displayName}?`, - onConfirm: () => { - setSelectedApp(newServicePrincipals) - var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) - delete updatedPermissions.Permissions[appId] - setNewPermissions(updatedPermissions) - }, - }) + if (removePermissionConfirm) { + ModalService.confirm({ + title: 'Remove Service Principal', + body: `Are you sure you want to remove ${servicePrincipal.displayName}?`, + onConfirm: () => { + setSelectedApp(newServicePrincipals) + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + delete updatedPermissions.Permissions[appId] + setNewPermissions(updatedPermissions) + }, + }) + } else { + setSelectedApp(newServicePrincipals) + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + delete updatedPermissions.Permissions[appId] + setNewPermissions(updatedPermissions) + } } const confirmReset = () => { - ModalService.confirm({ - title: 'Reset to Default', - body: 'Are you sure you want to reset all permissions to default?', - onConfirm: () => { - setSelectedApp([]) - setPermissionsImported(false) - setManifestVisible(false) - setCalloutMessage('Permissions reset to default.') - }, - }) + if (removePermissionConfirm) { + ModalService.confirm({ + title: 'Reset to Default', + body: 'Are you sure you want to reset all permissions to default?', + onConfirm: () => { + setSelectedApp([]) + setPermissionsImported(false) + setManifestVisible(false) + setCalloutMessage('Permissions reset to default.') + }, + }) + } else { + setSelectedApp([]) + setPermissionsImported(false) + setManifestVisible(false) + setCalloutMessage('Permissions reset to default.') + } } const handleSubmit = (values) => { if (onSubmit) { var postBody = { - Permissions: newPermissions, + Permissions: newPermissions.Permissions, } onSubmit(postBody) } @@ -127,30 +149,43 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt } const removePermissionRow = (servicePrincipal, permissionType, permissionId, permissionValue) => { - // modal confirm - ModalService.confirm({ - title: 'Remove Permission', - body: `Are you sure you want to remove the permission: ${permissionValue}?`, - onConfirm: () => { - var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) - var currentPermission = updatedPermissions?.Permissions[servicePrincipal][permissionType] - var newPermission = [] - if (currentPermission) { - currentPermission.map((perm) => { - if (perm.id !== permissionId) { - newPermission.push(perm) - } - }) - } - updatedPermissions.Permissions[servicePrincipal][permissionType] = newPermission - setNewPermissions(updatedPermissions) - }, - }) + if (removePermissionConfirm) { + ModalService.confirm({ + title: 'Remove Permission', + body: `Are you sure you want to remove the permission: ${permissionValue}?`, + onConfirm: () => { + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + var currentPermission = updatedPermissions?.Permissions[servicePrincipal][permissionType] + var newPermission = [] + if (currentPermission) { + currentPermission.map((perm) => { + if (perm.id !== permissionId) { + newPermission.push(perm) + } + }) + } + updatedPermissions.Permissions[servicePrincipal][permissionType] = newPermission + setNewPermissions(updatedPermissions) + }, + }) + } else { + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + var currentPermission = updatedPermissions?.Permissions[servicePrincipal][permissionType] + var newPermission = [] + if (currentPermission) { + currentPermission.map((perm) => { + if (perm.id !== permissionId) { + newPermission.push(perm) + } + }) + } + updatedPermissions.Permissions[servicePrincipal][permissionType] = newPermission + setNewPermissions(updatedPermissions) + } } - const generateManifest = (appDisplayName = 'CIPP-SAM', prompt = false) => { - if (prompt) { - // modal input form for appDisplayName + const generateManifest = ({ appDisplayName = 'CIPP-SAM', prompt = false }) => { + if (prompt || appDisplayName === '') { ModalService.prompt({ title: 'Generate Manifest', body: 'Please enter the display name for the application.', @@ -337,6 +372,11 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt }, }, }) + } else if (spSuccess & (currentPermissions !== initialPermissions)) { + setSelectedApp([]) + setNewPermissions(currentPermissions) + setInitialPermissions(currentPermissions) + setPermissionsImported(false) } else if (spSuccess && initialAppIds.length > 0 && permissionsImported == false) { var newApps = [] initialAppIds?.map((appId) => { @@ -350,10 +390,12 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt }) setSelectedApp(newApps) setNewPermissions(currentPermissions) + setInitialPermissions(currentPermissions) setPermissionsImported(true) } }, [ currentPermissions, + initialPermissions, permissionsImported, spSuccess, selectedApp, @@ -361,6 +403,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt setSelectedApp, setPermissionsImported, setNewPermissions, + setInitialPermissions, ]) const ApiPermissionRow = ({ servicePrincipal = null }) => { @@ -679,7 +722,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt return ( - + {servicePrincipals?.Metadata?.Success && ( @@ -738,7 +781,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt { - generateManifest() + generateManifest({ appDisplayName: appDisplayName }) }} className={`circular-button`} title={'+'} @@ -764,7 +807,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt title="Import Manifest" id="importManifest" visible={manifestVisible} - onHide={() => { + hideFunction={() => { setManifestVisible(false) }} addedClass="offcanvas-large" @@ -957,6 +1000,9 @@ CippAppPermissionBuilder.propTypes = { onSubmit: PropTypes.func, currentPermissions: PropTypes.object, isSubmitting: PropTypes.bool, + colSize: PropTypes.number, + removePermissionConfirm: PropTypes.bool, + appDisplayName: PropTypes.string, } export default CippAppPermissionBuilder diff --git a/src/data/alerts.json b/src/data/alerts.json index 13f18265caf4..fe03d7b6a968 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -19,6 +19,11 @@ "label": "Alert on changed admin Passwords", "recommendedRunInterval": "30m" }, + { + "name": "InactiveLicensedUsers", + "label": "Alert on licensed users that have not logged in for 90 days", + "recommendedRunInterval": "1d" + }, { "name": "QuotaUsed", "label": "Alert on % mailbox quota used", diff --git a/src/data/standards.json b/src/data/standards.json index 45e7c0438ea1..4b12a7181b10 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -349,7 +349,7 @@ "name": "standards.TAP", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], - "helpText": "Enables TAP and sets the default TAP lifetime to 1 hour. This configuration also allows you to select is a TAP is single use or multi-logon.", + "helpText": "Enables TAP and sets the default TAP lifetime to 1 hour. This configuration also allows you to select if a TAP is single use or multi-logon.", "docsDescription": "Enables Temporary Password generation for the tenant.", "addedComponent": [ { @@ -648,7 +648,7 @@ "name": "standards.DisableEmail", "cat": "Entra (AAD) Standards", "tag": ["highimpact"], - "helpText": "This blocks users from using email as an MFA method. This disables the email OTP option for guest users, and instead promts them to create a Microsoft account.", + "helpText": "This blocks users from using email as an MFA method. This disables the email OTP option for guest users, and instead prompts them to create a Microsoft account.", "addedComponent": [], "label": "Disables Email as an MFA method", "impact": "High Impact", diff --git a/src/scss/_custom.scss b/src/scss/_custom.scss index b118641b872e..2b1e6c2aa248 100644 --- a/src/scss/_custom.scss +++ b/src/scss/_custom.scss @@ -257,6 +257,10 @@ h3.underline:after { } } +.modal { + z-index: 1555 !important; +} + .modal-content { border-radius: var(--cipp-border-radius); } diff --git a/src/store/api/app.js b/src/store/api/app.js index 3eaa06328f84..bb2e4bb45ce2 100644 --- a/src/store/api/app.js +++ b/src/store/api/app.js @@ -139,4 +139,5 @@ export const { useLazyGenericPostRequestQuery, useLazyGenericGetRequestQuery, useGenericGetRequestQuery, + useGenericPostRequestQuery, } = appApi diff --git a/src/store/api/reports.js b/src/store/api/reports.js index 83be1b4fa361..2292662ad2be 100644 --- a/src/store/api/reports.js +++ b/src/store/api/reports.js @@ -6,10 +6,10 @@ export const reportsApi = baseApi.injectEndpoints({ query: () => ({ path: '/api/BestPracticeAnalyser_List' }), }), execBestPracticeAnalyser: builder.mutation({ - query: () => ({ path: '/api/BestPracticeAnalyser_OrchestrationStarter' }), + query: () => ({ path: '/api/ExecBPA' }), }), execDomainsAnalyser: builder.mutation({ - query: () => ({ path: '/api/DomainAnalyser_OrchestrationStarter' }), + query: () => ({ path: '/api/ExecDomainAnalyser' }), }), }), }) diff --git a/src/store/api/users.js b/src/store/api/users.js index 7ef16f12fdb3..552481567406 100644 --- a/src/store/api/users.js +++ b/src/store/api/users.js @@ -64,6 +64,7 @@ export const usersApi = baseApi.injectEndpoints({ userId: _args.userId, tenantFilter: _args.tenantFilter, userName: _args.userName, + overwrite: _args.overwrite, }, }) if (startRequest.error) { diff --git a/src/views/cipp/AppApprovalTemplates.jsx b/src/views/cipp/AppApprovalTemplates.jsx new file mode 100644 index 000000000000..6f9159565dcd --- /dev/null +++ b/src/views/cipp/AppApprovalTemplates.jsx @@ -0,0 +1,140 @@ +import React from 'react' +import { + CCol, + CRow, + CCallout, + CSpinner, + CButton, + CFormInput, + CFormLabel, + CTooltip, +} from '@coreui/react' +import { Field, Form, FormSpy } from 'react-final-form' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' +import { CippPageList, CippWizard } from 'src/components/layout' +import { cellDateFormatter, CippTable, WizardTableField } from 'src/components/tables' +import PropTypes from 'prop-types' +import { + Condition, + RFFCFormCheck, + RFFCFormInput, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms' +import { useLazyGenericPostRequestQuery } from 'src/store/api/app' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { CippOffcanvas } from 'src/components/utilities' +import CippAppPermissionBuilder from 'src/components/utilities/CippAppPermissionBuilder' + +const AppApprovalTemplates = () => { + const [editorVisible, setEditorVisible] = React.useState(false) + const [selectedTemplate, setSelectedTemplate] = React.useState(null) + const templateNameRef = React.useRef(null) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + + const onSubmit = (values) => { + var body = { + TemplateName: templateNameRef.current.value, + Permissions: values.Permissions, + } + if (selectedTemplate?.TemplateId) { + body.TemplateId = selectedTemplate.TemplateId + } + + console.log(body) + genericPostRequest({ + path: '/api/ExecAppPermissionTemplate?Action=Save', + values: body, + }).then(() => {}) + } + const titleButton = ( + { + setSelectedTemplate({}) + templateNameRef.current.value = '' + setEditorVisible(true) + }} + > + Add Template + + ) + return ( + <> + row['TemplateName'], + sortable: true, + exportSelector: 'TemplateName', + }, + { + name: 'Updated By', + selector: (row) => row['UpdatedBy'], + sortable: true, + exportSelector: 'UpdatedBy', + }, + { + name: 'Updated At', + selector: (row) => row['Timestamp'], + sortable: true, + exportSelector: 'Timestamp', + cell: cellDateFormatter({ format: 'short' }), + }, + { + name: 'Actions', + cell: (row) => ( + + { + setSelectedTemplate(row) + templateNameRef.current.value = row.TemplateName + setEditorVisible(true) + }} + > + + + + ), + }, + ], + reportName: 'AppApprovalTemplates', + }} + /> + setEditorVisible(false)} + > + Template Name + + Permissions + + + + ) +} + +export default AppApprovalTemplates diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 6d7a63d2a3d0..4ed3362e2b59 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -169,7 +169,7 @@ export function SettingsExtensionMappings({ type }) { ) } - const halocolumns = [ + const haloColumns = [ { name: 'Tenant', selector: (row) => row.Tenant?.displayName, @@ -205,7 +205,7 @@ export function SettingsExtensionMappings({ type }) { }, ] - const ninjacolumns = [ + const ninjaColumns = [ { name: 'Tenant', selector: (row) => row.Tenant?.displayName, @@ -286,7 +286,7 @@ export function SettingsExtensionMappings({ type }) { @@ -439,7 +439,7 @@ export function SettingsExtensionMappings({ type }) { diff --git a/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx b/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx index d36dffb0b597..55a56aea7f5e 100644 --- a/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx +++ b/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx @@ -21,6 +21,7 @@ import { useListTenantsQuery } from 'src/store/api/tenants' import { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' import CippButtonCard from 'src/components/contentcards/CippButtonCard' import CippAppPermissionBuilder from 'src/components/utilities/CippAppPermissionBuilder' +import Skeleton from 'react-loading-skeleton' const SettingsAppPermissions = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() @@ -36,6 +37,7 @@ const SettingsAppPermissions = () => { const { data: samAppPermissions = [], isFetching: samAppPermissionsFetching, + isSuccess: samAppPermissionsSuccess, refetch: refetchSam, } = useGenericGetRequestQuery({ path: 'api/ExecSAMAppPermissions', @@ -51,11 +53,14 @@ const SettingsAppPermissions = () => { advised.

- + {samAppPermissionsFetching && } + {samAppPermissionsSuccess && ( + + )} {postResults.data && ( diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index 3f16a1e389d9..740aa2417455 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -14,6 +14,8 @@ import { CForm, CRow, CSpinner, + CLink, + CBadge, } from '@coreui/react' import useQuery from 'src/hooks/useQuery' import { useDispatch } from 'react-redux' @@ -24,14 +26,16 @@ import { useLazyGenericPostRequestQuery, useLazyGenericGetRequestQuery, useGenericGetRequestQuery, + useGenericPostRequestQuery, } from 'src/store/api/app' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { useListMailboxDetailsQuery, useListMailboxPermissionsQuery } from 'src/store/api/mailbox' -import { CellBoolean, CippDatatable } from 'src/components/tables' +import { CellBadge, CellBoolean, CippDatatable } from 'src/components/tables' import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import PropTypes from 'prop-types' +import Skeleton from 'react-loading-skeleton' const formatter = (cell, warning = false, reverse = false, colourless = false) => CellBoolean({ cell, warning, reverse, colourless }) @@ -42,6 +46,8 @@ const MailboxSettings = () => { const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') const [active, setActive] = useState(1) + const [forwardingRefresh, setForwardingRefresh] = useState('0') + const [oooRefresh, setOooRefresh] = useState('0') const columnsCal = [ { name: 'User', @@ -122,12 +128,20 @@ const MailboxSettings = () => { - + + setForwardingRefresh((Math.random() + 1).toString(36).substring(7)) + } + /> - + + setOooRefresh((Math.random() + 1).toString(36).substring(7)) + } + /> @@ -158,12 +172,20 @@ const MailboxSettings = () => { )} {active === 3 && ( <> - + )} {active === 4 && ( <> - + )} @@ -195,8 +217,10 @@ const MailboxPermissions = () => { params: { Endpoint: 'users', TenantFilter: tenantDomain, - $filter: 'assignedLicenses/$count ne 0 and accountEnabled eq true', + $filter: "assignedLicenses/$count ne 0 and accountEnabled eq true and userType eq 'Member'", + $select: 'id,displayName,userPrincipalName', $count: true, + $orderby: 'displayName', }, }) @@ -258,8 +282,8 @@ const MailboxPermissions = () => { label="Remove Full Access" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="RemoveFullAccess" @@ -272,8 +296,8 @@ const MailboxPermissions = () => { label="Add Full Access - Automapping Enabled" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddFullAccess" @@ -286,8 +310,8 @@ const MailboxPermissions = () => { label="Add Full Access - Automapping Disabled" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddFullAccessNoAutoMap" @@ -300,8 +324,8 @@ const MailboxPermissions = () => { label="Add Send-as permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddSendAs" @@ -314,8 +338,8 @@ const MailboxPermissions = () => { label="Remove Send-as permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="RemoveSendAs" @@ -328,8 +352,8 @@ const MailboxPermissions = () => { label="Add Send On Behalf permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddSendOnBehalf" @@ -342,8 +366,8 @@ const MailboxPermissions = () => { label="Remove Send On Behalf permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="RemoveSendOnBehalf" @@ -560,7 +584,7 @@ const CalendarPermissions = () => { ) } -const MailboxForwarding = () => { +const MailboxForwarding = ({ refreshFunction }) => { const dispatch = useDispatch() let query = useQuery() const userId = query.get('userId') @@ -586,7 +610,10 @@ const MailboxForwarding = () => { params: { Endpoint: 'users', TenantFilter: tenantDomain, - $filter: "userType eq 'Member' and mail ge ' '", // filter out guests and users with no mailbox. #HACK "mail ne 'null'" does not work so this horrible hack is required + $filter: "userType eq 'Member' and proxyAddresses/$count ne 0", + $select: 'id,displayName,userPrincipalName', + $count: true, + $orderby: 'displayName', }, }) useEffect(() => { @@ -613,7 +640,9 @@ const MailboxForwarding = () => { disableForwarding: values.forwardOption === 'disabled', } //window.alert(JSON.stringify(shippedValues)) - genericPostRequest({ path: '/api/ExecEmailForward', values: shippedValues }) + genericPostRequest({ path: '/api/ExecEmailForward', values: shippedValues }).then(() => { + refreshFunction() + }) } const initialState = { ...user, @@ -661,11 +690,10 @@ const MailboxForwarding = () => { {values.forwardOption === 'internalAddress' && ( ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="ForwardInternal" @@ -754,41 +782,182 @@ const MailboxForwarding = () => { ) } +MailboxForwarding.propTypes = { + refreshFunction: PropTypes.func, +} -const ForwardingSettings = () => { +const ForwardingSettings = ({ refresh }) => { const query = useQuery() const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') - const { data: details, isFetching, error } = useListMailboxDetailsQuery({ userId, tenantDomain }) - const content = [ - { - heading: 'Forward and Deliver', - body: formatter(details?.ForwardAndDeliver, false, false, true), + const [content, setContent] = useState([]) + const [currentRefresh, setCurrentRefresh] = useState('') + const { + data: details, + isFetching, + isSuccess, + error, + } = useGenericPostRequestQuery({ + path: `/api/ListExoRequest`, + values: { + TenantFilter: tenantDomain, + Cmdlet: 'Get-Mailbox', + cmdParams: { Identity: userId }, + Select: 'ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward', + refresh: currentRefresh, }, - { - heading: 'Forwarding Address', - body: details?.ForwardingAddress ? details?.ForwardingAddress : 'N/A', + }) + + const { + data: users = [], + isFetching: usersIsFetching, + isSuccess: usersSuccess, + error: usersError, + } = useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + Endpoint: 'users', + TenantFilter: tenantDomain, + $filter: "userType eq 'Member' and proxyAddresses/$count ne 0", + $select: 'id,displayName,userPrincipalName', + $count: true, }, - ] + }) + + useEffect(() => { + if (refresh !== currentRefresh) { + setCurrentRefresh(refresh) + } + + if (usersSuccess && isSuccess) { + if (details?.Results?.ForwardingAddress !== null) { + var user = null + if ( + details?.Results?.ForwardingAddress.match( + /^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$/, + ) + ) { + const userId = details?.Results?.ForwardingAddress + user = users?.Results?.find((u) => u.id === userId) + } + if (user) { + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: ( + <> + + Internal + + {user.displayName} + + ), + }, + ]) + } else { + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: ( + <> + + Internal + + {details?.Results?.ForwardingAddress} + + ), + }, + ]) + } + } else if (details?.Results?.ForwardingSmtpAddress !== null) { + var smtpAddress = details?.Results?.ForwardingSmtpAddress.replace('smtp:', '') + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: ( + <> + + External + + {smtpAddress} + + ), + }, + ]) + } else { + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: 'N/A', + }, + ]) + } + } + }, [refresh, currentRefresh, users, details, usersSuccess, isSuccess]) return ( - {isFetching && } - {!isFetching && ( - - {content.map((item, index) => ( -
-
{item.heading}
-

{item.body}

+ + {isFetching || usersIsFetching ? ( + <> +
+
Forward and Deliver
+

+ +

- ))} -
- )} +
+
Forwarding Address
+

+ +

+
+ + ) : ( + <> + {content.map((item, index) => ( +
+
{item.heading}
+

{item.body}

+
+ ))} + + )} + + + setCurrentRefresh((Math.random() + 1).toString(36).substring(7))} + color="primary" + variant="ghost" + className="float-end" + > + + + ) } +ForwardingSettings.propTypes = { + refresh: PropTypes.string, +} -const OutOfOffice = () => { +const OutOfOffice = ({ refreshFunction }) => { const dispatch = useDispatch() let query = useQuery() const userId = query.get('userId') @@ -803,19 +972,6 @@ const OutOfOffice = () => { error: userError, } = useListMailboxPermissionsQuery({ tenantDomain, userId }) - const { - data: users = [], - isFetching: usersIsFetching, - error: usersError, - } = useGenericGetRequestQuery({ - path: '/api/ListGraphRequest', - params: { - Endpoint: 'users', - TenantFilter: tenantDomain, - $filter: 'assignedLicenses/$count ne 0 and accountEnabled eq true', - $count: true, - }, - }) useEffect(() => { if (postResults.isSuccess) { // @TODO do something here? @@ -841,7 +997,9 @@ const OutOfOffice = () => { ExternalMessage: values.ExternalMessage ? values.ExternalMessage : '', } //window.alert(JSON.stringify(shippedValues)) - genericPostRequest({ path: '/api/ExecSetOoO', values: shippedValues }) + genericPostRequest({ path: '/api/ExecSetOoO', values: shippedValues }).then(() => { + refreshFunction() + }) } const initialState = { ...user, @@ -865,9 +1023,9 @@ const OutOfOffice = () => { )} - {usersIsFetching && } + {userIsFetching && } {userError && Error loading user} - {!usersIsFetching && ( + {!userIsFetching && (
{ ) } +OutOfOffice.propTypes = { + refreshFunction: PropTypes.func, +} -const OutOfOfficeSettings = () => { +const OutOfOfficeSettings = ({ refresh }) => { const query = useQuery() const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') const tenantFilter = tenantDomain + const [currentRefresh, setCurrentRefresh] = useState('') + + useEffect(() => { + if (refresh !== currentRefresh) { + setCurrentRefresh(refresh) + } + }, [refresh, currentRefresh, setCurrentRefresh]) + const { data: details, isFetching, error, } = useGenericGetRequestQuery({ path: '/api/ListOoO', - params: { userId, tenantFilter }, + params: { userId, tenantFilter, currentRefresh }, }) const combinedRegex = /(<([^>]+)>)|| /gi const content = [ @@ -996,22 +1165,45 @@ const OutOfOfficeSettings = () => { ] return ( - {isFetching && ( - - Loading - - )} - {!isFetching && ( - - {content.map((item, index) => ( -
-
{item.heading}
-

{item.body}

-
- ))} -
- )} - {error && Could not connect to API: {error.message}} + + {isFetching && ( + <> + {content.map((item, index) => ( +
+
{item.heading}
+

+ +

+
+ ))} + + )} + {!isFetching && ( + <> + {content.map((item, index) => ( +
+
{item.heading}
+

{item.body}

+
+ ))} + + )} + + {error && Could not connect to API: {error.message}} +
+ + setCurrentRefresh((Math.random() + 1).toString(36).substring(7))} + color="primary" + variant="ghost" + className="float-end" + > + + +
) } +OutOfOfficeSettings.propTypes = { + refresh: PropTypes.string, +} diff --git a/src/views/email-exchange/administration/MailboxRuleList.jsx b/src/views/email-exchange/administration/MailboxRuleList.jsx index 34f3798900d8..b941e501e01b 100644 --- a/src/views/email-exchange/administration/MailboxRuleList.jsx +++ b/src/views/email-exchange/administration/MailboxRuleList.jsx @@ -3,6 +3,43 @@ import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { CellTip } from 'src/components/tables' import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { CButton } from '@coreui/react' +import { faTrash } from '@fortawesome/free-solid-svg-icons' +import { ModalService } from 'src/components/utilities' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' + +const DeleteMailboxRuleButton = (ruleId, userPrincipalName, ruleName) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() + const handleModal = (modalMessage, modalUrl) => { + ModalService.confirm({ + body: ( +
+
{modalMessage}
+
+ ), + title: 'Confirm', + onConfirm: () => genericGetRequest({ path: modalUrl }), + }) + } + return ( + { + ModalService.confirm( + handleModal( + 'Are you sure you want to remove this mailbox rule?', + `/api/ExecRemoveMailboxRule?TenantFilter=${tenant?.defaultDomainName}&ruleId=${ruleId}&ruleName=${ruleName}&userPrincipalName=${userPrincipalName}`, + ), + ) + }} + > + + + ) +} const MailboxRuleList = () => { const tenant = useSelector((state) => state.app.currentTenant) @@ -62,9 +99,17 @@ const MailboxRuleList = () => { exportSelector: 'ForwardTo', cell: cellGenericFormatter(), }, + { + name: 'Action', + maxWidth: '100px', + cell: (row) => + DeleteMailboxRuleButton(row['Identity'], row['UserPrincipalName'], row['Name']), + }, ] return ( + // TODO: Add support for displaying the result of the delete operation. Currently, the delete operation is performed but the result is not displayed anywhere but the networking tab of the dev tools in the browser. + // All API code is in place and should return the needed HTTP status information. -Bobby { const tenant = useSelector((state) => state.app.currentTenant) - const Offcanvas = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) + const [msgOcVisible, setMsgOcVisible] = useState(false) + const [getQuarantineMessage, quarantineMessage] = useLazyGenericGetRequestQuery() return ( <> - setOCVisible(true)}> - - + + { + setMsgOcVisible(true) + getQuarantineMessage({ + path: `/api/ListMailQuarantineMessage`, + params: { TenantFilter: tenant.defaultDomainName, Identity: row?.Identity }, + }) + }} + > + + + + + setOCVisible(true)}> + + + { id={row.id} hideFunction={() => setOCVisible(false)} /> + setMsgOcVisible(false)} + visible={msgOcVisible} + placement="end" + > + <> + {quarantineMessage.isLoading && } + {quarantineMessage.isSuccess && ( + + )} + + ) } diff --git a/src/views/email-exchange/connectors/ConnectorList.jsx b/src/views/email-exchange/connectors/ConnectorList.jsx index 1f3829abcb1d..c5c2b2d0951a 100644 --- a/src/views/email-exchange/connectors/ConnectorList.jsx +++ b/src/views/email-exchange/connectors/ConnectorList.jsx @@ -20,44 +20,46 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { , modalBody: row, modalType: 'POST', modalUrl: `/api/AddExConnectorTemplate`, - modalMessage: 'Are you sure you want to create a template based on this rule?', + modalMessage: 'Are you sure you want to create a template based on this connector?', }, { - label: 'Enable Rule', + label: 'Enable Connector', color: 'info', icon: , modal: true, modalUrl: `/api/EditExConnector?State=Enable&TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to enable this rule?', + modalMessage: 'Are you sure you want to enable this connector?', }, { - label: 'Disable Rule', + label: 'Disable Connector', color: 'info', icon: , modal: true, modalUrl: `/api/EditExConnector?State=Disable&TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to disable this rule?', + modalMessage: 'Are you sure you want to disable this connector?', }, { - label: 'Delete Rule', + label: 'Delete Connector', color: 'danger', modal: true, icon: , modalUrl: `/api/RemoveExConnector?TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to delete this rule?', + modalMessage: 'Are you sure you want to delete this connector?', }, ]} placement="end" diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx index f010c0b37c4f..bc63e647b186 100644 --- a/src/views/email-exchange/tools/MessageViewer.jsx +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -23,7 +23,7 @@ import DOMPurify from 'dompurify' import ReactHtmlParser from 'react-html-parser' import CippDropzone from 'src/components/utilities/CippDropzone' -const MessageViewer = ({ emailSource }) => { +export const MessageViewer = ({ emailSource }) => { const [emlContent, setEmlContent] = useState(null) const [emlError, setEmlError] = useState(false) const [messageHtml, setMessageHtml] = useState('') diff --git a/src/views/email-exchange/transport/TransportRules.jsx b/src/views/email-exchange/transport/TransportRules.jsx index 9d534e17dee5..549f245a2ea5 100644 --- a/src/views/email-exchange/transport/TransportRules.jsx +++ b/src/views/email-exchange/transport/TransportRules.jsx @@ -57,7 +57,7 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { modal: true, icon: , modalUrl: `/api/RemoveTransportRule?TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}`, - modalMessage: 'Are you sure you want to disable this rule?', + modalMessage: 'Are you sure you want to delete this rule?', }, ]} placement="end" diff --git a/src/views/endpoint/applications/ApplicationsAddRMM.jsx b/src/views/endpoint/applications/ApplicationsAddRMM.jsx index a6583e00fd4c..e90e31ff78e3 100644 --- a/src/views/endpoint/applications/ApplicationsAddRMM.jsx +++ b/src/views/endpoint/applications/ApplicationsAddRMM.jsx @@ -325,7 +325,7 @@ const AddRMM = () => { diff --git a/src/views/endpoint/applications/ListApplicationQueue.jsx b/src/views/endpoint/applications/ListApplicationQueue.jsx index 53f1e9197d33..eb42ed86ec47 100644 --- a/src/views/endpoint/applications/ListApplicationQueue.jsx +++ b/src/views/endpoint/applications/ListApplicationQueue.jsx @@ -20,7 +20,7 @@ const RefreshAction = () => { Please note: This job runs automatically every 12 hours.
), - onConfirm: () => execStandards({ path: 'api/AddChocoApp_OrchestrationStarter' }), + onConfirm: () => execStandards({ path: 'api/ExecAppUpload' }), }) return ( diff --git a/src/views/endpoint/intune/MEMListCompliance.jsx b/src/views/endpoint/intune/MEMListCompliance.jsx index 328a387566fd..87c2fdfabdf3 100644 --- a/src/views/endpoint/intune/MEMListCompliance.jsx +++ b/src/views/endpoint/intune/MEMListCompliance.jsx @@ -138,6 +138,7 @@ const ComplianceList = () => { Endpoint: 'deviceManagement/deviceCompliancePolicies', $orderby: 'displayName', $count: true, + $expand: 'assignments', }, columns, reportName: `${tenant?.defaultDomainName}-MEMPolicies-List`, diff --git a/src/views/endpoint/intune/MEMListPolicies.jsx b/src/views/endpoint/intune/MEMListPolicies.jsx index f63db28effef..725c4cc0cd42 100644 --- a/src/views/endpoint/intune/MEMListPolicies.jsx +++ b/src/views/endpoint/intune/MEMListPolicies.jsx @@ -15,6 +15,7 @@ import { CippPageList } from 'src/components/layout' import { Link } from 'react-router-dom' import { CippActionsOffcanvas, CippCodeBlock } from 'src/components/utilities' import { TitleButton } from 'src/components/buttons' +import { cellBooleanFormatter, cellDateFormatter } from 'src/components/tables' const Actions = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) @@ -90,12 +91,43 @@ const columns = [ name: 'Name', sortable: true, exportSelector: 'displayName', + maxWidth: 'auto', }, { selector: (row) => row['PolicyTypeName'], name: 'Profile Type', sortable: true, exportSelector: 'PolicyTypeName', + maxWidth: '300px', + }, + { + selector: (row) => row['PolicyAssignment'], + name: 'Assigned', + sortable: true, + exportSelector: 'PolicyAssignment', + maxWidth: '300px', + }, + { + selector: (row) => row['PolicyExclude'], + name: 'Excluded', + sortable: true, + exportSelector: 'PolicyExclude', + maxWidth: '300px', + }, + { + selector: (row) => row['description'], + name: 'Description', + sortable: true, + exportSelector: 'description', + maxWidth: 'auto', + }, + { + selector: (row) => row['lastModifiedDateTime'], + name: 'Last Modified', + sortable: true, + exportSelector: 'lastModifiedDateTime', + cell: cellDateFormatter({ format: 'relative' }), + maxWidth: '150px', }, { selector: (row) => row['id'], diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index 146ab7d6fef1..b8d5945f001b 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -23,7 +23,6 @@ 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' diff --git a/src/views/identity/administration/Devices.jsx b/src/views/identity/administration/Devices.jsx index 664f7552757c..f7808c39034a 100644 --- a/src/views/identity/administration/Devices.jsx +++ b/src/views/identity/administration/Devices.jsx @@ -36,6 +36,13 @@ const DevicesList = () => { modalUrl: `/api/ExecDeviceDelete?TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&Action=Enable`, modalMessage: 'Are you sure you want to enable this device.', }, + { + label: 'Retrieve Bitlocker Keys', + color: 'info', + modal: true, + modalUrl: `/api/ExecGetRecoveryKey?TenantFilter=${tenant.defaultDomainName}&GUID=${row.id}`, + modalMessage: 'Are you sure you want to retrieve the Bitlocker keys?', + }, { label: 'Disable Device', color: 'info', diff --git a/src/views/identity/administration/UserMailboxRuleList.jsx b/src/views/identity/administration/UserMailboxRuleList.jsx index ae2592866e85..f0a1e05a4d31 100644 --- a/src/views/identity/administration/UserMailboxRuleList.jsx +++ b/src/views/identity/administration/UserMailboxRuleList.jsx @@ -2,7 +2,12 @@ import React from 'react' import PropTypes from 'prop-types' import { CellBoolean, cellBooleanFormatter, CellTip } from 'src/components/tables' import { DatatableContentCard } from 'src/components/contentcards' -import { faEnvelope } from '@fortawesome/free-solid-svg-icons' +import { faEnvelope, faTrash } from '@fortawesome/free-solid-svg-icons' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { CButton } from '@coreui/react' +import { ModalService } from 'src/components/utilities' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' const rowStyle = (row, rowIndex) => { const style = {} @@ -10,6 +15,37 @@ const rowStyle = (row, rowIndex) => { return style } +const DeleteMailboxRuleButton = (tenantDomain, ruleId, userPrincipalName, ruleName) => { + const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() + const handleModal = (modalMessage, modalUrl) => { + ModalService.confirm({ + body: ( +
+
{modalMessage}
+
+ ), + title: 'Confirm', + onConfirm: () => genericGetRequest({ path: modalUrl }), + }) + } + return ( + { + ModalService.confirm( + handleModal( + 'Are you sure you want to remove this mailbox rule?', + `/api/ExecRemoveMailboxRule?TenantFilter=${tenantDomain}&ruleId=${ruleId}&ruleName=${ruleName}&userPrincipalName=${userPrincipalName}`, + ), + ) + }} + > + + + ) +} + export default function UserMailboxRuleList({ userId, userEmail, tenantDomain, className = null }) { const formatter = (cell) => CellBoolean({ cell }) const columns = [ @@ -70,6 +106,11 @@ export default function UserMailboxRuleList({ userId, userEmail, tenantDomain, c exportSelector: 'DeleteMessage', width: '200px', }, + { + name: 'Action', + maxWidth: '100px', + cell: (row) => DeleteMailboxRuleButton(tenantDomain, row['Identity'], userEmail, row['Name']), + }, ] return ( { let query = useQuery() @@ -85,28 +86,33 @@ const ViewBec = () => { const logonColumns = [ { name: 'App', - selector: (row) => row['AppDisplayName'], + selector: (row) => row['appDisplayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Date Time', - selector: (row) => row['CreatedDateTime'], + selector: (row) => row['createdDateTime'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Error code', selector: (row) => row.id, sortable: true, + cell: cellGenericFormatter(), }, { name: 'Details', selector: (row) => row.Status, sortable: true, + cell: cellGenericFormatter(), }, { name: 'IP', selector: (row) => row.IPAddress, sortable: true, + cell: cellGenericFormatter(), }, ] @@ -115,21 +121,25 @@ const ViewBec = () => { name: 'IP', selector: (row) => row['IPAddress'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'User', selector: (row) => row['userPrincipalName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Application', selector: (row) => row['AppDisplayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Result', selector: (row) => row['Status'], sortable: true, + cell: cellGenericFormatter(), }, ] const newUserColumns = [ @@ -137,16 +147,19 @@ const ViewBec = () => { name: 'DisplayName', selector: (row) => row['displayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Username', selector: (row) => row['userPrincipalName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Date', - selector: (row) => row['CreatedDateTime'], + selector: (row) => row['createdDateTime'], sortable: true, + cell: cellGenericFormatter(), }, ] @@ -155,16 +168,19 @@ const ViewBec = () => { name: 'displayName', selector: (row) => row['displayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Username', selector: (row) => row['userPrincipalName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Date', selector: (row) => row['lastPasswordChangeDateTime'], sortable: true, + cell: cellGenericFormatter(), }, ] @@ -173,44 +189,46 @@ const ViewBec = () => { name: 'Operation', selector: (row) => row['Operation'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Executed by', selector: (row) => row['UserKey'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Executed on', selector: (row) => row['ObjectId'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Permissions', selector: (row) => row['Permissions'], sortable: true, + cell: cellGenericFormatter(), }, ] const appColumns = [ { - name: 'Type', - selector: (row) => row['Operation'], - sortable: true, - }, - { - name: 'User', - selector: (row) => row['UserId'], + name: 'Application', + selector: (row) => row['appDisplayName'], sortable: true, + cell: cellGenericFormatter(), }, { - name: 'Application', - selector: (row) => row['ObjectId'], + name: 'Application ID', + selector: (row) => row['appId'], sortable: true, + cell: cellGenericFormatter(), }, { - name: 'Result', - selector: (row) => row['ResultStatus'], + name: 'Created', + selector: (row) => row['createdDateTime'], sortable: true, + cell: cellGenericFormatter(), }, ] const handleReMediate = useConfirmModal({ @@ -239,6 +257,7 @@ const ViewBec = () => { execBecView({ tenantFilter: tenantDomain, userId: userId, + userName: userName, overwrite: true, }) } @@ -303,9 +322,9 @@ const ViewBec = () => { data={alerts.SuspectUserDevices} striped responsive={true} - tableProps={{ subHeaderComponent: false, pagination: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-user-devices" /> )} @@ -321,8 +340,8 @@ const ViewBec = () => { data={alerts.NewRules} striped responsive={true} - tableProps={{ subHeaderComponent: false }} - reportName="none" + isModal={true} + reportName="bec-inbox-rules" /> )} @@ -337,8 +356,8 @@ const ViewBec = () => { data={alerts.LastSuspectUserLogon} striped responsive={true} - tableProps={{ subHeaderComponent: false }} - reportName="none" + isModal={true} + reportName="bec-suspect-user-logons" /> )} @@ -354,9 +373,9 @@ const ViewBec = () => { data={alerts.NewUsers} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-new-users" /> )} @@ -372,9 +391,9 @@ const ViewBec = () => { data={alerts.ChangedPasswords} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-changed-passwords" /> )} @@ -390,9 +409,9 @@ const ViewBec = () => { data={alerts.MailboxPermissionChanges} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-mailbox-permission-changes" /> )} @@ -408,9 +427,9 @@ const ViewBec = () => { data={alerts.AddedApps} striped responsive={true} - tableProps={{ subHeaderComponent: false }} wrapperClasses="table-responsive" - reportName="none" + isModal={true} + reportName="bec-added-apps" /> )} @@ -426,7 +445,7 @@ const ViewBec = () => { data={alerts.SuspectUserMailboxLogons} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" reportName="none" /> diff --git a/src/views/identity/administration/ViewUser.jsx b/src/views/identity/administration/ViewUser.jsx index 72341f5cda9f..a7530eacfbc2 100644 --- a/src/views/identity/administration/ViewUser.jsx +++ b/src/views/identity/administration/ViewUser.jsx @@ -91,6 +91,9 @@ const ViewUser = (props) => { + {/* // TODO: Add support for displaying the result of the delete operation. Currently, the delete operation is performed but the result is not displayed anywhere but the networking tab of the dev tools in the browser. + All API code is in place and should return the needed HTTP status information. + Possibly even remove the row in the table if the delete operation was successful? -Bobby */} { name="intuneprotection" label="Intune Protection Policies" /> +

Email Security

+ +

CIPP

{

Conditional Access

-
-

Intune

+
+ +

Email Security

+ +

CIPP

diff --git a/src/views/tenant/standards/BestPracticeAnalyser.jsx b/src/views/tenant/standards/BestPracticeAnalyser.jsx index a0e1cb00b101..692c39478591 100644 --- a/src/views/tenant/standards/BestPracticeAnalyser.jsx +++ b/src/views/tenant/standards/BestPracticeAnalyser.jsx @@ -61,7 +61,7 @@ const RefreshAction = ({ singleTenant = false, refreshFunction = null }) => { ), onConfirm: () => execBestPracticeAnalyser({ - path: 'api/BestPracticeAnalyser_OrchestrationStarter', + path: 'api/ExecBPA', params: params, }), }) diff --git a/version_latest.txt b/version_latest.txt index 798e38995c4d..19b860c1872d 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.3.0 +6.4.0