From 34360562edaec7cf90d56d696d01a4be056c35a1 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Sun, 2 Apr 2023 20:32:44 +0200 Subject: [PATCH 01/24] Changed mention of templates to alerts --- src/views/tenant/administration/ListAlertsQueue.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/tenant/administration/ListAlertsQueue.js b/src/views/tenant/administration/ListAlertsQueue.js index 4d6bf2e72e8f..485c11b5444c 100644 --- a/src/views/tenant/administration/ListAlertsQueue.js +++ b/src/views/tenant/administration/ListAlertsQueue.js @@ -167,11 +167,11 @@ const ListAlertsQueue = () => { selectableRows: true, actionsList: [ { - label: 'Delete Template', + label: 'Delete alerts', color: 'info', modal: true, modalUrl: `/api/RemoveQueuedAlert?ID=!tenantId`, - modalMessage: 'Are you sure you want to delete these templates?', + modalMessage: 'Are you sure you want to delete these alerts?', }, ], }, From 7925bba4076f901997967ec50c7270aad1738707 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Tue, 4 Apr 2023 10:20:22 +0100 Subject: [PATCH 02/24] Added Ability to disable MFA Nudges Standard Added Ability to disable MFA Nudges Standard @KelvinTegelaar please can you fix the output at the end of applying the standard --- src/data/standards.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/data/standards.json b/src/data/standards.json index 72b083fb4aaa..450f2cbc123f 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -161,11 +161,18 @@ }, { "cat": "AAD", - "name": "standards.NudgeMFA", + "name": "standards.NudgeMFA.enable", "helpText": "This is the default helptext", "addedComponent": null, "label": "Request to setup Authenticator if not setup yet." }, + { + "cat": "AAD", + "name": "standards.NudgeMFA.disable", + "helpText": "This is the default helptext", + "addedComponent": null, + "label": "Disables the request to setup Authenticator if setup." + }, { "cat": "AAD", "name": "standards.DisableSelfServiceLicenses", From e7187660af96e9deeda19ca7c16afd02ee8a2264 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 7 Apr 2023 21:01:55 -0400 Subject: [PATCH 03/24] GDAP Role Wizard Break out GDAP Role to Group mapping to separate wizard --- src/_nav.js | 10 ++ src/adminRoutes.js | 12 ++ .../tenant/administration/GDAPRoleWizard.js | 139 ++++++++++++++++++ src/views/tenant/administration/GDAPWizard.js | 16 +- .../tenant/administration/ListGDAPRoles.js | 38 +++++ 5 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 src/views/tenant/administration/GDAPRoleWizard.js create mode 100644 src/views/tenant/administration/ListGDAPRoles.js diff --git a/src/_nav.js b/src/_nav.js index e5a5d4f3f548..2f6644b6ac11 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -649,6 +649,16 @@ const _nav = [ to: '/cipp/gdap', icon: , items: [ + { + component: CNavItem, + name: 'Role Wizard', + to: '/tenant/administration/gdap-role-wizard', + }, + { + component: CNavItem, + name: 'GDAP Roles', + to: '/tenant/administration/gdap-roles', + }, { component: CNavItem, name: 'Migration Wizard', diff --git a/src/adminRoutes.js b/src/adminRoutes.js index fab00d4080e5..e33468c53952 100644 --- a/src/adminRoutes.js +++ b/src/adminRoutes.js @@ -4,6 +4,8 @@ const Setup = React.lazy(() => import('src/views/cipp/Setup')) const ApplyStandard = React.lazy(() => import('src/views/tenant/standards/ApplyStandard')) const GDAPStatus = React.lazy(() => import('src/views/tenant/administration/ListGDAPQueue')) const GDAP = React.lazy(() => import('src/views/tenant/administration/GDAPWizard')) +const GDAPRoleWizard = React.lazy(() => import('src/views/tenant/administration/GDAPRoleWizard')) +const GDAPRoles = React.lazy(() => import('src/views/tenant/administration/ListGDAPRoles')) const appapproval = React.lazy(() => import('src/views/cipp/AppApproval')) const adminRoutes = [ @@ -12,6 +14,16 @@ const adminRoutes = [ { path: '/cipp/settings', name: 'Settings', component: CIPPSettings }, { path: '/cipp/setup', name: 'Setup', component: Setup }, { path: '/tenant/administration/gdap', name: 'GDAP Wizard', component: GDAP }, + { + path: '/tenant/administration/gdap-role-wizard', + name: 'GDAP Role Wizard', + component: GDAPRoleWizard, + }, + { + path: '/tenant/administration/gdap-roles', + name: 'GDAP Roles', + component: GDAPRoles, + }, { path: '/tenant/administration/appapproval', name: 'App Approval', component: appapproval }, { path: '/tenant/administration/gdap-status', name: 'GDAP Status', component: GDAPStatus }, { path: '/tenant/standards/apply-standard', name: 'Apply Standard', component: ApplyStandard }, diff --git a/src/views/tenant/administration/GDAPRoleWizard.js b/src/views/tenant/administration/GDAPRoleWizard.js new file mode 100644 index 000000000000..ec44a8c5ab50 --- /dev/null +++ b/src/views/tenant/administration/GDAPRoleWizard.js @@ -0,0 +1,139 @@ +import React from 'react' +import { CCol, CRow, CForm, CCallout, CSpinner, CButton } from '@coreui/react' +import { Field, FormSpy } from 'react-final-form' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' +import { CippWizard } from 'src/components/layout' +import { WizardTableField } from 'src/components/tables' +import PropTypes from 'prop-types' +import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' + +const Error = ({ name }) => ( + + touched && error ? ( + + + {error} + + ) : null + } + /> +) + +Error.propTypes = { + name: PropTypes.string.isRequired, +} + +const requiredArray = (value) => (value && value.length !== 0 ? undefined : 'Required') + +const GDAPRoleWizard = () => { + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() + + const handleSubmit = async (values) => { + genericPostRequest({ path: '/api/ExecAddGDAPRole', values: values }) + } + + const formValues = {} + + return ( + + +
+

Step 1

+
+ Select which roles you want to map to Groups in your Partner Tenant +
+
+
+ + + For each role you select a new group will be created inside of your partner tenant + called "M365 GDAP RoleName". Add your users to these new groups to set their GDAP + permissions. + + + {(props) => ( + row['Name'], + sortable: true, + exportselector: 'Name', + }, + { + name: 'Description', + selector: (row) => row['Description'], + sortable: true, + }, + ]} + fieldProps={props} + /> + )} + + + +
+
+ +
+

Step 2

+
Confirm and apply
+
+
+ {!postResults.isSuccess && ( + + {(props) => { + return ( + <> + + + +
Roles and group names
+ + {props.values.gdapRoles.map((role, idx) => ( +
  • + {role.Name} - M365 GDAP {role.Name} +
  • + ))} +
    +
    +
    + + ) + }} +
    + )} + {postResults.isFetching && ( + + Loading + + )} + {postResults.isSuccess && ( + + {postResults.data.Results.map((message, idx) => { + return
  • {message}
  • + })} +
    + )} +
    +
    +
    + ) +} + +export default GDAPRoleWizard diff --git a/src/views/tenant/administration/GDAPWizard.js b/src/views/tenant/administration/GDAPWizard.js index 6ccac1b57599..adbfa782ed43 100644 --- a/src/views/tenant/administration/GDAPWizard.js +++ b/src/views/tenant/administration/GDAPWizard.js @@ -120,7 +120,7 @@ const GDAPWizard = () => {

    Step 3

    @@ -131,10 +131,6 @@ const GDAPWizard = () => {
    - For each role you select a new group will be created inside of your partner tenant - called "M365 GDAP RoleName". Add your users to these new groups to set their GDAP - permissions. -

    CIPP will create a single relationship with all roles you've selected for the maximum duration of 730 days using a GUID as a random name for the relationship.
    It is recommend to put CIPP user in the correct GDAP Role Groups to manage your @@ -145,17 +141,17 @@ const GDAPWizard = () => { row['Name'], + selector: (row) => row['RoleName'], sortable: true, exportselector: 'Name', }, { - name: 'Description', - selector: (row) => row['Description'], + name: 'Group', + selector: (row) => row['GroupName'], sortable: true, }, ]} @@ -193,7 +189,7 @@ const GDAPWizard = () => { {props.values.gdapRoles.map((role, idx) => (
  • - {role.Name} - M365 GDAP {role.Name} + {role.RoleName} - {role.GroupName}
  • ))}
    diff --git a/src/views/tenant/administration/ListGDAPRoles.js b/src/views/tenant/administration/ListGDAPRoles.js new file mode 100644 index 000000000000..e3e953e13352 --- /dev/null +++ b/src/views/tenant/administration/ListGDAPRoles.js @@ -0,0 +1,38 @@ +import React from 'react' +import { useSelector } from 'react-redux' +import { CSpinner, CCallout } from '@coreui/react' +import { CippPageList } from 'src/components/layout' + +const ListGDAPRoles = () => { + const columns = [ + { + name: 'Role', + selector: (row) => row['RoleName'], + sortable: true, + exportSelector: 'RoleName', + }, + { + name: 'Group', + selector: (row) => row['GroupName'], + sortable: true, + exportSelector: 'GroupName', + }, + ] + return ( +
    + +
    + ) +} + +export default ListGDAPRoles From ebd1268265d29032df07747cbe08cc42ff4d2bb4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sat, 8 Apr 2023 07:46:35 -0400 Subject: [PATCH 04/24] Reorder GDAP wizard steps Put role selection before tenants --- src/views/tenant/administration/GDAPWizard.js | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/views/tenant/administration/GDAPWizard.js b/src/views/tenant/administration/GDAPWizard.js index adbfa782ed43..708666bce6cd 100644 --- a/src/views/tenant/administration/GDAPWizard.js +++ b/src/views/tenant/administration/GDAPWizard.js @@ -45,7 +45,7 @@ const GDAPWizard = () => { onSubmit={handleSubmit} wizardTitle="GDAP Migration Wizard" > - +

    Step 1

    Setup GDAP Migration tool
    @@ -82,48 +82,13 @@ const GDAPWizard = () => { )}
    - -
    -

    Step 2

    -
    Choose a tenant
    -
    -
    - - {(props) => ( - row['displayName'], - sortable: true, - exportselector: 'displayName', - }, - { - name: 'Default Domain Name', - selector: (row) => row['defaultDomainName'], - sortable: true, - exportselector: 'mail', - }, - ]} - fieldProps={props} - /> - )} - - -
    -
    +
    -

    Step 3

    +

    Step 2

    Select which roles you want to add to GDAP relationship.
    @@ -163,6 +128,42 @@ const GDAPWizard = () => {
    + +
    +

    Step 3

    +
    Choose a tenant
    +
    +
    + + {(props) => ( + row['displayName'], + sortable: true, + exportselector: 'displayName', + }, + { + name: 'Default Domain Name', + selector: (row) => row['defaultDomainName'], + sortable: true, + exportselector: 'mail', + }, + ]} + fieldProps={props} + /> + )} + + +
    +

    Step 4

    From 54f1009802b2eab3edc4514a2fbf7422297aa0ad Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sat, 8 Apr 2023 07:56:10 -0400 Subject: [PATCH 05/24] Add Map GDAP roles button --- src/views/tenant/administration/ListGDAPRoles.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/views/tenant/administration/ListGDAPRoles.js b/src/views/tenant/administration/ListGDAPRoles.js index e3e953e13352..f7279740b20c 100644 --- a/src/views/tenant/administration/ListGDAPRoles.js +++ b/src/views/tenant/administration/ListGDAPRoles.js @@ -2,6 +2,7 @@ import React from 'react' import { useSelector } from 'react-redux' import { CSpinner, CCallout } from '@coreui/react' import { CippPageList } from 'src/components/layout' +import { TitleButton } from 'src/components/buttons' const ListGDAPRoles = () => { const columns = [ @@ -23,6 +24,9 @@ const ListGDAPRoles = () => { + } tenantSelector={false} datatable={{ keyField: 'id', From 71119fcc993acea16c43fbc7a456358ff377428a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sat, 8 Apr 2023 17:54:28 -0400 Subject: [PATCH 06/24] Add link to Role wizard in GDAP wizard --- src/views/tenant/administration/GDAPWizard.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/views/tenant/administration/GDAPWizard.js b/src/views/tenant/administration/GDAPWizard.js index 708666bce6cd..b8c110be6c9c 100644 --- a/src/views/tenant/administration/GDAPWizard.js +++ b/src/views/tenant/administration/GDAPWizard.js @@ -5,6 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' import { CippWizard } from 'src/components/layout' import { WizardTableField } from 'src/components/tables' +import { TitleButton } from 'src/components/buttons' import PropTypes from 'prop-types' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' @@ -101,6 +102,9 @@ const GDAPWizard = () => {
    It is recommend to put CIPP user in the correct GDAP Role Groups to manage your environment secure after deployment of GDAP. +
    + +
    {(props) => ( Date: Sat, 8 Apr 2023 20:11:32 -0400 Subject: [PATCH 07/24] Job History offcanvas --- src/components/header/AppHeaderDropdown.js | 62 ++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/components/header/AppHeaderDropdown.js b/src/components/header/AppHeaderDropdown.js index bb0a0246ca72..f2baf4c25c0e 100644 --- a/src/components/header/AppHeaderDropdown.js +++ b/src/components/header/AppHeaderDropdown.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import { CAvatar, CDropdown, @@ -7,15 +7,49 @@ import { CDropdownToggle, CLink, } from '@coreui/react' -import { faUser, faBook, faSignOutAlt } from '@fortawesome/free-solid-svg-icons' +import { faUser, faBook, faSignOutAlt, faHistory, faTrash } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Link } from 'react-router-dom' import { authApi } from 'src/store/api/auth' -import { CippProfile, CippOffcanvas } from 'src/components/utilities' +import { CippProfile, CippOffcanvas, CippActionsOffcanvas } from 'src/components/utilities' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' const AppHeaderDropdown = () => { const [profileVisible, setProfileVisible] = useState(false) + const [cippQueueExtendedInfo, setCippQueueExtendedInfo] = useState([]) + const [cippQueueVisible, setCippQueueVisible] = useState(false) + const [cippQueueRefresh, setCippQueueRefresh] = useState('') const { data: profile } = authApi.endpoints.loadClientPrincipal.useQueryState() + + const [getCippQueueList, cippQueueList] = useLazyGenericGetRequestQuery() + + function loadCippQueue() { + setCippQueueVisible(true) + setCippQueueRefresh((Math.random() + 1).toString(36).substring(7)) + getCippQueueList({ path: `api/ListCippQueue?refresh=${cippQueueRefresh}` }) + } + + useEffect(() => { + if (cippQueueList.isFetching) { + setCippQueueExtendedInfo([{ label: 'Fetching recent jobs', value: 'Please wait' }]) + } + if ( + cippQueueList.isSuccess && + Array.isArray(cippQueueList.data) && + cippQueueList.data.length > 0 + ) { + setCippQueueExtendedInfo( + cippQueueList.data?.map((job) => ({ + label: `${job.Name}`, + value: job.Status, + })), + ) + } else { + setCippQueueExtendedInfo([{ label: 'No jobs to display', value: '' }]) + } + console.log(cippQueueList) + }, [cippQueueList]) + return ( <> @@ -30,6 +64,10 @@ const AppHeaderDropdown = () => { Profile + loadCippQueue()}> + + Recent Jobs + Logbook @@ -49,6 +87,24 @@ const AppHeaderDropdown = () => { > + , + }, + ]} + placement="end" + visible={cippQueueVisible} + id="cipp-queue" + hideFunction={() => setCippQueueVisible(false)} + /> ) } From 4dd059016e3e576a636c29694e60f351417ea74a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 9 Apr 2023 21:25:42 -0400 Subject: [PATCH 08/24] Add custom suffix feature --- .../tenant/administration/GDAPRoleWizard.js | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/src/views/tenant/administration/GDAPRoleWizard.js b/src/views/tenant/administration/GDAPRoleWizard.js index ec44a8c5ab50..b225d5cb0751 100644 --- a/src/views/tenant/administration/GDAPRoleWizard.js +++ b/src/views/tenant/administration/GDAPRoleWizard.js @@ -1,5 +1,5 @@ import React from 'react' -import { CCol, CRow, CForm, CCallout, CSpinner, CButton } from '@coreui/react' +import { CCol, CRow, CForm, CCallout, CSpinner } from '@coreui/react' import { Field, FormSpy } from 'react-final-form' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' @@ -7,6 +7,7 @@ import { CippWizard } from 'src/components/layout' import { WizardTableField } from 'src/components/tables' import PropTypes from 'prop-types' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { RFFCFormInput } from 'src/components/forms' const Error = ({ name }) => ( { For each role you select a new group will be created inside of your partner tenant called "M365 GDAP RoleName". Add your users to these new groups to set their GDAP - permissions. + permissions. If you need to segment your groups for different teams or to define custom + permissions, use the Custom Suffix to create additional group mappings per role. - - {(props) => ( - row['Name'], - sortable: true, - exportselector: 'Name', - }, - { - name: 'Description', - selector: (row) => row['Description'], - sortable: true, - }, - ]} - fieldProps={props} + + + - )} - + + + + + + {(props) => ( + row['Name'], + sortable: true, + exportselector: 'Name', + }, + { + name: 'Description', + selector: (row) => row['Description'], + sortable: true, + }, + ]} + fieldProps={props} + /> + )} + + +
    @@ -111,6 +127,14 @@ const GDAPRoleWizard = () => { ))} + {props.values.customSuffix != null && ( + <> +
    Custom Group Suffix
    + +
  • {props.values.customSuffix}
  • +
    + + )} From 3aa0882f9f07c1a47e0f78359af1f39256a5dba7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 10 Apr 2023 16:12:09 -0400 Subject: [PATCH 09/24] Add start gdap migration button to role wizard --- .../tenant/administration/GDAPRoleWizard.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/views/tenant/administration/GDAPRoleWizard.js b/src/views/tenant/administration/GDAPRoleWizard.js index b225d5cb0751..26c56de51a4d 100644 --- a/src/views/tenant/administration/GDAPRoleWizard.js +++ b/src/views/tenant/administration/GDAPRoleWizard.js @@ -1,5 +1,5 @@ import React from 'react' -import { CCol, CRow, CForm, CCallout, CSpinner } from '@coreui/react' +import { CCol, CRow, CForm, CCallout, CSpinner, CButton } from '@coreui/react' import { Field, FormSpy } from 'react-final-form' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' @@ -8,6 +8,7 @@ import { WizardTableField } from 'src/components/tables' import PropTypes from 'prop-types' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { RFFCFormInput } from 'src/components/forms' +import { Link } from 'react-router-dom' const Error = ({ name }) => ( { )} {postResults.isSuccess && ( - - {postResults.data.Results.map((message, idx) => { - return
  • {message}
  • - })} -
    + <> + + {postResults.data.Results.map((message, idx) => { + return
  • {message}
  • + })} +
    + + Start GDAP Migration + + )}
    From 5f80792de0db85fc39ecdb02a1144fd8fb61bb4c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:35:28 +0200 Subject: [PATCH 10/24] updated text. --- src/data/standards.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/standards.json b/src/data/standards.json index 450f2cbc123f..65db91dab662 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -150,7 +150,7 @@ "name": "standards.LegacyMFA", "helpText": "This is the default helptext", "addedComponent": null, - "label": "Enable per-user MFA for all user (Legacy)" + "label": "Enable per-user MFA for all user (Legacy, Requires DAP.)" }, { "cat": "AAD", From c1b7a4d77970f9b94a840ac377fc1d148e7bc00b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 17 Apr 2023 14:53:05 +0200 Subject: [PATCH 11/24] added execution of current file as priority for local dev. --- .vscode/launch.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index fcd6fc8d6c56..e8f4c409c8fb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,13 @@ { "version": "0.2.0", "configurations": [ + { + "name": "PowerShell: Execute current file", + "type": "PowerShell", + "request": "launch", + "script": "${file}", + "cwd": "${file}" + }, { "command": "npm run start-api", "name": "Run emulator", From e7fdcb2af057e40467c52139457a1455bca3bcfe Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Mon, 17 Apr 2023 22:39:16 +0200 Subject: [PATCH 12/24] modern readme --- README.md | 142 +----------------------------------------------------- 1 file changed, 1 insertion(+), 141 deletions(-) diff --git a/README.md b/README.md index 9e42be1b45e1..7cbd7845e426 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@

    ![OIT](github_assets/img/oitpsonsor_light.png)     -![Genuine Technology Services](github_assets/img/Genuine-logo-vertical-light.png)     ![Immybot](github_assets/img/Immybot.png)     ![NinjaOne](github_assets/img/NinjaOne-Light.png#gh-dark-mode-only) ![NinjaOne](github_assets/img/NinjaOne-Dark.png#gh-light-mode-only)     @@ -31,143 +30,4 @@ The CyberDrain Improved Partner Portal is a portal to help manage administration This project is a way to help you with administration, with user management, and deploying your own preferred standards. It's not a replacement for security tools, or a way to cut costs on specific subscriptions. The tool should assist you in removing the gripes with standard partner management and save you several hours per engineer per month. -# Deployment and Getting Started - -If you want to self-host, check out the installation manual [here](https://cipp.app/GettingStarted/Installation/). You will need some knowledge of Static Web Apps, Azure Functions, and Azure Keyvault - -# Why are you making this? - -I'm kind of done waiting for vendors to catch up to what we actually need. All RMM vendors are dramaticaly slow adopting cloud management. Microsoft themselves don't understand the Managed services markets, there are vendors that have tried jumping into the gap but either have unreasonable fees, weird constructions, require Global Admins without MFA, or just don't innovate at a pace that is required of cloud services right now. - -I'm also annoyed the untransparent behaviour that many companies in our market are showing. Most are claiming that working with the Microsoft Partner APIs is difficult, and requires a very heavy development team. I'm a guy that had no webdesign knowledge before this and created the first release of this app in 3 weekends. Vendors that claim high difficulty or issues with integration are simply not giving this _any_ priority. - -I was recently on a call with one of my friends and he said he was changing the world. That insipred me to change the world just a little bit too. :) I'm hoping that this is one of the tools that make you smile. - -# What's the pricing? - -This project is **FREE** but we do have a **Sponsorware** component. The sponsorware structure for this project is pretty simple; the code is available to everyone and free to use. You will need some technical know-how to put it all together. Sponsors receive the following benefits - -### For users of the project that sponsor: - -- The project will be hosted for you. -- The hosted version will always be the latest release and automatically updated. -- You'll also receive a staging environment with the latest (nightly/beta) build, to see new features before anyone else. -- You will receive priority on support issues reported on GitHub. -- You will be able to make 1 priortized feature request per month. - -Sponsorship allows me to sink some more time into this project and keep it free, so please consider it. :) - -### For company sponsors, depending on sponsor level you can get the following benefits; - -- Your company logo will be featured on this readme page at the top. -- Your company logo will be featured on https://cyberdrain.com -- A small version of your company logo with a link to your homepage will be on the footer, each user will see this on each page. - -# How does it look?! - -Check out the GIFs below to see how some of the workflows work. - - - - - - - - - -# What is the functionality? - -The current build functionality is described below, also check out our Changelog in the documentation folder, as the tool has a very rapid development schedule the list below might be out of date. - -## Identity Management - -- Manage M365 users - - List users, email addreses, and licenses. - - View & Edit user settings - - Research if user has been compromised - - Send user an MFA push to confirm their identity - - Convert a user to a shared mailbox - - Block signin, reset passwords - - Delete users -- Manage M365 groups - - List all M365 groups, group types, and e-mail addresses. - - Edit members and group owners -- Offboard users via an easy wizard - - Remove user licenses - - Convert user to shared mailbox - - Disable user sign-in - - Reset user password - - Remove user from all groups - - Hide user from address list - - Set Out of Office - - Give other user access to mailbox, and OneDrive - -## Tenants - -- Manage M365 tenants - - List all tenants and quick-links to the most user portals using delegated access. - - Edit Partner tenant names and default domain for your CSP partner environment - - List tenant conditional access policies - - Apply standard configuration to tenant on a repeat schedule. - - Execute a best practice analysis daily and report on best practice settings - - Analyse current domains, and domains outside of M365 for optimal security settings - - List alerts for tenants - -## Endpoint Management - -- Applications - - List all applications in tenants - - List installation status of a specific application per device - - Add Office Apps to multiple tenants - - Add/Remove Chocolatey Apps to multiple tenants - - Assign Apps to All Devices or All Users - - Report on installation status -- Autopilot - - Manage and create autopilot devices, profiles, status pages. -- Intune - - List Intune policies - - Apply Intune Policies - - Add Intune Policy Templates to deploy over multiple tenants - -## Teams & SharePoint - -- List OneDrive, Teams, and SharePoint usage -- View current Teams, installed applications, Team owners, members, and channels -- Add and edit Teams, members, owners and apps. -- Tenant Alerting - -## Exchange - -- View mailboxes and contacts -- View user mobile devices -- Convert mailboxes to shared or user mailboxes -- Report mailbox statistics, client access settings -- Perform message traces -- Change and view phishing policies. - -## Application settings - -- Use multiple user levels (readonly, editor, admin) to manage access -- Allow excluding of tenants -- Send automated alert emails to webhook or e-mail - -# Security - -Authentication is handled by Azure AD using static web apps security. This means the API is only reachable for authenticated users you've invited. For most of the security info related to that check out our staticwebapp.config.json and/or the doc pages on static web apps. Do you see something that might be a security risk, even the smallest? report it and we will handle it asap. Check out our security reporting options [here](https://github.com/KelvinTegelaar/CIPP/security) - -# Contributions - -Feel free to send pull requests or fill out issues when you encounter them, sponsors get a priority on issues and bugs. I'm also completely open to adding direct maintainers/contributors and working together. - -If you decide to contribute; remember that keeping the portal fast is a key component. CIPP is supposed to go brrrrr, any improvements that help with speed are welcomed. - -## Special thanks - -I'd like to give special thanks to the people that made this project possible; - -- [Kyle Hanslovan](https://huntress.com) -- [Ray Orsini](https://oit.co) -- The Team at [MSP.zone/MSP'R'Us](https://msp.zone) -- Gavin Stone at [MSPGeek](https://mspgeek.org) -- MSP2.0 for helping with some visual input. -- Scott, Chris, Jon, and others that helped me with some of the internals of the app. +for more information, we recommend checking out our website [here](https://cipp.app) From 93d8f3cebac72c2a81d569eaf0ece2618b46c2f6 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Tue, 18 Apr 2023 14:17:57 +0100 Subject: [PATCH 13/24] Fixed Device Compliance, and added ability to subscriber users to Office 365 Groups Fixed Device Compliance, and added ability to subscriber users to Office 365 Groups --- src/components/tables/CellBoolean.js | 6 ++++-- src/views/identity/administration/EditGroup.js | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/tables/CellBoolean.js b/src/components/tables/CellBoolean.js index 30962b8ec8aa..98f69dfa1adc 100644 --- a/src/components/tables/CellBoolean.js +++ b/src/components/tables/CellBoolean.js @@ -44,13 +44,15 @@ export default function CellBoolean({ cell.toLowerCase() === 'success' || cell.toLowerCase() === 'enabled' || cell.toLowerCase() === 'pass' || - cell.toLowerCase() === 'true' + cell.toLowerCase() === 'true' || + cell.toLowerCase() === 'compliant' ) { normalized = true } else if ( cell.toLowerCase() === 'fail' || cell.toLowerCase() === 'default' || - cell.toLowerCase() === 'false' + cell.toLowerCase() === 'false' || + cell.toLowerCase() === 'noncompliant' ) { normalized = false } diff --git a/src/views/identity/administration/EditGroup.js b/src/views/identity/administration/EditGroup.js index 796992213c1a..bc27feebc675 100644 --- a/src/views/identity/administration/EditGroup.js +++ b/src/views/identity/administration/EditGroup.js @@ -110,6 +110,8 @@ const EditGroup = () => { AddContacts: values.AddContacts ? values.AddContacts : '', RemoveContacts: values.RemoveContacts ? values.RemoveContacts : '', allowExternal: values.allowExternal, + sendCopies: values.sendCopies, + mail: group[0].mail, } //window.alert(JSON.stringify(shippedValues)) genericPostRequest({ path: '/api/EditGroup', values: shippedValues }) @@ -235,6 +237,12 @@ const EditGroup = () => { label="Let people outside the organization email the group" /> )} + {group[0].calculatedGroupType === 'Microsoft 365' && ( + + )} From 38d806d2b7ef79f1909f6dc399545a4afebf96d9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 18 Apr 2023 19:32:01 +0200 Subject: [PATCH 14/24] Update README.md --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 9e42be1b45e1..6b350fa81646 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,6 @@ ![CyberDrain Light](github_assets/img/CIPP.png#gh-dark-mode-only) ![CyberDrain Dark](github_assets/img/CIPP-Light.png#gh-light-mode-only) -


    - -[![GitHub Latest Release](https://img.shields.io/github/v/release/KelvinTegelaar/CIPP?label=Latest%20Release&style=for-the-badge)](https://github.com/KelvinTegelaar/CIPP/releases) -![CodeQL Security Analysis Status](https://img.shields.io/github/workflow/status/KelvinTegelaar/CIPP/CodeQL?label=CodeQL%20Security&style=for-the-badge) -[![GitHub Enhancement Requests](https://img.shields.io/github/issues/KelvinTegelaar/CIPP/enhancement?label=Enhancement%20Requests&style=for-the-badge)](https://github.com/KelvinTegelaar/CIPP/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement) -[![GitHub Bugs](https://img.shields.io/github/issues/KelvinTegelaar/CIPP/bug?label=Bugs&style=for-the-badge)](https://github.com/KelvinTegelaar/CIPP/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement+label%3Abug) -[![Discord](https://img.shields.io/discord/905453405936447518?label=Discord&style=for-the-badge)](https://discord.com/invite/cyberdrain) -[![GitHub Sponsors](https://img.shields.io/github/sponsors/KelvinTegelaar?label=Public%20Sponsors&style=for-the-badge)](https://github.com/sponsors/KelvinTegelaar) - -
    -

    Sponsored by

    From 99579683f35b2f1522935709fcc6b0b3cfc1541a Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Thu, 20 Apr 2023 10:02:39 +0100 Subject: [PATCH 15/24] Added New Functionality MobileDeviceList Page Added New Functionality Mobile Device List Page Allow Device Block Device Remove Device --- .../administration/ViewMobileDevices.js | 251 ++++++++++++------ 1 file changed, 173 insertions(+), 78 deletions(-) diff --git a/src/views/email-exchange/administration/ViewMobileDevices.js b/src/views/email-exchange/administration/ViewMobileDevices.js index 71e410efbb7e..f257c64bdb5f 100644 --- a/src/views/email-exchange/administration/ViewMobileDevices.js +++ b/src/views/email-exchange/administration/ViewMobileDevices.js @@ -1,89 +1,184 @@ -import React from 'react' +import React, { useState } from 'react' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import useQuery from 'src/hooks/useQuery' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { CellTip, cellDateFormatter } from 'src/components/tables' +import { faEye, faEdit, faEllipsisV, faMobileAlt } from '@fortawesome/free-solid-svg-icons' +import { CippActionsOffcanvas } from 'src/components/utilities' +import { Link } from 'react-router-dom' +import { CButton } from '@coreui/react' //TODO: Add CellBoolean -const columns = [ - { - selector: (row) => row['clientType'], - name: 'Client Type', - sortable: true, - cell: (row) => CellTip(row['clientType']), - exportSelector: 'clientType', - }, - { - selector: (row) => row['clientVersion'], - name: 'Client Version', - sortable: true, - exportSelector: 'clientVersion', - }, - { - selector: (row) => row['deviceAccessState'], - name: 'Access State', - sortable: true, - exportSelector: 'deviceAccessState', - }, - { - selector: (row) => row['deviceFriendlyName'], - name: 'Friendly Name', - sortable: true, - cell: (row) => CellTip(row['deviceFriendlyName']), - exportSelector: 'deviceFriendlyName', - }, - { - selector: (row) => row['deviceModel'], - name: 'Model', - sortable: true, - cell: (row) => CellTip(row['deviceModel']), - exportSelector: 'deviceModel', - }, - { - selector: (row) => row['deviceOS'], - name: 'OS', - sortable: true, - cell: (row) => CellTip(row['deviceOS']), - exportSelector: 'deviceOS', - }, - { - selector: (row) => row['deviceType'], - name: 'Device Type', - sortable: true, - cell: (row) => CellTip(row['deviceType']), - exportSelector: 'deviceType', - }, - { - selector: (row) => row['firstSync'], - name: 'First Sync', - sortable: true, - exportSelector: 'firstSync', - cell: cellDateFormatter(), - }, - { - selector: (row) => row['lastSyncAttempt'], - name: 'Last Sync Attempt', - sortable: true, - exportSelector: 'lastSyncAttempt', - cell: cellDateFormatter(), - }, - { - selector: (row) => row['lastSuccessSync'], - name: 'Last Succesfull Sync', - sortable: true, - exportSelector: 'lastSuccessSync', - cell: cellDateFormatter(), - }, - { - selector: (row) => row['status'], - name: 'Status', - sortable: true, - exportSelector: 'status', - }, -] const MobileDeviceList = () => { const tenant = useSelector((state) => state.app.currentTenant) + + const Offcanvas = (row, rowIndex, formatExtraData) => { + const [ocVisible, setOCVisible] = useState(false) + return ( + <> + setOCVisible(true)}> + + + setOCVisible(false)} + /> + + ) + } + + const columns = [ + { + selector: (row) => query.get('userId'), + name: 'User ID', + sortable: true, + cell: (row) => CellTip(query.get('userId')), + exportSelector: 'userId', + }, + { + selector: (row) => row['clientType'], + name: 'Client Type', + sortable: true, + cell: (row) => CellTip(row['clientType']), + exportSelector: 'clientType', + }, + { + selector: (row) => row['clientVersion'], + name: 'Client Version', + sortable: true, + exportSelector: 'clientVersion', + }, + { + selector: (row) => row['deviceAccessState'], + name: 'Access State', + sortable: true, + exportSelector: 'deviceAccessState', + }, + { + selector: (row) => row['deviceFriendlyName'], + name: 'Friendly Name', + sortable: true, + cell: (row) => CellTip(row['deviceFriendlyName']), + exportSelector: 'deviceFriendlyName', + }, + { + selector: (row) => row['deviceModel'], + name: 'Model', + sortable: true, + cell: (row) => CellTip(row['deviceModel']), + exportSelector: 'deviceModel', + }, + { + selector: (row) => row['deviceOS'], + name: 'OS', + sortable: true, + cell: (row) => CellTip(row['deviceOS']), + exportSelector: 'deviceOS', + }, + { + selector: (row) => row['deviceType'], + name: 'Device Type', + sortable: true, + cell: (row) => CellTip(row['deviceType']), + exportSelector: 'deviceType', + }, + { + selector: (row) => row['deviceID'], + name: 'Device ID', + sortable: true, + exportSelector: 'deviceID', + cell: cellDateFormatter(), + }, + { + selector: (row) => row['firstSync'], + name: 'First Sync', + sortable: true, + exportSelector: 'firstSync', + cell: cellDateFormatter(), + }, + { + selector: (row) => row['lastSyncAttempt'], + name: 'Last Sync Attempt', + sortable: true, + exportSelector: 'lastSyncAttempt', + cell: cellDateFormatter(), + }, + { + selector: (row) => row['lastSuccessSync'], + name: 'Last Succesfull Sync', + sortable: true, + exportSelector: 'lastSuccessSync', + cell: cellDateFormatter(), + }, + { + selector: (row) => row['status'], + name: 'Status', + sortable: true, + exportSelector: 'status', + }, + { + selector: (row) => row['Guid'], + name: 'Guid', + sortable: true, + exportSelector: 'Guid', + }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '75px', + }, + ] + let query = useQuery() const userId = query.get('userId') return ( @@ -96,9 +191,9 @@ const MobileDeviceList = () => { path: '/api/ListMailboxMobileDevices', columns, params: { TenantFilter: tenant?.defaultDomainName, mailbox: userId }, + selectableRows: true, }} /> ) } - export default MobileDeviceList From f279c795ec98c28c7373dc939dc7589d2508d7b1 Mon Sep 17 00:00:00 2001 From: BNWEIN Date: Thu, 20 Apr 2023 17:54:29 +0100 Subject: [PATCH 16/24] Added "Enable Online Archive" To user Actions Added "Enable Online Archive" To user Actions --- src/views/identity/administration/UserActions.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/views/identity/administration/UserActions.js b/src/views/identity/administration/UserActions.js index 4bb0d3818e35..78ca9e360fe8 100644 --- a/src/views/identity/administration/UserActions.js +++ b/src/views/identity/administration/UserActions.js @@ -8,6 +8,7 @@ import { faLockOpen, faUserTimes, faEllipsisH, + faEnvelope, } from '@fortawesome/free-solid-svg-icons' import { ActionContentCard } from 'src/components/contentcards' import { useLazyGenericGetRequestQuery } from 'src/store/api/app' @@ -46,6 +47,16 @@ export default function UserActions({ tenantDomain, userId, userEmail, className `/api/ExecSendPush?TenantFilter=${tenantDomain}&UserEmail=${userEmail}`, ), }, + { + label: 'Enable Online Archive ', + link: '#', + icon: faEnvelope, + onClick: () => + handleModal( + 'Are you sure you want to enable the online archive for this user?', + `/api/ExecEnableArchive?TenantFilter=${tenantDomain}&ID=${userEmail}`, + ), + }, { label: 'Convert to Shared Mailbox', link: '#', From a3074058188d387f0fb90a6c01a5f0e3df7ee335 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 20 Apr 2023 20:22:33 +0200 Subject: [PATCH 17/24] added all tenants --- src/views/email-exchange/reports/MailboxStatisticsList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/email-exchange/reports/MailboxStatisticsList.js b/src/views/email-exchange/reports/MailboxStatisticsList.js index 5a4432e026c2..6d80e5c3d569 100644 --- a/src/views/email-exchange/reports/MailboxStatisticsList.js +++ b/src/views/email-exchange/reports/MailboxStatisticsList.js @@ -75,6 +75,7 @@ const MailboxStatsList = () => { return ( Date: Thu, 20 Apr 2023 20:52:25 +0200 Subject: [PATCH 18/24] added allusers --- src/views/identity/administration/Users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/identity/administration/Users.js b/src/views/identity/administration/Users.js index 3b7a0e3a04e1..993dc433638f 100644 --- a/src/views/identity/administration/Users.js +++ b/src/views/identity/administration/Users.js @@ -276,7 +276,7 @@ const Users = (row) => { ) return ( Date: Thu, 20 Apr 2023 22:32:20 +0200 Subject: [PATCH 19/24] updates to cards for queues --- src/components/header/AppHeaderDropdown.js | 5 ++- src/components/tables/CippOffcanvasTable.js | 2 +- .../utilities/CippActionsOffcanvas.js | 32 +++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/components/header/AppHeaderDropdown.js b/src/components/header/AppHeaderDropdown.js index f2baf4c25c0e..e6589af9697b 100644 --- a/src/components/header/AppHeaderDropdown.js +++ b/src/components/header/AppHeaderDropdown.js @@ -42,6 +42,8 @@ const AppHeaderDropdown = () => { cippQueueList.data?.map((job) => ({ label: `${job.Name}`, value: job.Status, + link: job.Link, + timestamp: job.Timestamp, })), ) } else { @@ -89,7 +91,8 @@ const AppHeaderDropdown = () => { ( + <> + + + Report Name: {action.label} + + Status: {action.value} + + + Generated on {action.timestamp.split('T')[0]} at {action.timestamp.split('T')[1]} + + + + + )) + } catch (error) { + console.error('An error occored building OCanvas actions' + error.toString()) + } + const extendedInfoContent = let actionsContent try { @@ -176,7 +202,9 @@ export default function CippActionsOffcanvas(props) { {getResults.isError && ( Could not connect to API: {getResults.error.message} )} + Extended Information + {cardContent && cardContent} {extendedInfoContent} {Actions} @@ -193,7 +221,7 @@ const CippActionsOffcanvasPropTypes = { label: PropTypes.string, value: PropTypes.any, }), - ).isRequired, + ), actions: PropTypes.arrayOf( PropTypes.shape({ label: PropTypes.string, From 020c2f5895e87b0321eed1d691b3b2b4b865e78d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 20 Apr 2023 23:10:21 +0200 Subject: [PATCH 20/24] added visible tenants --- src/views/identity/administration/Users.js | 147 ++++++++++++--------- 1 file changed, 86 insertions(+), 61 deletions(-) diff --git a/src/views/identity/administration/Users.js b/src/views/identity/administration/Users.js index 993dc433638f..ca19f07ef253 100644 --- a/src/views/identity/administration/Users.js +++ b/src/views/identity/administration/Users.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { CButton } from '@coreui/react' import { Link } from 'react-router-dom' import { useSelector } from 'react-redux' @@ -200,68 +200,93 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { ) } -const columns = [ - { - name: 'Display Name', - selector: (row) => row['displayName'], - sortable: true, - cell: (row) => CellTip(row['displayName']), - exportSelector: 'displayName', - minWidth: '300px', - }, - { - name: 'Email', - selector: (row) => row['mail'], - sortable: true, - cell: (row) => CellTip(row['mail']), - exportSelector: 'mail', - minWidth: '250px', - }, - { - name: 'User Type', - selector: (row) => row['userType'], - sortable: true, - exportSelector: 'userType', - minWidth: '140px', - }, - { - name: 'Enabled', - selector: (row) => row['accountEnabled'], - cell: cellBooleanFormatter({ colourless: true }), - sortable: true, - exportSelector: 'accountEnabled', - minWidth: '100px', - }, - { - name: 'AD Synced', - selector: (row) => row['onPremisesSyncEnabled'], - cell: cellBooleanFormatter({ colourless: true }), - sortable: true, - exportSelector: 'onPremisesSyncEnabled', - minWidth: '120px', - }, - { - name: 'Licenses', - selector: (row) => row['LicJoined'], - exportSelector: 'LicJoined', - sortable: true, - grow: 5, - wrap: true, - minWidth: '200px', - }, - { - name: 'id', - selector: (row) => row['id'], - omit: true, - }, - { - name: 'Actions', - cell: Offcanvas, - }, -] - const Users = (row) => { + const [tenantColumnSet, setTenantColumn] = useState(true) + const columns = [ + { + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + cell: (row) => CellTip(row['Tenant']), + exportSelector: 'Tenant', + omit: tenantColumnSet, + }, + { + name: 'Retrieval Status', + selector: (row) => row['CippStatus'], + sortable: true, + cell: (row) => CellTip(row['CippStatus']), + exportSelector: 'CippStatus', + omit: tenantColumnSet, + }, + { + name: 'Display Name', + selector: (row) => row['displayName'], + sortable: true, + cell: (row) => CellTip(row['displayName']), + exportSelector: 'displayName', + minWidth: '300px', + }, + { + name: 'Email', + selector: (row) => row['mail'], + sortable: true, + cell: (row) => CellTip(row['mail']), + exportSelector: 'mail', + minWidth: '250px', + }, + { + name: 'User Type', + selector: (row) => row['userType'], + sortable: true, + exportSelector: 'userType', + minWidth: '140px', + }, + { + name: 'Enabled', + selector: (row) => row['accountEnabled'], + cell: cellBooleanFormatter({ colourless: true }), + sortable: true, + exportSelector: 'accountEnabled', + minWidth: '100px', + }, + { + name: 'AD Synced', + selector: (row) => row['onPremisesSyncEnabled'], + cell: cellBooleanFormatter({ colourless: true }), + sortable: true, + exportSelector: 'onPremisesSyncEnabled', + minWidth: '120px', + }, + { + name: 'Licenses', + selector: (row) => row['LicJoined'], + exportSelector: 'LicJoined', + sortable: true, + grow: 5, + wrap: true, + minWidth: '200px', + }, + { + name: 'id', + selector: (row) => row['id'], + omit: true, + }, + { + name: 'Actions', + cell: Offcanvas, + }, + ] const tenant = useSelector((state) => state.app.currentTenant) + useEffect(() => { + if (tenant.defaultDomainName === 'AllTenants') { + setTenantColumn(false) + } + if (tenant.defaultDomainName !== 'AllTenants') { + setTenantColumn(true) + } + }, [tenantColumnSet]) + const titleButtons = (

    From 07b8ae49714f86639ad47b8d2d8015a066a9399b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 20 Apr 2023 21:32:55 -0400 Subject: [PATCH 21/24] Add ReactTimeAgo --- package-lock.json | 65 +++++++++++++++++++ package.json | 2 + .../utilities/CippActionsOffcanvas.js | 13 ++-- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08e6a9e247c5..619b33124282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "enzyme": "^3.11.0", "final-form": "^4.20.4", "fuzzysort": "^1.1.4", + "javascript-time-ago": "^2.5.9", "jspdf": "^2.4.0", "jspdf-autotable": "^3.5.23", "moment": "^2.29.1", @@ -53,6 +54,7 @@ "react-select": "^5.3.0", "react-select-search": "^3.0.8", "react-syntax-highlighter": "^15.4.5", + "react-time-ago": "^7.2.1", "redux": "4.1.1", "redux-persist": "^6.0.0", "simplebar-react": "^2.3.6", @@ -10065,6 +10067,14 @@ "node": ">=8" } }, + "node_modules/javascript-time-ago": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.9.tgz", + "integrity": "sha512-pQ8mNco/9g9TqWXWWjP0EWl6i/lAQScOyEeXy5AB+f7MfLSdgyV9BJhiOD1zrIac/lrxPYOWNbyl/IW8CW5n0A==", + "dependencies": { + "relative-time-format": "^1.1.6" + } + }, "node_modules/jest": { "version": "27.5.1", "dev": true, @@ -15163,6 +15173,26 @@ "react": "17.0.2" } }, + "node_modules/react-time-ago": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/react-time-ago/-/react-time-ago-7.2.1.tgz", + "integrity": "sha512-X5zwJDZHa1fsMwMvh8mrHN31g85s84zMCp+d7YL6IX50kNnr6YMAS2wpt1BmO9OxBV2Ue5J1ptD6JI8Zjd35HA==", + "dependencies": { + "memoize-one": "^6.0.0", + "prop-types": "^15.8.1", + "raf": "^3.4.1" + }, + "peerDependencies": { + "javascript-time-ago": "^2.3.7", + "react": ">=0.16.8", + "react-dom": ">=0.16.8" + } + }, + "node_modules/react-time-ago/node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/react-transition-group": { "version": "4.4.2", "license": "BSD-3-Clause", @@ -15500,6 +15530,11 @@ "node": ">= 0.10" } }, + "node_modules/relative-time-format": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz", + "integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==" + }, "node_modules/renderkid": { "version": "3.0.0", "dev": true, @@ -24586,6 +24621,14 @@ } } }, + "javascript-time-ago": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.9.tgz", + "integrity": "sha512-pQ8mNco/9g9TqWXWWjP0EWl6i/lAQScOyEeXy5AB+f7MfLSdgyV9BJhiOD1zrIac/lrxPYOWNbyl/IW8CW5n0A==", + "requires": { + "relative-time-format": "^1.1.6" + } + }, "jest": { "version": "27.5.1", "dev": true, @@ -27687,6 +27730,23 @@ "scheduler": "^0.20.2" } }, + "react-time-ago": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/react-time-ago/-/react-time-ago-7.2.1.tgz", + "integrity": "sha512-X5zwJDZHa1fsMwMvh8mrHN31g85s84zMCp+d7YL6IX50kNnr6YMAS2wpt1BmO9OxBV2Ue5J1ptD6JI8Zjd35HA==", + "requires": { + "memoize-one": "^6.0.0", + "prop-types": "^15.8.1", + "raf": "^3.4.1" + }, + "dependencies": { + "memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + } + } + }, "react-transition-group": { "version": "4.4.2", "requires": { @@ -27914,6 +27974,11 @@ "version": "0.2.7", "dev": true }, + "relative-time-format": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz", + "integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==" + }, "renderkid": { "version": "3.0.0", "dev": true, diff --git a/package.json b/package.json index d9e64244f095..7d4a99103c8f 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "enzyme": "^3.11.0", "final-form": "^4.20.4", "fuzzysort": "^1.1.4", + "javascript-time-ago": "^2.5.9", "jspdf": "^2.4.0", "jspdf-autotable": "^3.5.23", "moment": "^2.29.1", @@ -72,6 +73,7 @@ "react-select": "^5.3.0", "react-select-search": "^3.0.8", "react-syntax-highlighter": "^15.4.5", + "react-time-ago": "^7.2.1", "redux": "4.1.1", "redux-persist": "^6.0.0", "simplebar-react": "^2.3.6", diff --git a/src/components/utilities/CippActionsOffcanvas.js b/src/components/utilities/CippActionsOffcanvas.js index cbf9125a0b6f..9e712289e6b7 100644 --- a/src/components/utilities/CippActionsOffcanvas.js +++ b/src/components/utilities/CippActionsOffcanvas.js @@ -19,6 +19,11 @@ import { CippOffcanvasTable } from 'src/components/tables' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { Link, useNavigate } from 'react-router-dom' import { stringCamelCase } from 'src/components/utilities/CippCamelCase' +import TimeAgo from 'javascript-time-ago' + +import en from 'javascript-time-ago/locale/en.json' +TimeAgo.addDefaultLocale(en) +import ReactTimeAgo from 'react-time-ago' export default function CippActionsOffcanvas(props) { const inputRef = useRef('') @@ -107,14 +112,14 @@ export default function CippActionsOffcanvas(props) { Status: {action.value} - Generated on {action.timestamp.split('T')[0]} at {action.timestamp.split('T')[1]} + )) } catch (error) { - console.error('An error occored building OCanvas actions' + error.toString()) + console.error('An error occurred building OCanvas actions' + error.toString()) } const extendedInfoContent = @@ -144,7 +149,7 @@ export default function CippActionsOffcanvas(props) { )) } catch (error) { - console.error('An error occored building OCanvas actions' + error.toString()) + console.error('An error occurred building OCanvas actions' + error.toString()) } let actionsSelectorsContent try { @@ -169,7 +174,7 @@ export default function CippActionsOffcanvas(props) { } catch (error) { // When we create an Off Canvas control without selectors we will get this if (!error.toString().includes("Cannot read properties of undefined (reading '")) { - console.error('An error occored building OCanvas selectors' + error.toString()) + console.error('An error occurred building OCanvas selectors' + error.toString()) } } return ( From 9759c6791e6591b3b7599450ecc0eb1d5f055d1a Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:20:41 +0200 Subject: [PATCH 22/24] fix minor bug with date calc --- src/components/header/AppHeaderDropdown.js | 14 +++++++++++--- src/components/utilities/CippActionsOffcanvas.js | 6 ++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/header/AppHeaderDropdown.js b/src/components/header/AppHeaderDropdown.js index e6589af9697b..568e03211542 100644 --- a/src/components/header/AppHeaderDropdown.js +++ b/src/components/header/AppHeaderDropdown.js @@ -31,7 +31,14 @@ const AppHeaderDropdown = () => { useEffect(() => { if (cippQueueList.isFetching) { - setCippQueueExtendedInfo([{ label: 'Fetching recent jobs', value: 'Please wait' }]) + setCippQueueExtendedInfo([ + { + label: 'Fetching recent jobs', + value: 'Please wait', + timpestamp: Date(), + link: '#', + }, + ]) } if ( cippQueueList.isSuccess && @@ -47,9 +54,10 @@ const AppHeaderDropdown = () => { })), ) } else { - setCippQueueExtendedInfo([{ label: 'No jobs to display', value: '' }]) + setCippQueueExtendedInfo([ + { label: 'No jobs to display', value: '', timpestamp: Date(), link: '#' }, + ]) } - console.log(cippQueueList) }, [cippQueueList]) return ( diff --git a/src/components/utilities/CippActionsOffcanvas.js b/src/components/utilities/CippActionsOffcanvas.js index 9e712289e6b7..b24aa1a28c12 100644 --- a/src/components/utilities/CippActionsOffcanvas.js +++ b/src/components/utilities/CippActionsOffcanvas.js @@ -109,11 +109,9 @@ export default function CippActionsOffcanvas(props) { Report Name: {action.label} - Status: {action.value} + {action.value && Status: {action.value}} - - - + {action.timestamp && } From 45ace28e378082fdee4cb52434f6aafe6c3f781d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:28:04 +0200 Subject: [PATCH 23/24] add tenant column --- .../reports/MailboxStatisticsList.js | 158 ++++++++++-------- 1 file changed, 91 insertions(+), 67 deletions(-) diff --git a/src/views/email-exchange/reports/MailboxStatisticsList.js b/src/views/email-exchange/reports/MailboxStatisticsList.js index 6d80e5c3d569..febb16cc0062 100644 --- a/src/views/email-exchange/reports/MailboxStatisticsList.js +++ b/src/views/email-exchange/reports/MailboxStatisticsList.js @@ -1,77 +1,101 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' import { CellTip, cellBooleanFormatter } from 'src/components/tables' import { CippPageList } from 'src/components/layout' -const conditionalRowStyles = [ - { - when: (row) => (row.UsedGB / row.QuotaGB) * 100 > 80 && (row.UsedGB / row.QuotaGB) * 100 < 90, - classNames: ['mbusage-warning'], - }, - { - when: (row) => (row.UsedGB / row.QuotaGB) * 100 > 90 && (row.UsedGB / row.QuotaGB) * 100 < 100, - classNames: ['mbusage-danger'], - }, -] - -const columns = [ - { - selector: (row) => row['UPN'], - name: 'User Prinicipal Name', - sortable: true, - cell: (row) => CellTip(row['UPN']), - exportSelector: 'UPN', - minWidth: '200px', - }, - { - selector: (row) => row['displayName'], - name: 'Display Name', - sortable: true, - cell: (row) => CellTip(row['displayName']), - exportSelector: 'displayName', - }, - { - selector: (row) => row['MailboxType'], - name: 'Mailbox Type', - sortable: true, - exportSelector: 'MailboxType', - }, - { - selector: (row) => row['LastActive'], - name: 'Last Active', - sortable: true, - exportSelector: 'LastActive', - }, - { - selector: (row) => row['UsedGB'], - name: 'Used Space(GB)', - sortable: true, - exportSelector: 'UsedGB', - }, - { - selector: (row) => row['QuotaGB'], - name: 'Quota (GB)', - sortable: true, - exportSelector: 'QuotaGB', - }, - { - selector: (row) => row['ItemCount'], - name: 'Item Count (Total)', - sortable: true, - exportSelector: 'ItemCount', - }, - { - selector: (row) => row['HasArchive'], - name: 'Archiving Enabled', - sortable: true, - cell: cellBooleanFormatter({ colourless: true }), - exportSelector: 'HasArchive', - }, -] - const MailboxStatsList = () => { + const [tenantColumnSet, setTenantColumn] = useState(true) const tenant = useSelector((state) => state.app.currentTenant) + const conditionalRowStyles = [ + { + when: (row) => (row.UsedGB / row.QuotaGB) * 100 > 80 && (row.UsedGB / row.QuotaGB) * 100 < 90, + classNames: ['mbusage-warning'], + }, + { + when: (row) => + (row.UsedGB / row.QuotaGB) * 100 > 90 && (row.UsedGB / row.QuotaGB) * 100 < 100, + classNames: ['mbusage-danger'], + }, + ] + const columns = [ + { + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + cell: (row) => CellTip(row['Tenant']), + exportSelector: 'Tenant', + omit: tenantColumnSet, + }, + { + name: 'Retrieval Status', + selector: (row) => row['CippStatus'], + sortable: true, + cell: (row) => CellTip(row['CippStatus']), + exportSelector: 'CippStatus', + omit: tenantColumnSet, + }, + { + selector: (row) => row['UPN'], + name: 'User Prinicipal Name', + sortable: true, + cell: (row) => CellTip(row['UPN']), + exportSelector: 'UPN', + minWidth: '200px', + }, + { + selector: (row) => row['displayName'], + name: 'Display Name', + sortable: true, + cell: (row) => CellTip(row['displayName']), + exportSelector: 'displayName', + }, + { + selector: (row) => row['MailboxType'], + name: 'Mailbox Type', + sortable: true, + exportSelector: 'MailboxType', + }, + { + selector: (row) => row['LastActive'], + name: 'Last Active', + sortable: true, + exportSelector: 'LastActive', + }, + { + selector: (row) => row['UsedGB'], + name: 'Used Space(GB)', + sortable: true, + exportSelector: 'UsedGB', + }, + { + selector: (row) => row['QuotaGB'], + name: 'Quota (GB)', + sortable: true, + exportSelector: 'QuotaGB', + }, + { + selector: (row) => row['ItemCount'], + name: 'Item Count (Total)', + sortable: true, + exportSelector: 'ItemCount', + }, + { + selector: (row) => row['HasArchive'], + name: 'Archiving Enabled', + sortable: true, + cell: cellBooleanFormatter({ colourless: true }), + exportSelector: 'HasArchive', + }, + ] + useEffect(() => { + if (tenant.defaultDomainName === 'AllTenants') { + setTenantColumn(false) + } + if (tenant.defaultDomainName !== 'AllTenants') { + setTenantColumn(true) + } + }, [tenantColumnSet]) return ( Date: Fri, 21 Apr 2023 10:29:39 +0200 Subject: [PATCH 24/24] upped version --- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/version_latest.txt b/public/version_latest.txt index 15a279981720..fbcbf7380658 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -3.3.0 +3.4.0 \ No newline at end of file diff --git a/version_latest.txt b/version_latest.txt index 0fa4ae489037..fbcbf7380658 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -3.3.0 \ No newline at end of file +3.4.0 \ No newline at end of file