diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml index 1f5a0ee5feb6..6e1c3fe8b8c6 100644 --- a/.github/workflows/dev_deploy.yml +++ b/.github/workflows/dev_deploy.yml @@ -4,14 +4,10 @@ on: push: branches: - dev - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - dev jobs: build_and_deploy_job: - if: github.event.repository.fork == false && github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') + if: github.event.repository.fork == false && github.event_name == 'push' runs-on: ubuntu-latest name: Build and Deploy Job steps: diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d42c5d5a7c44..c21abba6989f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,6 +5,7 @@ "eg2.vscode-npm-script", "christian-kohler.npm-intellisense", "esbenp.prettier-vscode", - "stylelint.vscode-stylelint" + "stylelint.vscode-stylelint", + "editorconfig.editorconfig" ] } diff --git a/README.md b/README.md index 677617c1ae29..29ef370db018 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,13 @@ ![CyberDrain Light](github_assets/img/CIPP.png#gh-dark-mode-only) ![CyberDrain Dark](github_assets/img/CIPP-Light.png#gh-light-mode-only) -

Sponsored by

-

- -![OIT](github_assets/img/oitpsonsor_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)     -![Huntress](github_assets/img/Huntress.png) -![HaloPSA](github_assets/img/halopsa-red-grey.svg) - -

- # What is this? -The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners. The current Microsoft partner landscape makes it fairly hard to manage multi tenant situations, with loads of manual work. Microsoft Lighthouse might resolve this in the future but development of this is lagging far behind development of the current market for Microsoft Partners. +The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners. The current Microsoft partner landscape makes it fairly hard to manage multi tenant situations, with loads of manual work. Microsoft Lighthouse might resolve this in the future but development of this is lagging far behind development of the current market for Microsoft Partners. +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. +For more information, we recommend checking out our website [here](https://cipp.app) +For detailed documentation about features of CIPP, please check out our [documentation.](https://docs.cipp.app) -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. +# Our sponsors -for more information, we recommend checking out our website [here](https://cipp.app) +You can find our sponsors [here.](https://docs.cipp.app/#our-sponsors) diff --git a/package.json b/package.json index 039c0b97dd85..4e17521cd42b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "5.9.3", + "version": "6.0.1", "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/version_latest.txt b/public/version_latest.txt index 99a8b57b6f85..5fe60723048a 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -5.9.3 +6.0.1 diff --git a/src/_nav.jsx b/src/_nav.jsx index 874b6e28eb42..631d6eaf1169 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -19,6 +19,7 @@ import { faUserShield, faEnvelope, faToolbox, + faDownload, } from '@fortawesome/free-solid-svg-icons' const _nav = [ @@ -162,6 +163,11 @@ const _nav = [ name: 'App Consent Requests', to: '/tenant/administration/app-consent-requests', }, + { + component: CNavItem, + name: 'Authentication Methods', + to: '/tenant/administration/authentication-methods', + }, { component: CNavItem, name: 'Tenant Onboarding', @@ -179,6 +185,25 @@ const _nav = [ }, ], }, + { + component: CNavGroup, + name: 'Configuration Backup', + section: 'Tenant Administration', + to: '/cipp/gdap', + icon: , + items: [ + { + component: CNavItem, + name: 'Backup Wizard', + to: '/tenant/backup/backup-wizard', + }, + { + component: CNavItem, + name: 'Restore Wizard', + to: '/tenant/backup/restore-wizard', + }, + ], + }, { component: CNavGroup, name: 'Tools', @@ -608,6 +633,11 @@ const _nav = [ name: 'Mailboxes', to: '/email/administration/mailboxes', }, + { + component: CNavItem, + name: 'Deleted Mailboxes', + to: '/email/administration/deleted-mailboxes', + }, { component: CNavItem, name: 'Mailbox Rules', diff --git a/src/components/contentcards/CippButtonCard.jsx b/src/components/contentcards/CippButtonCard.jsx index 4a6400ad9cca..8e74d5470693 100644 --- a/src/components/contentcards/CippButtonCard.jsx +++ b/src/components/contentcards/CippButtonCard.jsx @@ -9,9 +9,10 @@ export default function CippButtonCard({ CardButton, children, isFetching, + className = 'h-100', }) { return ( - + {titleType === 'big' ?

{title}

: title} @@ -32,4 +33,5 @@ CippButtonCard.propTypes = { CardButton: PropTypes.element.isRequired, children: PropTypes.element.isRequired, isFetching: PropTypes.bool.isRequired, + className: PropTypes.string, } diff --git a/src/components/tables/CellBytes.jsx b/src/components/tables/CellBytes.jsx new file mode 100644 index 000000000000..b5b3e38211b8 --- /dev/null +++ b/src/components/tables/CellBytes.jsx @@ -0,0 +1,24 @@ +import PropTypes from 'prop-types' + +export function CellBytes({ cell }) { + return (cell / 1024 ** 3).toFixed(2) +} + +CellBytes.propTypes = { + propName: PropTypes.string, + cell: PropTypes.object, +} + +export function CellBytesToPercentage({ row, value, dividedBy }) { + return Math.round((row[value] / row[dividedBy]) * 100 * 10) / 10 +} + +CellBytesToPercentage.propTypes = { + propName: PropTypes.string, + cell: PropTypes.object, +} + +export const cellBytesFormatter = () => (row, index, column, id) => { + const cell = column.selector(row) + return CellBytes({ cell }) +} diff --git a/src/components/tables/CellCopyButton.jsx b/src/components/tables/CellCopyButton.jsx new file mode 100644 index 000000000000..ca3a2b7c98c2 --- /dev/null +++ b/src/components/tables/CellCopyButton.jsx @@ -0,0 +1,16 @@ +import PropTypes from 'prop-types' +import CippCopyToClipboard from '../utilities/CippCopyToClipboard' + +export function CellCopyButton({ cell }) { + return +} + +CellCopyButton.propTypes = { + propName: PropTypes.string, + cell: PropTypes.object, +} + +export const cellCopyButtonFormatter = () => (row, index, column, id) => { + const cell = column.selector(row) + return CellCopyButton({ cell }) +} diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index 6eee3a54e611..04ede5a4c204 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -629,12 +629,14 @@ export default function CippTable({ if (!disablePDFExport || !disableCSVExport) { const keys = [] const exportFormatter = {} + const exportFormatterArgs = {} columns.map((col) => { if (col.exportSelector) keys.push(col.exportSelector) if (col.exportFormatter) exportFormatter[col.exportSelector] = col.exportFormatter + if (col.exportFormatterArgs) + exportFormatterArgs[col.exportSelector] = col.exportFormatterArgs return null }) - // Define the flatten function const flatten = (obj, prefix = '') => { if (obj === null) return {} @@ -664,18 +666,18 @@ export default function CippTable({ // Define the applyFormatter function const applyFormatter = (obj) => { return Object.keys(obj).reduce((acc, key) => { + const formatterArgs = exportFormatterArgs[key] const formatter = exportFormatter[key] - // Since the keys after flattening will be dot-separated, we need to adjust this to support nested keys if necessary. const keyParts = key.split('.') const finalKeyPart = keyParts[keyParts.length - 1] const formattedValue = - typeof formatter === 'function' ? formatter({ cell: obj[key] }) : obj[key] + typeof formatter === 'function' + ? formatter({ row: obj, cell: obj[key], ...formatterArgs }) + : obj[key] acc[key] = formattedValue return acc }, {}) } - - // Process exportData function const processExportData = (exportData, selectedColumns) => { //filter out the columns that are not selected via selectedColumns exportData = exportData.map((item) => { diff --git a/src/data/AuditLogSchema.json b/src/data/AuditLogSchema.json index d29cdb5cffea..f58b08322473 100644 --- a/src/data/AuditLogSchema.json +++ b/src/data/AuditLogSchema.json @@ -70,12 +70,12 @@ }, "List:Operation": [ { "value": "UserLoggedIn", "name": "A user logged in" }, - { "value": "accessed mailbox items", "name": "accessed mailbox items" }, + { "value": "mailitemsaccessed", "name": "accessed mailbox items" }, { "value": "add delegation entry.", "name": "added delegation entry" }, { "value": "add domain to company.", "name": "added domain to company" }, { "value": "add group.", "name": "added group" }, { "value": "add member to group.", "name": "added member to group" }, - { "value": "add mailboxpermission", "name": "added delegate mailbox permissions" }, + { "value": "add-mailboxpermission", "name": "added delegate mailbox permissions" }, { "value": "add member to role.", "name": "added member to role" }, { "value": "add partner to company.", "name": "added a partner to the directory" }, { "value": "add service principal.", "name": "added service principal" }, @@ -111,7 +111,7 @@ "value": "remove service principal credentials.", "name": "removed credentials from a service principal" }, - { "value": "remove mailboxpermission", "name": "removed delegate mailbox permissions" }, + { "value": "remove-mailboxpermission", "name": "removed delegate mailbox permissions" }, { "value": "remove member from role.", "name": "removed a user from a directory role" }, { "value": "remove partner from company.", "name": "removed a partner from the directory" }, { "value": "removefolderpermissions", "name": "removed permissions from folder" }, @@ -132,7 +132,7 @@ "value": "set force change user password.", "name": "set property that forces user to change password" }, - { "value": "set inboxrule", "name": "modified inbox rule from outlook web app" }, + { "value": "set-inboxrule", "name": "modified inbox rule from outlook web app" }, { "value": "set license properties.", "name": "set license properties" }, { "value": "set password policy.", "name": "set password policy" }, { "value": "softdelete", "name": "deleted messages from deleted items folder" }, diff --git a/src/data/Extensions.json b/src/data/Extensions.json index 1ca3a87b94d7..6d4b8ab242f0 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -1,9 +1,10 @@ [ { - "name": "CIPP-API Integration", + "name": "CIPP-API", "type": "CIPP-API", "cat": "API", "forceSyncButton": false, + "disableWhenhosted": true, "helpText": "This integration allows you to enable CIPP-API access outside of CIPP. Requires Global Administrator permissions inside your tenant for activation of the API. The API credentials will only be shown once.", "SettingOptions": [ { @@ -20,7 +21,7 @@ "mappingRequired": false }, { - "name": "Gradient Integration", + "name": "Gradient", "type": "Gradient", "cat": "Billing & Invoicing", "forceSyncButton": true, @@ -54,7 +55,7 @@ "mappingRequired": false }, { - "name": "Halo PSA Ticketing Integration", + "name": "Halo PSA Ticketing", "type": "HaloPSA", "cat": "Ticketing", "forceSyncButton": false, @@ -111,7 +112,7 @@ "mappingRequired": true }, { - "name": "NinjaOne Integration", + "name": "NinjaOne", "type": "NinjaOne", "cat": "Documentation & Monitoring", "forceSyncButton": true, @@ -121,7 +122,7 @@ "type": "input", "fieldtype": "input", "name": "NinjaOne.Instance", - "label": "Please enter your NinjaOne Instance", + "label": "Please enter your NinjaOne Instance hostname", "placeholder": "app.ninjarmm.com, eu.ninjarmm.com, oc.ninjarmm.com, ca.ninjarmm.com, us2.ninjarmm.com" }, { @@ -140,18 +141,18 @@ }, { "type": "checkbox", - "name": "NinjaOne.UserDocumentsEnabled", - "label": "Synchronize Detailed User Information (Requires NinjaOne Documentation)" + "name": "NinjaOne.LicenseDocumentsEnabled", + "label": "Sync Licenses (Requires NinjaOne Documentation)" }, { "type": "checkbox", - "name": "NinjaOne.LicenseDocumentsEnabled", - "label": "Synchronize Detailed License Information (Requires NinjaOne Documentation)" + "name": "NinjaOne.UserDocumentsEnabled", + "label": "Sync Users (Requires NinjaOne Documentation)" }, { "type": "checkbox", "name": "NinjaOne.LicensedOnly", - "label": "Only Synchronize Licensed Users" + "label": "Only Sync Licensed Users (Requires NinjaOne Documentation)" }, { "type": "checkbox", @@ -159,7 +160,51 @@ "label": "Enable Integration" } ], - "mappingRequired": true + "mappingRequired": true, + "fieldMapping": true, + "autoMapSyncApi": true, + "showSyncButton": true + }, + { + "name": "Hudu", + "type": "Hudu", + "cat": "Documentation", + "forceSyncButton": true, + "helpText": "This integration allows you to populate custom asset layouts with Tenant information, monitor device compliance state, document other items and generate relationships inside Hudu.", + "SettingOptions": [ + { + "type": "input", + "fieldtype": "input", + "name": "Hudu.BaseUrl", + "label": "Please enter your Hudu URL", + "placeholder": "https://yourcompany.huducloud.com" + }, + { + "type": "input", + "fieldtype": "password", + "name": "Hudu.APIKey", + "label": "Hudu API Key", + "placeholder": "Enter your Hudu API Key" + }, + { + "type": "checkbox", + "name": "Hudu.CreateMissingUsers", + "label": "Create missing users in Hudu" + }, + { + "type": "checkbox", + "name": "Hudu.CreateMissingDevices", + "label": "Create missing devices in Hudu" + }, + { + "type": "checkbox", + "name": "Hudu.Enabled", + "label": "Enable Integration" + } + ], + "mappingRequired": true, + "fieldMapping": true, + "showSyncButton": true }, { "name": "PasswordPusher", diff --git a/src/data/alerts.json b/src/data/alerts.json index d6c7215f2d04..2d635fb529f9 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -89,5 +89,10 @@ "name": "DepTokenExpiry", "label": "Alert on expiring DEP tokens", "recommendedRunInterval": "1d" + }, + { + "name": "SoftDeletedMailboxes", + "label": "Alert on soft deleted mailboxes", + "recommendedRunInterval": "1d" } -] +] \ No newline at end of file diff --git a/src/data/standards.json b/src/data/standards.json index 39f4dfef6108..395a9ed33ae7 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -4,6 +4,7 @@ "cat": "Global Standards", "tag": ["lowimpact"], "helpText": "Defines the email address to receive general updates and information related to M365 subscriptions. Leave a contact field blank if you do not want to update the contact information.", + "docsDescription": "", "disabledFeatures": { "report": false, "warn": false, @@ -33,7 +34,9 @@ ], "label": "Set contact e-mails", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-MsolCompanyContactInformation", + "recommendedBy": [] }, { "name": "standards.AuditLog", @@ -43,7 +46,9 @@ "addedComponent": [], "label": "Enable the Unified Audit Log", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Enable-OrganizationCustomization", + "recommendedBy": ["CIS"] }, { "name": "standards.PhishProtection", @@ -58,7 +63,9 @@ "report": true, "warn": true, "remediate": false - } + }, + "powershellEquivalent": "Portal only", + "recommendedBy": ["CIPP"] }, { "name": "standards.Branding", @@ -109,17 +116,22 @@ ], "label": "Set branding for the tenant", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Portal only", + "recommendedBy": [] }, { "name": "standards.EnableCustomerLockbox", "cat": "Global Standards", "tag": ["lowimpact", "CIS", "CustomerLockBoxEnabled"], "helpText": "Enables Customer Lockbox that offers an approval process for Microsoft support to access organization data", + "docsDescription": "Customer Lockbox ensures that Microsoft can't access your content to do service operations without your explicit approval. Customer Lockbox ensures only authorized requests allow access to your organizations data.", "addedComponent": [], "label": "Enable Customer Lockbox", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig -CustomerLockBoxEnabled $true", + "recommendedBy": ["CIS"] }, { "name": "standards.EnablePronouns", @@ -129,37 +141,48 @@ "addedComponent": [], "label": "Enable Pronouns", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaAdminPeoplePronoun -IsEnabledInOrganization:$true", + "recommendedBy": [] }, { "name": "standards.AnonReportDisable", "cat": "Global Standards", "tag": ["lowimpact"], "helpText": "Shows usernames instead of pseudo anonymised names in reports. This standard is required for reporting to work correctly.", + "docsDescription": "Microsoft announced some APIs and reports no longer return names, to comply with compliance and legal requirements in specific countries. This proves an issue for a lot of MSPs because those reports are often helpful for engineers. This standard applies a setting that shows usernames in those API calls / reports.", "addedComponent": [], "label": "Enable Usernames instead of pseudo anonymised names in reports", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaAdminReportSetting -BodyParameter @{displayConcealedNames = $true}", + "recommendedBy": [] }, { "name": "standards.DisableGuestDirectory", "cat": "Global Standards", "tag": ["lowimpact"], "helpText": "Disables Guest access to enumerate directory objects. This prevents guest users from seeing other users or guests in the directory.", + "docsDescription": "Sets it so guests can view only their own user profile. Permission to view other users isn't allowed. Also restricts guest users from seeing the membership of groups they're in. See exactly what get locked down in the [Microsoft documentation.](https://learn.microsoft.com/en-us/entra/fundamentals/users-default-permissions)", "addedComponent": [], "label": "Restrict guest user access to directory objects", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-AzureADMSAuthorizationPolicy -GuestUserRoleId '2af84b1e-32c8-42b7-82bc-daa82404023b'", + "recommendedBy": [] }, { "name": "standards.DisableBasicAuthSMTP", "cat": "Global Standards", "tag": ["mediumimpact"], "helpText": "Disables SMTP AUTH for the organization and all users. This is the default for new tenants. ", + "docsDescription": "Disables SMTP basic authentication for the tenant and all users with it explicitly enabled.", "addedComponent": [], "label": "Disable SMTP Basic Authentication", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-TransportConfig -SmtpClientAuthenticationDisabled $true", + "recommendedBy": [] }, { "name": "standards.ActivityBasedTimeout", @@ -197,43 +220,74 @@ ], "label": "Enable Activity based Timeout", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Portal or Graph API", + "recommendedBy": ["CIS"] + }, + { + "name": "standards.AppDeploy", + "cat": "Entra (AAD) Standards", + "tag": ["lowimpact"], + "helpText": "Deploys selected applications to the tenant. Use a comma separated list of application IDs to deploy multiple applications. Permissions will be copied from the source application.", + "docsDescription": "Uses the CIPP functionality that deploys applications across an entire tenant base as a standard.", + "addedComponent": [ + { + "type": "input", + "name": "standards.AppDeploy.appids", + "label": "Application IDs, comma separated" + } + ], + "label": "Deploy Application", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Portal or Graph API", + "recommendedBy": [] }, { "name": "standards.laps", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Enables the tenant to use LAPS. You must still create a policy for LAPS to be active on all devices. Use the template standards to deploy this by default.", + "docsDescription": "Enables the LAPS functionality on the tenant. Prerequisite for using Windows LAPS via Azure AD.", "addedComponent": [], "label": "Enable LAPS on the tenant", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Portal or Graph API", + "recommendedBy": [] }, { "name": "standards.PWdisplayAppInformationRequiredState", "cat": "Entra (AAD) Standards", "tag": ["lowimpact", "CIS"], "helpText": "Enables the MS authenticator app to display information about the app that is requesting authentication. This displays the application name.", + "docsDescription": "Allows users to use Passwordless with Number Matching and adds location information from the last request", "addedComponent": [], "label": "Enable Passwordless with Location information and Number Matching", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": ["CIS"] }, { "name": "standards.allowOTPTokens", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Allows you to use MS authenticator OTP token generator", + "docsDescription": "Allows you to use Microsoft Authenticator OTP token generator. Useful for using the NPS extension as MFA on VPN clients.", "addedComponent": [], "label": "Enable OTP via Authenticator", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.PWcompanionAppAllowedState", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Sets the state of Authenticator Lite, Authenticator lite is a companion app for passwordless authentication.", + "docsDescription": "Sets the Authenticator Lite state to enabled. This allows users to use the Authenticator Lite built into the Outlook app instead of the full Authenticator app.", "addedComponent": [ { "type": "Select", @@ -253,43 +307,55 @@ ], "label": "Set Authenticator Lite state", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.EnableFIDO2", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Enables the FIDO2 authenticationMethod for the tenant", + "docsDescription": "Enables FIDO2 capabilities for the tenant. This allows users to use FIDO2 keys like a Yubikey for authentication.", "addedComponent": [], "label": "Enable FIDO2 capabilities", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.EnableHardwareOAuth", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Enables the HardwareOath authenticationMethod for the tenant. This allows you to use hardware tokens for generating 6 digit MFA codes.", + "docsDescription": "Enables Hardware OAuth tokens for the tenant. This allows users to use hardware tokens like a Yubikey for authentication.", "addedComponent": [], "label": "Enable Hardware OAuth tokens", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.allowOAuthTokens", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Allows you to use any software OAuth token generator", + "docsDescription": "Enables OTP Software OAuth tokens for the tenant. This allows users to use OTP codes generated via software, like a password manager to be used as an authentication method.", "addedComponent": [], "label": "Enable OTP Software OAuth tokens", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "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.", + "docsDescription": "Enables Temporary Password generation for the tenant.", "addedComponent": [ { "type": "Select", @@ -309,17 +375,22 @@ ], "label": "Enable Temporary Access Passwords", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.PasswordExpireDisabled", "cat": "Entra (AAD) Standards", "tag": ["lowimpact", "CIS", "PWAgePolicyNew"], "helpText": "Disables the expiration of passwords for the tenant by setting the password expiration policy to never expire for any user.", + "docsDescription": "Sets passwords to never expire for tenant, recommended to use in conjunction with secure password requirements.", "addedComponent": [], "label": "Do not expire passwords", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgDomain", + "recommendedBy": ["CIS"] }, { "name": "standards.ExternalMFATrusted", @@ -345,23 +416,29 @@ ], "label": "Sets the Cross-tenant access setting to trust external MFA", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaPolicyCrossTenantAccessPolicyDefault", + "recommendedBy": [] }, { "name": "standards.DisableTenantCreation", "cat": "Entra (AAD) Standards", "tag": ["lowimpact", "CIS"], "helpText": "Restricts creation of M365 tenants to the Global Administrator or Tenant Creator roles. ", + "docsDescription": "Users by default are allowed to create M365 tenants. This disables that so only admins can create new M365 tenants.", "addedComponent": [], "label": "Disable M365 Tenant creation by users", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgPolicyAuthorizationPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.EnableAppConsentRequests", "cat": "Entra (AAD) Standards", "tag": ["lowimpact", "CIS"], "helpText": "Enables App consent admin requests for the tenant via the GA role. Does not overwrite existing reviewer settings", + "docsDescription": "Enables the ability for users to request admin consent for applications. Should be used in conjunction with the \"Require admin consent for applications\" standards", "addedComponent": [ { "type": "AdminRolesMultiSelect", @@ -371,13 +448,16 @@ ], "label": "Enable App consent admin requests", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgPolicyAdminConsentRequestPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.NudgeMFA", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Sets the state of the registration campaign for the tenant", + "docsDescription": "Sets the state of the registration campaign for the tenant. If enabled nudges users to set up the Microsoft Authenticator during sign-in.", "addedComponent": [ { "type": "Select", @@ -403,27 +483,35 @@ ], "label": "Sets the state for the request to setup Authenticator", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgPolicyAuthenticationMethodPolicy", + "recommendedBy": [] }, { "name": "standards.DisableM365GroupUsers", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], "helpText": "Restricts M365 group creation to certain admin roles. This disables the ability to create Teams, Sharepoint sites, Planner, etc", + "docsDescription": "Users by default are allowed to create M365 groups. This restricts M365 group creation to certain admin roles. This disables the ability to create Teams, SharePoint sites, Planner, etc", "addedComponent": [], "label": "Disable M365 Group creation by users", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaDirectorySetting", + "recommendedBy": [] }, { "name": "standards.DisableAppCreation", "cat": "Entra (AAD) Standards", "tag": ["lowimpact", "CIS"], "helpText": "Disables the ability for users to create App registrations in the tenant.", + "docsDescription": "Disables the ability for users to create applications in Entra. Done to prevent breached accounts from creating an app to maintain access to the tenant, even after the breached account has been secured.", "addedComponent": [], "label": "Disable App creation by users", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgPolicyAuthorizationPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.DisableSecurityGroupUsers", @@ -433,7 +521,9 @@ "addedComponent": [], "label": "Disable Security Group creation by users", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Update-MgBetaPolicyAuthorizationPolicy", + "recommendedBy": [] }, { "name": "standards.LegacyMFACleanup", @@ -443,17 +533,27 @@ "addedComponent": [], "label": "Remove Legacy MFA if SD or CA is active", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-MsolUser -StrongAuthenticationRequirements $null", + "recommendedBy": [] }, { "name": "standards.DisableSelfServiceLicenses", "cat": "Entra (AAD) Standards", "tag": ["mediumimpact"], - "helpText": "This standard currently does not function and can be safely disabled", - "addedComponent": [], + "helpText": "This standard disables all self service licenses and enables all exclusions", + "addedComponent": [ + { + "type": "input", + "name": "standards.DisableSelfServiceLicenses.Exclusions", + "label": "License Ids to exclude from this standard" + } + ], "label": "Disable Self Service Licensing", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-MsolCompanySettings -AllowAdHocSubscriptions $false", + "recommendedBy": [] }, { "name": "standards.DisableGuests", @@ -463,13 +563,16 @@ "addedComponent": [], "label": "Disable Guest accounts that have not logged on for 90 days", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Graph API", + "recommendedBy": [] }, { "name": "standards.OauthConsent", "cat": "Entra (AAD) Standards", "tag": ["mediumimpact", "CIS"], "helpText": "Disables users from being able to consent to applications, except for those specified in the field below", + "docsDescription": "Requires users to get administrator consent before sharing data with applications. You can preapprove specific applications.", "addedComponent": [ { "type": "input", @@ -479,16 +582,21 @@ ], "label": "Require admin consent for applications (Prevent OAuth phishing)", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Update-MgPolicyAuthorizationPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.OauthConsentLowSec", "cat": "Entra (AAD) Standards", "tag": ["mediumimpact"], "helpText": "Sets the default oauth consent level so users can consent to applications that have low risks.", + "docsDescription": "Allows users to consent to applications with low assigned risk.", "label": "Allow users to consent to applications with low security risk (Prevent OAuth phishing. Lower impact, less secure)", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Update-MgPolicyAuthorizationPolicy", + "recommendedBy": [] }, { "name": "standards.UndoOauth", @@ -498,33 +606,42 @@ "addedComponent": [], "label": "Undo App Consent Standard", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgPolicyAuthorizationPolicy", + "recommendedBy": [] }, { "name": "standards.SecurityDefaults", "cat": "Entra (AAD) Standards", "tag": ["highimpact"], "helpText": "Enables security defaults for the tenant, for newer tenants this is enabled by default. Do not enable this feature if you use Conditional Access.", + "docsDescription": "Enables SD for the tenant, which disables all forms of basic authentication and enforces users to configure MFA. Users are only prompted for MFA when a logon is considered 'suspect' by Microsoft.", "addedComponent": [], "label": "Enable Security Defaults", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "[Read more here](https://www.cyberdrain.com/automating-with-powershell-enabling-secure-defaults-and-sd-explained/)", + "recommendedBy": [] }, { "name": "standards.DisableSMS", "cat": "Entra (AAD) Standards", "tag": ["highimpact"], - "helpText": "This blocks users from using SMS as an MFA method. If a user only has SMS as a MFA method, they will be unable to login.", + "helpText": "This blocks users from using SMS as an MFA method. If a user only has SMS as a MFA method, they will be unable to log in.", + "docsDescription": "Disables SMS as an MFA method for the tenant. If a user only has SMS as a MFA method, they will be unable to sign in.", "addedComponent": [], "label": "Disables SMS as an MFA method", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.DisableVoice", "cat": "Entra (AAD) Standards", "tag": ["highimpact"], - "helpText": "This blocks users from using Voice call as an MFA method. If a user only has Voice as a MFA method, they will be unable to login.", + "helpText": "This blocks users from using Voice call as an MFA method. If a user only has Voice as a MFA method, they will be unable to log in.", + "docsDescription": "Disables Voice call as an MFA method for the tenant. If a user only has Voice call as a MFA method, they will be unable to sign in.", "addedComponent": [], "label": "Disables Voice call as an MFA method", "impact": "High Impact", @@ -538,17 +655,22 @@ "addedComponent": [], "label": "Disables Email as an MFA method", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.Disablex509Certificate", "cat": "Entra (AAD) Standards", "tag": ["highimpact"], "helpText": "This blocks users from using Certificates as an MFA method.", + "docsDescription": "", "addedComponent": [], "label": "Disables Certificates as an MFA method", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.PerUserMFA", @@ -558,13 +680,16 @@ "addedComponent": [], "label": "Enables per user MFA for all users.", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Graph API", + "recommendedBy": [] }, { "name": "standards.OutBoundSpamAlert", "cat": "Exchange Standards", "tag": ["lowimpact", "CIS"], "helpText": "Set the Outbound Spam Alert e-mail address", + "docsDescription": "Sets the e-mail address to which outbound spam alerts are sent.", "addedComponent": [ { "type": "input", @@ -574,23 +699,29 @@ ], "label": "Set Outbound Spam Alert e-mail", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-HostedOutboundSpamFilterPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.MessageExpiration", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Sets the transport message configuration to timeout a message at 12 hours.", + "docsDescription": "Expires messages in the transport queue after 12 hours. Makes the NDR for failed messages show up faster for users. Default is 24 hours.", "addedComponent": [], "label": "Lower Transport Message Expiration to 12 hours", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-TransportConfig -MessageExpirationTimeout 12.00:00:00", + "recommendedBy": [] }, { "name": "standards.GlobalQuarantineNotifications", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Sets the Global Quarantine Notification Interval to the selected value. Determines how often the quarantine notification is sent to users.", + "docsDescription": "Sets the global quarantine notification interval for the tenant. This is the time between the quarantine notification emails are sent out to users. Default is 24 hours.", "addedComponent": [ { "type": "Select", @@ -614,23 +745,29 @@ ], "label": "Set Global Quarantine Notification Interval", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-QuarantinePolicy -EndUserSpamNotificationFrequency", + "recommendedBy": [] }, { "name": "standards.DisableTNEF", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Disables Transport Neutral Encapsulation Format (TNEF)/winmail.dat for the tenant. TNEF can cause issues if the recipient is not using a client supporting TNEF.", + "docsDescription": "Disables Transport Neutral Encapsulation Format (TNEF)/winmail.dat for the tenant. TNEF can cause issues if the recipient is not using a client supporting TNEF. Cannot be overridden by the user. For more information, see [Microsoft's documentation.](https://learn.microsoft.com/en-us/exchange/mail-flow/content-conversion/tnef-conversion?view=exchserver-2019)", "addedComponent": [], "label": "Disable TNEF/winmail.dat", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-RemoteDomain -Identity 'Default' -TNEFEnabled $false", + "recommendedBy": [] }, { "name": "standards.FocusedInbox", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Sets the default Focused Inbox state for the tenant. This can be overridden by the user.", + "docsDescription": "Sets the default Focused Inbox state for the tenant. This can be overridden by the user in their Outlook settings. For more information, see [Microsoft's documentation.](https://support.microsoft.com/en-us/office/focused-inbox-for-outlook-f445ad7f-02f4-4294-a82e-71d8964e3978)", "addedComponent": [ { "type": "Select", @@ -650,13 +787,16 @@ ], "label": "Set Focused Inbox state", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig -FocusedInboxOn $true or $false", + "recommendedBy": [] }, { "name": "standards.CloudMessageRecall", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Sets the Cloud Message Recall state for the tenant. This allows users to recall messages from the cloud.", + "docsDescription": "Sets the default state for Cloud Message Recall for the tenant. By default this is enabled. You can read more about the feature [here.](https://techcommunity.microsoft.com/t5/exchange-team-blog/cloud-based-message-recall-in-exchange-online/ba-p/3744714)", "addedComponent": [ { "type": "Select", @@ -676,17 +816,22 @@ ], "label": "Set Cloud Message Recall state", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig -MessageRecallEnabled", + "recommendedBy": [] }, { "name": "standards.AutoExpandArchive", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Enables auto-expanding archives for the tenant", + "docsDescription": "Enables auto-expanding archives for the tenant. Does not enable archives for users.", "addedComponent": [], "label": "Enable Auto-expanding archives", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig -AutoExpandingArchive", + "recommendedBy": [] }, { "name": "standards.EnableOnlineArchiving", @@ -696,13 +841,28 @@ "addedComponent": [], "label": "Enable Online Archive for all users", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Enable-Mailbox -Archive $true", + "recommendedBy": [] + }, + { + "name": "standards.EnableLitigationHold", + "cat": "Exchange Standards", + "tag": ["lowimpact"], + "helpText": "Enables litigation hold for all UserMailboxes with a valid license.", + "addedComponent": [], + "label": "Enable Litigation Hold for all users", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Set-Mailbox -LitigationHoldEnabled $true", + "recommendedBy": [] }, { "name": "standards.SpoofWarn", "cat": "Exchange Standards", "tag": ["lowimpact", "CIS"], "helpText": "Adds or removes indicators to e-mail messages received from external senders in Outlook. Works on all Outlook clients/OWA", + "docsDescription": "Adds or removes indicators to e-mail messages received from external senders in Outlook. You can read more about this feature on [Microsoft's Exchange Team Blog.](https://techcommunity.microsoft.com/t5/exchange-team-blog/native-external-sender-callouts-on-email-in-outlook/ba-p/2250098)", "addedComponent": [ { "type": "Select", @@ -722,7 +882,9 @@ ], "label": "Enable or disable 'external' warning in Outlook", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "et-ExternalInOutlook –Enabled $true or $false", + "recommendedBy": ["CIS"] }, { "name": "standards.EnableMailTips", @@ -740,7 +902,9 @@ ], "label": "Enable all MailTips", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig", + "recommendedBy": ["CIS"] }, { "name": "standards.TeamsMeetingsByDefault", @@ -766,17 +930,22 @@ ], "label": "Set Teams Meetings by default state", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig -OnlineMeetingsByDefaultEnabled", + "recommendedBy": [] }, { "name": "standards.DisableViva", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Disables the daily viva reports for all users.", + "docsDescription": "", "addedComponent": [], "label": "Disable daily Insight/Viva reports", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-UserBriefingConfig", + "recommendedBy": [] }, { "name": "standards.RotateDKIM", @@ -786,7 +955,9 @@ "addedComponent": [], "label": "Rotate DKIM keys that are 1024 bit to 2048 bit", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Rotate-DkimSigningConfig", + "recommendedBy": ["CIS"] }, { "name": "standards.AddDKIM", @@ -796,17 +967,22 @@ "addedComponent": [], "label": "Enables DKIM for all domains that currently support it", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "New-DkimSigningConfig and Set-DkimSigningConfig", + "recommendedBy": ["CIS"] }, { "name": "standards.EnableMailboxAuditing", "cat": "Exchange Standards", "tag": ["lowimpact", "CIS", "exo_mailboxaudit"], "helpText": "Enables Mailbox auditing for all mailboxes and on tenant level. Disables audit bypass on all mailboxes. Unified Audit Log needs to be enabled for this standard to function.", + "docsDescription": "Enables mailbox auditing on tenant level and for all mailboxes. Disables audit bypass on all mailboxes. By default Microsoft does not enable mailbox auditing for Resource Mailboxes, Public Folder Mailboxes and DiscoverySearch Mailboxes. Unified Audit Log needs to be enabled for this standard to function.", "addedComponent": [], "label": "Enable Mailbox auditing", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-OrganizationConfig -AuditDisabled $false", + "recommendedBy": ["CIS"] }, { "name": "standards.SendReceiveLimitTenant", @@ -829,13 +1005,16 @@ ], "label": "Set send/receive size limits", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-MailboxPlan", + "recommendedBy": [] }, { "name": "standards.calDefault", "cat": "Exchange Standards", "tag": ["lowimpact"], "helpText": "Sets the default sharing level for the default calendar, for all users", + "docsDescription": "Sets the default sharing level for the default calendar for all users in the tenant. You can read about the different sharing levels [here.](https://learn.microsoft.com/en-us/powershell/module/exchange/set-mailboxfolderpermission?view=exchange-ps#-accessrights)", "disabledFeatures": { "report": true, "warn": true, @@ -896,27 +1075,35 @@ ], "label": "Set Sharing Level for Default calendar", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-MailboxFolderPermission", + "recommendedBy": [] }, { "name": "standards.DisableExternalCalendarSharing", "cat": "Exchange Standards", "tag": ["lowimpact", "CIS", "exo_individualsharing"], "helpText": "Disables the ability for users to share their calendar with external users. Only for the default policy, so exclusions can be made if needed.", + "docsDescription": "Disables external calendar sharing for the entire tenant. This is not a widely used feature, and it's therefore unlikely that this will impact users. Only for the default policy, so exclusions can be made if needed by making a new policy and assigning it to users.", "addedComponent": [], "label": "Disable external calendar sharing", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Get-SharingPolicy | Set-SharingPolicy -Enabled $False", + "recommendedBy": ["CIS"] }, { "name": "standards.DisableAdditionalStorageProviders", "cat": "Exchange Standards", "tag": ["lowimpact", "CIS", "exo_storageproviderrestricted"], "helpText": "Disables the ability for users to open files in Outlook on the Web, from other providers such as Box, Dropbox, Facebook, Google Drive, OneDrive Personal, etc.", + "docsDescription": "Disables additional storage providers in OWA. This is to prevent users from using personal storage providers like Dropbox, Google Drive, etc. Usually this has little user impact.", "addedComponent": [], "label": "Disable additional storage providers in OWA", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Get-OwaMailboxPolicy | Set-OwaMailboxPolicy -AdditionalStorageProvidersEnabled $False", + "recommendedBy": ["CIS"] }, { "name": "standards.ShortenMeetings", @@ -958,13 +1145,16 @@ ], "label": "Set shorten meetings state", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-OrganizationConfig -ShortenEventScopeDefault -DefaultMinutesToReduceShortEventsBy -DefaultMinutesToReduceLongEventsBy", + "recommendedBy": [] }, { "name": "standards.Bookings", "cat": "Exchange Standards", "tag": ["mediumimpact"], "helpText": "Sets the state of Bookings on the tenant. Bookings is a scheduling tool that allows users to book appointments with others both internal and external.", + "docsDescription": "", "addedComponent": [ { "type": "Select", @@ -984,17 +1174,22 @@ ], "label": "Set Bookings state", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-OrganizationConfig -BookingsEnabled", + "recommendedBy": [] }, { "name": "standards.DisableOutlookAddins", "cat": "Exchange Standards", "tag": ["mediumimpact", "CIS", "exo_outlookaddins"], "helpText": "Disables the ability for users to install add-ins in Outlook. This is to prevent users from installing malicious add-ins.", + "docsDescription": "Disables users from being able to install add-ins in Outlook. Only admins are able to approve add-ins for the users. This is done to reduce the threat surface for data exfiltration.", "addedComponent": [], "label": "Disable users from installing add-ins in Outlook", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Get-ManagementRoleAssignment | Remove-ManagementRoleAssignment", + "recommendedBy": ["CIS"] }, { "name": "standards.SafeSendersDisable", @@ -1009,33 +1204,42 @@ }, "label": "Remove Safe Senders to prevent SPF bypass", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-MailboxJunkEmailConfiguration", + "recommendedBy": [] }, { "name": "standards.DelegateSentItems", "cat": "Exchange Standards", "tag": ["mediumimpact"], "helpText": "Sets emails sent as and on behalf of shared mailboxes to also be stored in the shared mailbox sent items folder", + "docsDescription": "This makes sure that e-mails sent from shared mailboxes or delegate mailboxes, end up in the mailbox of the shared/delegate mailbox instead of the sender, allowing you to keep replies in the same mailbox as the original e-mail.", "addedComponent": [], "label": "Set mailbox Sent Items delegation (Sent items for shared mailboxes)", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-Mailbox", + "recommendedBy": [] }, { "name": "standards.SendFromAlias", "cat": "Exchange Standards", "tag": ["mediumimpact"], "helpText": "Enables the ability for users to send from their alias addresses.", + "docsDescription": "Allows users to change the 'from' address to any set in their Azure AD Profile.", "addedComponent": [], "label": "Allow users to send from their alias addresses", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-Mailbox", + "recommendedBy": [] }, { "name": "standards.UserSubmissions", "cat": "Exchange Standards", "tag": ["mediumimpact"], "helpText": "Set the state of the spam submission button in Outlook", + "docsDescription": "Set the state of the built-in Report button in Outlook. This gives the users the ability to report emails as spam or phish.", "addedComponent": [ { "type": "Select", @@ -1055,17 +1259,40 @@ ], "label": "Set the state of the built-in Report button in Outlook", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "New-ReportSubmissionPolicy or Set-ReportSubmissionPolicy", + "recommendedBy": [] + }, + { + "name": "standards.UserReportDestinationEmail", + "cat": "Exchange Standards", + "tag": ["mediumimpact"], + "helpText": "Sets the destination for email when users report them as spam or phishing. Works well together with the 'Set the state of the built-in Report button in Outlook standard'.", + "addedComponent": [ + { + "type": "input", + "name": "standards.UserReportDestinationEmail.Email", + "label": "Destination email address" + } + ], + "label": "Set the destination email for user reported emails", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "New-ReportSubmissionRule or Set-ReportSubmissionRule", + "recommendedBy": [] }, { "name": "standards.DisableSharedMailbox", "cat": "Exchange Standards", "tag": ["mediumimpact", "CIS"], "helpText": "Blocks login for all accounts that are marked as a shared mailbox. This is Microsoft best practice to prevent direct logons to shared mailboxes.", + "docsDescription": "Shared mailboxes can be directly logged into if the password is reset, this presents a security risk as do all shared login credentials. Microsoft's recommendation is to disable the user account for shared mailboxes. It would be a good idea to review the sign-in reports to establish potential impact.", "addedComponent": [], "label": "Disable Shared Mailbox AAD accounts", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Get-Mailbox & Update-MgUser", + "recommendedBy": ["CIS"] }, { "name": "standards.SafeLinksPolicy", @@ -1091,7 +1318,9 @@ ], "label": "Default SafeLinks Policy", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-SafeLinksPolicy or New-SafeLinksPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.AntiPhishPolicy", @@ -1138,6 +1367,40 @@ "name": "standards.AntiPhishPolicy.EnableUnusualCharactersSafetyTips", "default": true }, + { + "type": "Select", + "label": "If the message is detected as spoof by spoof intelligence", + "name": "standards.AntiPhishPolicy.AuthenticationFailAction", + "values": [ + { + "label": "Quarantine the message", + "value": "Quarantine" + }, + { + "label": "Move to Junk Folder", + "value": "MoveToJmf" + } + ] + }, + { + "type": "Select", + "label": "Quarantine policy for Spoof", + "name": "standards.AntiPhishPolicy.SpoofQuarantineTag", + "values": [ + { + "label": "AdminOnlyAccessPolicy", + "value": "AdminOnlyAccessPolicy" + }, + { + "label": "DefaultFullAccessPolicy", + "value": "DefaultFullAccessPolicy" + }, + { + "label": "DefaultFullAccessWithNotificationPolicy", + "value": "DefaultFullAccessWithNotificationPolicy" + } + ] + }, { "type": "Select", "label": "If a message is detected as user impersonation", @@ -1157,6 +1420,25 @@ } ] }, + { + "type": "Select", + "label": "Quarantine policy for user impersonation", + "name": "standards.AntiPhishPolicy.TargetedUserQuarantineTag", + "values": [ + { + "label": "AdminOnlyAccessPolicy", + "value": "AdminOnlyAccessPolicy" + }, + { + "label": "DefaultFullAccessPolicy", + "value": "DefaultFullAccessPolicy" + }, + { + "label": "DefaultFullAccessWithNotificationPolicy", + "value": "DefaultFullAccessWithNotificationPolicy" + } + ] + }, { "type": "Select", "label": "If a message is detected as domain impersonation", @@ -1176,6 +1458,25 @@ } ] }, + { + "type": "Select", + "label": "Quarantine policy for domain impersonation", + "name": "standards.AntiPhishPolicy.TargetedDomainQuarantineTag", + "values": [ + { + "label": "DefaultFullAccessWithNotificationPolicy", + "value": "DefaultFullAccessWithNotificationPolicy" + }, + { + "label": "AdminOnlyAccessPolicy", + "value": "AdminOnlyAccessPolicy" + }, + { + "label": "DefaultFullAccessPolicy", + "value": "DefaultFullAccessPolicy" + } + ] + }, { "type": "Select", "label": "If Mailbox Intelligence detects an impersonated user", @@ -1217,7 +1518,9 @@ ], "label": "Default Anti-Phishing Policy", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-AntiphishPolicy or New-AntiphishPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.SafeAttachmentPolicy", @@ -1282,7 +1585,9 @@ ], "label": "Default Safe Attachment Policy", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-SafeAttachmentPolicy or New-SafeAttachmentPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.AtpPolicyForO365", @@ -1299,7 +1604,9 @@ ], "label": "Default Atp Policy For O365", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-AtpPolicyForO365", + "recommendedBy": ["CIS"] }, { "name": "standards.MalwareFilterPolicy", @@ -1364,7 +1671,9 @@ ], "label": "Default Malware Filter Policy", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Set-MalwareFilterPolicy or New-MalwareFilterPolicy", + "recommendedBy": ["CIS"] }, { "name": "standards.intuneDeviceRetirementDays", @@ -1380,7 +1689,9 @@ ], "label": "Set inactive device retirement days", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Graph API", + "recommendedBy": [] }, { "name": "standards.intuneBrandingProfile", @@ -1441,7 +1752,9 @@ ], "label": "Set Intune Company Portal branding profile", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Graph API", + "recommendedBy": [] }, { "name": "standards.intuneDeviceReg", @@ -1457,7 +1770,9 @@ ], "label": "Set Maximum Number of Devices per user", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Update-MgBetaPolicyDeviceRegistrationPolicy", + "recommendedBy": [] }, { "name": "standards.intuneRequireMFA", @@ -1466,17 +1781,70 @@ "helpText": "Requires MFA for all users to register devices with Intune. This is useful when not using Conditional Access.", "label": "Require Multifactor Authentication to register or join devices with Microsoft Entra", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Update-MgBetaPolicyDeviceRegistrationPolicy", + "recommendedBy": [] }, { "name": "standards.DeletedUserRentention", "cat": "SharePoint Standards", "tag": ["lowimpact"], - "helpText": "Sets the retention period for deleted users OneDrive to 1 year/365 days", - "addedComponent": [], - "label": "Retain a deleted user OneDrive for 1 year", + "helpText": "Sets the retention period for deleted users OneDrive to the specified number of years. The default is 1 year.", + "docsDescription": "When a OneDrive user gets deleted, the personal SharePoint site is saved for selected time in years and data can be retrieved from it.", + "addedComponent": [ + { + "type": "Select", + "name": "standards.DeletedUserRentention.Days", + "label": "Retention in years (Default 1)", + "values": [ + { + "label": "1 year", + "value": "365" + }, + { + "label": "2 years", + "value": "730" + }, + { + "label": "3 years", + "value": "1095" + }, + { + "label": "4 years", + "value": "1460" + }, + { + "label": "5 years", + "value": "1825" + }, + { + "label": "6 years", + "value": "2190" + }, + { + "label": "7 years", + "value": "2555" + }, + { + "label": "8 years", + "value": "2920" + }, + { + "label": "9 years", + "value": "3285" + }, + { + "label": "10 years", + "value": "3650" + } + ] + } + ], + "label": "Set deleted user retention time in OneDrive", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaAdminSharepointSetting", + "recommendedBy": [] }, { "name": "standards.TenantDefaultTimezone", @@ -1492,7 +1860,81 @@ ], "label": "Set Default Timezone for Tenant", "impact": "Low Impact", - "impactColour": "info" + "impactColour": "info", + "powershellEquivalent": "Update-MgBetaAdminSharepointSetting", + "recommendedBy": [] + }, + { + "name": "standards.SPAzureB2B", + "cat": "SharePoint Standards", + "tag": ["lowimpact", "CIS"], + "helpText": "Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled", + "addedComponent": [], + "label": "Enable SharePoint and OneDrive integration with Azure AD B2B", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Set-SPOTenant -EnableAzureADB2BIntegration $true", + "recommendedBy": ["CIS 3.0"] + }, + { + "name": "standards.SPDisallowInfectedFiles", + "cat": "SharePoint Standards", + "tag": ["lowimpact", "CIS"], + "helpText": "Ensure Office 365 SharePoint infected files are disallowed for download", + "addedComponent": [], + "label": "Disallow downloading infected files from SharePoint", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Set-SPOTenant -DisallowInfectedFileDownload $true", + "recommendedBy": ["CIS 3.0"] + }, + { + "name": "standards.SPDirectSharing", + "cat": "SharePoint Standards", + "tag": ["mediumimpact", "CIS"], + "helpText": "Ensure default link sharing is set to Direct in SharePoint and OneDrive", + "addedComponent": [], + "label": "Default sharing to Direct users", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "Set-SPOTenant -DefaultSharingLinkType Direct", + "recommendedBy": ["CIS 3.0"] + }, + { + "name": "standards.SPExternalUserExpiration", + "cat": "SharePoint Standards", + "tag": ["mediumimpact", "CIS"], + "helpText": "Ensure guest access to a site or OneDrive will expire automatically", + "addedComponent": [ + { + "type": "number", + "name": "standards.SPExternalUserExpiration.Days", + "label": "Days until expiration (Default 60)" + } + ], + "label": "Set guest access to expire automatically", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "Set-SPOTenant -ExternalUserExpireInDays 30 -ExternalUserExpirationRequired $True", + "recommendedBy": ["CIS 3.0"] + }, + { + "name": "standards.SPEmailAttestation", + "cat": "SharePoint Standards", + "tag": ["mediumimpact", "CIS"], + "helpText": "Ensure reauthentication with verification code is restricted", + "addedComponent": [ + { + "type": "number", + "name": "standards.SPEmailAttestation.Days", + "label": "Require reauth every X Days (Default 15)" + } + ], + "label": "Require reauthentication with verification code", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "Set-SPOTenant -EmailAttestationRequired $true -EmailAttestationReAuthDays 15", + "recommendedBy": ["CIS 3.0"] }, { "name": "standards.DisableAddShortcutsToOneDrive", @@ -1507,17 +1949,22 @@ "addedComponent": [], "label": "Disable Add Shortcuts To OneDrive", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Graph API or Portal", + "recommendedBy": [] }, { "name": "standards.DisableSharePointLegacyAuth", "cat": "SharePoint Standards", "tag": ["mediumimpact", "CIS"], "helpText": "Disables the ability to authenticate with SharePoint using legacy authentication methods. Any applications that use legacy authentication will need to be updated to use modern authentication.", + "docsDescription": "Disables the ability for users and applications to access SharePoint via legacy basic authentication. This will likely not have any user impact, but will block systems/applications depending on basic auth or the SharePointOnlineCredentials class.", "addedComponent": [], "label": "Disable legacy basic authentication for SharePoint", "impact": "Medium Impact", - "impactColour": "warning" + "impactColour": "warning", + "powershellEquivalent": "Set-SPOTenant -LegacyAuthProtocolsEnabled $false", + "recommendedBy": ["CIS"] }, { "name": "standards.sharingCapability", @@ -1551,27 +1998,35 @@ ], "label": "Set Sharing Level for OneDrive and Sharepoint", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgBetaAdminSharepointSetting", + "recommendedBy": ["CIS"] }, { "name": "standards.DisableReshare", "cat": "SharePoint Standards", "tag": ["highimpact", "CIS"], "helpText": "Disables the ability for external users to share files they don't own. Sharing links can only be made for People with existing access", + "docsDescription": "Disables the ability for external users to share files they don't own. Sharing links can only be made for People with existing access. This is a tenant wide setting and overrules any settings set on the site level", "addedComponent": [], "label": "Disable Resharing by External Users", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgBetaAdminSharepointSetting", + "recommendedBy": ["CIS"] }, { "name": "standards.DisableUserSiteCreate", "cat": "SharePoint Standards", "tag": ["highimpact"], "helpText": "Disables users from creating new SharePoint sites", + "docsDescription": "Disables standard users from creating SharePoint sites, also disables the ability to fully create teams", "addedComponent": [], "label": "Disable site creation by standard users", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgAdminSharepointSetting", + "recommendedBy": [] }, { "name": "standards.ExcludedfileExt", @@ -1587,7 +2042,9 @@ ], "label": "Exclude File Extensions from Syncing", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgAdminSharepointSetting", + "recommendedBy": [] }, { "name": "standards.disableMacSync", @@ -1597,17 +2054,21 @@ "addedComponent": [], "label": "Do not allow Mac devices to sync using OneDrive", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgAdminSharepointSetting", + "recommendedBy": [] }, { "name": "standards.unmanagedSync", "cat": "SharePoint Standards", "tag": ["highimpact"], - "helpText": "This standard will only allow devices that are AD joined, or AAD joined to sync with OneDrive", + "helpText": "The unmanaged Sync standard has been temporarily disabled and does nothing.", "addedComponent": [], "label": "Only allow users to sync OneDrive from AAD joined devices", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgAdminSharepointSetting", + "recommendedBy": [] }, { "name": "standards.sharingDomainRestriction", @@ -1642,6 +2103,8 @@ ], "label": "Restrict sharing to a specific domain", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgAdminSharepointSetting", + "recommendedBy": [] } ] diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 6cd0031a1c6c..9666488abfc9 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -34,6 +34,8 @@ import React from 'react' "/identity/reports/Signin-report": React.lazy(() => import('./views/identity/reports/SignIns')), "/identity/reports/azure-ad-connect-report": React.lazy(() => import('./views/identity/reports/AzureADConnectReport')), "/identity/reports/risk-detections": React.lazy(() => import('./views/identity/reports/RiskDetections')), + "/tenant/backup/backup-wizard": React.lazy(() => import('./views/tenant/backup/CreateBackup')), + "/tenant/backup/restore-wizard": React.lazy(() => import('./views/tenant/backup/RestoreBackup')), "/tenant/administration/tenants": React.lazy(() => import('./views/tenant/administration/Tenants')), "/tenant/administration/tenants/edit": React.lazy(() => import('./views/tenant/administration/EditTenant')), "/tenant/administration/partner-relationships": React.lazy(() => import('./views/tenant/administration/PartnerRelationships')), @@ -46,6 +48,7 @@ import React from 'react' "/tenant/administration/enterprise-apps": React.lazy(() => import('./views/tenant/administration/ListEnterpriseApps')), "/tenant/administration/app-consent-requests": React.lazy(() => import('./views/tenant/administration/ListAppConsentRequests')), "/tenant/conditional/list-policies": React.lazy(() => import('./views/tenant/conditional/ConditionalAccess')), + "/tenant/administration/authentication-methods": React.lazy(() => import('./views/tenant/administration/AuthMethods')), "/tenant/conditional/deploy-vacation": React.lazy(() => import('./views/tenant/conditional/DeployVacation')), "/tenant/conditional/test-policy": React.lazy(() => import('./views/tenant/conditional/TestCAPolicy')), "/tenant/conditional/list-named-locations": React.lazy(() => import('./views/tenant/conditional/NamedLocations')), @@ -117,6 +120,7 @@ import React from 'react' "/email/administration/view-mobile-devices": React.lazy(() => import('./views/email-exchange/administration/ViewMobileDevices')), "/email/administration/edit-contact": React.lazy(() => import('./views/email-exchange/administration/EditContact')), "/email/administration/mailboxes": React.lazy(() => import('./views/email-exchange/administration/MailboxesList')), + "/email/administration/deleted-mailboxes": React.lazy(() => import('./views/email-exchange/administration/DeletedMailboxes')), "/email/administration/mailbox-rules": React.lazy(() => import('./views/email-exchange/administration/MailboxRuleList')), "/email/administration/Quarantine": React.lazy(() => import('./views/email-exchange/administration/QuarantineList')), "/email/administration/tenant-allow-block-lists": React.lazy(() => import('./views/email-exchange/administration/ListTenantAllowBlockList')), diff --git a/src/routes.json b/src/routes.json index 2975b4c168ac..96584165d77c 100644 --- a/src/routes.json +++ b/src/routes.json @@ -228,6 +228,20 @@ "name": "Administration", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/tenant/backup/backup-wizard", + "name": "Backup", + "component": "views/tenant/backup/CreateBackup", + + "allowedRoles": ["admin", "editor", "readonly"] + }, + { + "path": "/tenant/backup/restore-wizard", + "name": "Restore Backup", + "component": "views/tenant/backup/RestoreBackup", + + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/tenant/administration/tenants", "name": "Tenants", @@ -300,6 +314,12 @@ "component": "views/tenant/conditional/ConditionalAccess", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/tenant/administration/authentication-methods", + "name": "Authentication Methods", + "component": "views/tenant/administration/AuthMethods", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/tenant/conditional/deploy-vacation", "name": "Deploy Vacation Mode", @@ -786,6 +806,12 @@ "component": "views/email-exchange/administration/MailboxesList", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "name": "Deleted Mailboxes", + "path": "/email/administration/deleted-mailboxes", + "component": "views/email-exchange/administration/DeletedMailboxes", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "name": "List Mailbox Rules", "path": "/email/administration/mailbox-rules", diff --git a/src/store/api/groups.js b/src/store/api/groups.js index 385770a4ecd6..59316ad90f02 100644 --- a/src/store/api/groups.js +++ b/src/store/api/groups.js @@ -59,6 +59,16 @@ export const groupsApi = baseApi.injectEndpoints({ params: { tenantFilter: tenantDomain, userId }, }), }), + listGroupSenderAuth: builder.query({ + query: ({ tenantDomain, groupId, type }) => ({ + path: '/api/ListGroupSenderAuthentication', + params: { + TenantFilter: tenantDomain, + GroupId: groupId, + Type: type, + }, + }), + }), }), }) export const { @@ -69,4 +79,5 @@ export const { useListGroupMembersQuery, useListGroupOwnersQuery, useListUserGroupsQuery, + useListGroupSenderAuthQuery, } = groupsApi diff --git a/src/views/cipp/ExtensionMappings.jsx b/src/views/cipp/ExtensionMappings.jsx new file mode 100644 index 000000000000..b0d25677ef2e --- /dev/null +++ b/src/views/cipp/ExtensionMappings.jsx @@ -0,0 +1,393 @@ +import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' +import { CButton, CCallout, CCardText, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' +import { Form } from 'react-final-form' +import { RFFSelectSearch } from 'src/components/forms/index.js' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import React, { useEffect } from 'react' +import { CippCallout } from 'src/components/layout/index.js' +import { CippTable } from 'src/components/tables' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' + +/** + * Retrieves and sets the extension mappings for HaloPSA and NinjaOne. + * + * @returns {JSX.Element} - JSX component representing the settings extension mappings. + */ +export default function ExtensionMappings({ type, fieldMappings = false, autoMapSyncApi = false }) { + const [mappingArray, setMappingArray] = React.useState('defaultMapping') + const [mappingValue, setMappingValue] = React.useState({}) + const [tenantMappingArray, setTenantMappingsArray] = React.useState([]) + const [autoMap, setAutoMap] = React.useState(false) + const [listMappingBackend, listMappingBackendResult = []] = useLazyGenericGetRequestQuery() + const [listFieldsBackend, listFieldsBackendResult] = useLazyGenericGetRequestQuery() + const [setExtensionConfig, extensionConfigResult = []] = useLazyGenericPostRequestQuery() + const [setExtensionAutomap, extensionAutomapResult] = useLazyGenericPostRequestQuery() + const [setFieldsExtensionConfig, extensionFieldsConfigResult] = useLazyGenericPostRequestQuery() + + const onOrgSubmit = () => { + console.log(mappingArray) + const originalFormat = mappingArray.reduce((acc, item) => { + acc[item.Tenant?.customerId] = { label: item.companyName, value: item.companyId } + return acc + }, {}) + setExtensionConfig({ + path: `api/ExecExtensionMapping?AddMapping=${type}`, + values: { mappings: originalFormat }, + }).then(() => { + listMappingBackend({ path: `api/ExecExtensionMapping?List=${type}` }) + setMappingValue({}) + }) + } + + const onOrgsAutomap = async (values) => { + if (autoMapSyncApi) { + await setExtensionAutomap({ + path: `api/ExecExtensionMapping?AutoMapping=${type}`, + values: { mappings: values }, + }) + await listMappingBackend({ + path: `api/ExecExtensionMapping?List=${type}`, + }) + } + + var newMappings = [] + listMappingBackendResult.data?.Tenants.map((tenant) => { + const company = listMappingBackendResult.data?.Companies.find( + (client) => client.name === tenant.displayName, + ) + if (company !== undefined && !mappingArray.find((item) => item.companyId === company.value)) { + newMappings.push({ + Tenant: tenant, + companyName: company.name, + companyId: company.value, + }) + } + }) + setMappingArray((currentMappings) => [...currentMappings, ...newMappings]) + setAutoMap(true) + } + + const onFieldsSubmit = (values) => { + setFieldsExtensionConfig({ + path: `api/ExecExtensionMapping?AddMapping=${type}Fields`, + values: { mappings: values }, + }) + } + + useEffect(() => { + if (listMappingBackendResult.isSuccess) { + setMappingArray( + Object.keys(listMappingBackendResult.data?.Mappings).map((key) => ({ + Tenant: listMappingBackendResult.data?.Tenants.find( + (tenant) => tenant.customerId === key, + ), + companyName: listMappingBackendResult.data?.Mappings[key].label, + companyId: listMappingBackendResult.data?.Mappings[key].value, + })), + ) + } + }, [listMappingBackendResult, setMappingArray]) + + const Actions = (row, rowIndex, formatExtraData) => { + return ( + <> + + + setMappingArray((currentMappings) => currentMappings.filter((item) => item !== row)) + } + > + + + + + ) + } + const columns = [ + { + name: 'Tenant', + selector: (row) => row.Tenant?.displayName, + sortable: true, + cell: (row) => CellTip(row.Tenant?.displayName), + exportSelector: 'Tenant', + }, + { + name: 'TenantId', + selector: (row) => row.Tenant?.customerId, + sortable: true, + exportSelector: 'Tenant/customerId', + omit: true, + }, + { + name: `${type} Company Name`, + selector: (row) => row['companyName'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'companyName', + }, + { + name: `${type} Company ID`, + selector: (row) => row['companyId'], + sortable: true, + cell: (row) => CellTip(row['companyId']), + exportSelector: 'companyId', + }, + { + name: 'Actions', + cell: Actions, + maxWidth: '80px', + }, + ] + + return ( + + <> + {listMappingBackendResult.isUninitialized && + listMappingBackend({ path: `api/ExecExtensionMapping?List=${type}` })} + {listFieldsBackendResult.isUninitialized && + fieldMappings && + listFieldsBackend({ path: `api/ExecExtensionMapping?List=${type}Fields` })} + + + + Save Mappings + + onOrgsAutomap()} + className="me-2" + disabled={listMappingBackendResult.isFetching} + > + + Automap {type} Organizations + + + } + > + {listMappingBackendResult.isFetching && listMappingBackendResult.isUninitialized ? ( + + ) : ( +
{ + return ( + + + Use the table below to map your client to the correct {type} Organization. + { + //load all the existing mappings and show them first in a table. + listMappingBackendResult.isSuccess && ( + + listMappingBackend({ path: `api/ExecExtensionMapping?List=${type}` }) + } + /> + ) + } + + + { + return !Object.keys(listMappingBackendResult.data?.Mappings).includes( + tenant.customerId, + ) + }).map((tenant) => ({ + name: tenant.displayName, + value: tenant.customerId, + }))} + onChange={(e) => setTenantMappingsArray(e.value)} + isLoading={listMappingBackendResult.isFetching} + /> + + + + + + { + return !Object.values(listMappingBackendResult.data?.Mappings) + .map((value) => { + return value.value + }) + .includes(client.value.toString()) + }).map((client) => ({ + name: client.name, + value: client.value, + }))} + onChange={(e) => setMappingValue(e)} + placeholder={`Select a ${type} Organization`} + isLoading={listMappingBackendResult.isFetching} + /> + + { + //set the new mapping in the array + if ( + mappingValue.value !== undefined && + mappingValue.value !== '-1' && + Object.values(mappingArray) + .map((item) => item.companyId) + .includes(mappingValue.value) === false + ) { + setMappingArray([ + ...mappingArray, + { + Tenant: listMappingBackendResult.data?.Tenants.find( + (tenant) => tenant.customerId === tenantMappingArray, + ), + companyName: mappingValue.label, + companyId: mappingValue.value, + }, + ]) + } + }} + className={`my-4 circular-button`} + title={'+'} + > + + + + + + {(extensionAutomapResult.isSuccess || extensionAutomapResult.isError) && + !extensionAutomapResult.isFetching && ( + + {extensionAutomapResult.isSuccess + ? extensionAutomapResult.data.Results + : 'Error'} + + )} + {(extensionConfigResult.isSuccess || extensionConfigResult.isError) && + !extensionConfigResult.isFetching && ( + + {extensionConfigResult.isSuccess + ? extensionConfigResult.data.Results + : 'Error'} + + )} + + + + After editing the mappings you must click Save Mappings for the changes to + take effect. The table will be saved exactly as presented. + + + ) + }} + /> + )} + + + {fieldMappings && ( + + + Save Mappings + + } + > + {listFieldsBackendResult.isFetching && listFieldsBackendResult.isUninitialized && ( + + )} + {listFieldsBackendResult.isSuccess && listFieldsBackendResult.data?.Mappings && ( + { + return ( + + {listFieldsBackendResult?.data?.CIPPFieldHeaders?.map((header, key) => ( + +
{header.Title}
+

{header.Description}

+ {listFieldsBackendResult?.data?.CIPPFields?.filter( + (f) => f.FieldType == header.FieldType, + ).map((field, fieldkey) => ( + + item?.FieldType === field.FieldType || item?.type === 'unset', + )} + placeholder="Select a Field" + /> + ))} +
+ ))} + + {(extensionFieldsConfigResult.isSuccess || + extensionFieldsConfigResult.isError) && + !extensionFieldsConfigResult.isFetching && ( + + {extensionFieldsConfigResult.isSuccess + ? extensionFieldsConfigResult.data.Results + : 'Error'} + + )} + +
+ ) + }} + /> + )} +
+ )} + + ) +} diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx index 7bfe1d7feb86..eec80f5a1068 100644 --- a/src/views/cipp/Extensions.jsx +++ b/src/views/cipp/Extensions.jsx @@ -21,7 +21,7 @@ import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import CippButtonCard from 'src/components/contentcards/CippButtonCard.jsx' import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms/RFFComponents.jsx' import { Form } from 'react-final-form' -import { SettingsExtensionMappings } from './app-settings/SettingsExtensionMappings' +import ExtensionMappings from 'src/views/cipp/ExtensionMappings.jsx' export default function CIPPExtensions() { const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() @@ -42,19 +42,23 @@ export default function CIPPExtensions() { }) } - const ButtonGenerate = (integrationType, forceSync) => ( + const ButtonGenerate = (integrationType, forceSync, disabled) => ( <> - - {extensionConfigResult.isFetching && ( - - )} - Set Extension Settings + + + Save - onSubmitTest(integrationType)} className="me-2"> - {listExtensionTestResult.isFetching && ( - - )} - Test Extension + onSubmitTest(integrationType)} className="me-2"> + + Test {forceSync && ( Force Sync )} @@ -83,6 +90,7 @@ export default function CIPPExtensions() { queryString.set('tab', tab.toString()) navigate(`${location.pathname}?${queryString}`) } + const hostedMetaContent = document.querySelector('meta[name="hosted"]')?.getAttribute('content') return ( @@ -105,12 +113,25 @@ export default function CIPPExtensions() { + {hostedMetaContent === 'true' && integration.disableWhenhosted && ( + + This extension requires activation in the management portal for hosted + clients. + + )}

{integration.helpText}

)} + {listSyncExtensionResult?.data?.Results && ( + + {listSyncExtensionResult?.data?.Results} + + )}
- - - + {integration.mappingRequired && ( + + + + )}
diff --git a/src/views/cipp/app-settings/SettingsBackend.jsx b/src/views/cipp/app-settings/SettingsBackend.jsx index 3dc9a9548af5..57ea62a28000 100644 --- a/src/views/cipp/app-settings/SettingsBackend.jsx +++ b/src/views/cipp/app-settings/SettingsBackend.jsx @@ -70,127 +70,160 @@ export function SettingsBackend() { {title} ) + const hostedMetaContent = document.querySelector('meta[name="hosted"]')?.getAttribute('content') return ( <> - {listBackendResult.isUninitialized && listBackend({ path: 'api/ExecBackendURLs' })} - - {BackendCardList.map((card, index) => ( - - - {card.description} - - - ))} - + {hostedMetaContent === 'true' ? ( + <> - {' '} window.open( - 'https://shell.azure.com/powershell', + 'https://management.cipp.app', '_blank', 'toolbar=no,scrollbars=yes,resizable=yes,menubar=no,location=no,status=no', ) } rel="noreferrer" > - Cloud Shell + Management Portal - setVisible(true)} className="me-2"> - Command Reference - } > -

Launch an Azure Cloud Shell Window

+ The Management Portal allows you to manage your CIPP resources for the hosted + environment.
-
-
- setVisible(false)} - title="Command Reference" - > -
Function App Config
- -
Function App Deployment
- -
Watch Function Logs
- -
Static Web App Config
- -
List CIPP Users
- -
+ + ) : ( + <> + {listBackendResult.isUninitialized && listBackend({ path: 'api/ExecBackendURLs' })} + + {BackendCardList.map((card, index) => ( + + + {card.description} + + + ))} + + + {' '} + + window.open( + 'https://shell.azure.com/powershell', + '_blank', + 'toolbar=no,scrollbars=yes,resizable=yes,menubar=no,location=no,status=no', + ) + } + rel="noreferrer" + > + Cloud Shell + + setVisible(true)} className="me-2"> + Command Reference + + + } + > +

Launch an Azure Cloud Shell Window

+
+
+
+ setVisible(false)} + title="Command Reference" + > +
Function App Config
+ +
Function App Deployment
+ +
Watch Function Logs
+ +
Static Web App Config
+ +
List CIPP Users
+ +
+ + )} ) } diff --git a/src/views/email-exchange/administration/DeletedMailboxes.jsx b/src/views/email-exchange/administration/DeletedMailboxes.jsx new file mode 100644 index 000000000000..0c115973a2b0 --- /dev/null +++ b/src/views/email-exchange/administration/DeletedMailboxes.jsx @@ -0,0 +1,159 @@ +import React, { useState } from 'react' +import { CButton } from '@coreui/react' +import { useSelector } from 'react-redux' +import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { cellBooleanFormatter } from 'src/components/tables' +import { CippPageList } from 'src/components/layout' +import { TitleButton } from 'src/components/buttons' +import { CippActionsOffcanvas } from 'src/components/utilities' + +const Offcanvas = (row, rowIndex, formatExtraData) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [ocVisible, setOCVisible] = useState(false) + + const now = new Date() // Get the current date and time + const requestName = `${row.UPN}-${String(now.getDate()).padStart(2, '0')}-${String( + now.getMonth() + 1, + ).padStart(2, '0')}-${now.getFullYear()}-${String(now.getHours()).padStart(2, '0')}${String( + now.getMinutes(), + ).padStart(2, '0')}` + + return ( + <> + setOCVisible(true)}> + + + setOCVisible(false)} + /> + + ) +} + +const columns = [ + { + name: 'Display Name', + selector: (row) => row['displayName'], + sortable: true, + exportSelector: 'displayName', + }, + { + name: 'User Principal Name', + selector: (row) => row['UPN'], + sortable: true, + exportSelector: 'UPN', + minWidth: '350px', + }, + { + name: 'Primary SMTP Address', + selector: (row) => row['primarySmtpAddress'], + sortable: true, + exportSelector: 'primarySmtpAddress', + minWidth: '350px', + }, + { + name: 'Date Deleted', + selector: (row) => row['WhenSoftDeleted'], + sortable: true, + exportSelector: 'WhenSoftDeleted', + }, + { + name: 'Recipient Type', + selector: (row) => row['recipientType'], + omit: true, + exportSelector: 'recipientType', + }, + { + name: 'Recipient Type Details', + selector: (row) => row['recipientTypeDetails'], + omit: true, + exportSelector: 'recipientTypeDetails', + }, + { + name: 'Additional Email Addresses', + selector: (row) => row['AdditionalEmailAddresses'], + omit: true, + exportSelector: 'AdditionalEmailAddresses', + }, + { + name: 'Exchange Guid', + selector: (row) => row['ExchangeGuid'], + sortable: true, + exportSelector: 'ExchangeGuid', + minWidth: '350px', + }, + { + name: 'Archive Guid', + selector: (row) => row['ArchiveGuid'], + sortable: true, + exportSelector: 'ArchiveGuid', + }, + { + name: 'id', + selector: (row) => row['Id'], + omit: true, + exportSelector: 'Id', + }, + { + name: 'Actions', + cell: Offcanvas, + }, +] + +const DeletedMailboxes = () => { + const tenant = useSelector((state) => state.app.currentTenant) + const titleButton = ( + + ) + return ( + + ) +} + +export default DeletedMailboxes diff --git a/src/views/email-exchange/reports/MailboxStatisticsList.jsx b/src/views/email-exchange/reports/MailboxStatisticsList.jsx index 0a6a4c80e9c0..abe707575e83 100644 --- a/src/views/email-exchange/reports/MailboxStatisticsList.jsx +++ b/src/views/email-exchange/reports/MailboxStatisticsList.jsx @@ -2,6 +2,11 @@ import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' import { CellTip, cellBooleanFormatter } from 'src/components/tables' import { CippPageList } from 'src/components/layout' +import { + CellBytes, + CellBytesToPercentage, + cellBytesFormatter, +} from 'src/components/tables/CellBytes' const MailboxStatsList = () => { const [tenantColumnSet, setTenantColumn] = useState(true) @@ -64,23 +69,32 @@ const MailboxStatsList = () => { exportSelector: 'lastActivityDate', }, { - selector: (row) => (row['storageUsedInBytes'] / 1024 ** 3).toFixed(2), + selector: (row) => row['storageUsedInBytes'], + cell: cellBytesFormatter(), name: 'Used Space (GB)', sortable: true, exportSelector: 'storageUsedInBytes', + exportFormatter: CellBytes, }, { - selector: (row) => (row['prohibitSendReceiveQuotaInBytes'] / 1024 ** 3).toFixed(2), + selector: (row) => row['prohibitSendReceiveQuotaInBytes'], + cell: cellBytesFormatter(), name: 'Quota (GB)', sortable: true, - exportSelector: 'QuotaGB', + exportSelector: 'prohibitSendReceiveQuotaInBytes', + exportFormatter: CellBytes, }, { selector: (row) => Math.round((row.storageUsedInBytes / row.prohibitSendReceiveQuotaInBytes) * 100 * 10) / 10, name: 'Quota Used(%)', sortable: true, - exportSelector: 'QuotaUsed', + exportSelector: 'CippStatus', + exportFormatter: CellBytesToPercentage, + exportFormatterArgs: { + value: 'storageUsedInBytes', + dividedBy: 'prohibitSendReceiveQuotaInBytes', + }, }, { selector: (row) => row['itemCount'], diff --git a/src/views/identity/administration/EditGroup.jsx b/src/views/identity/administration/EditGroup.jsx index 7e81fc66306e..2c74c0542a2c 100644 --- a/src/views/identity/administration/EditGroup.jsx +++ b/src/views/identity/administration/EditGroup.jsx @@ -18,6 +18,7 @@ import { useListGroupMembersQuery, useListGroupOwnersQuery, useListGroupQuery, + useListGroupSenderAuthQuery, } from 'src/store/api/groups' import { useDispatch } from 'react-redux' import { ModalService } from 'src/components/utilities' @@ -88,6 +89,13 @@ const EditGroup = () => { } }, [owners, members, ownersIsSuccess, membersIsSuccess, postResults]) + const { + data: SenderAuth = {}, + isFetching: SenderAuthisFetching, + error: SenderAuthError, + isSuccess: SenderAuthIsSuccess, + } = useListGroupSenderAuthQuery({ tenantDomain, groupId, type: group?.[0]?.calculatedGroupType }) + useEffect(() => { if (!groupId || !tenantDomain) { ModalService.open({ @@ -97,6 +105,7 @@ const EditGroup = () => { setQueryError(true) } }, [groupId, tenantDomain, dispatch]) + const onSubmit = (values) => { const shippedValues = { tenantID: tenantDomain, @@ -154,6 +163,12 @@ const EditGroup = () => { {isSuccess && ( { return ( @@ -304,8 +319,10 @@ const EditGroup = () => { {isFetching && } {isSuccess && ( <> - This is the (raw) information for this group. -
{JSON.stringify(group, null, 2)}
+
+ This is the (raw) information for this group. +
{JSON.stringify(group, null, 2)}
+
)} diff --git a/src/views/identity/administration/OffboardingWizard.jsx b/src/views/identity/administration/OffboardingWizard.jsx index 4d6c496951f8..d7008c071724 100644 --- a/src/views/identity/administration/OffboardingWizard.jsx +++ b/src/views/identity/administration/OffboardingWizard.jsx @@ -82,6 +82,7 @@ const OffboardingWizard = () => { HideFromGAL: values.HideFromGAL, DisableSignIn: values.DisableSignIn, RemoveGroups: values.RemoveGroups, + removeCalendarInvites: values.removeCalendarInvites, RemoveLicenses: values.RemoveLicenses, ResetPass: values.ResetPass, RevokeSessions: values.RevokeSessions, @@ -175,6 +176,7 @@ const OffboardingWizard = () => { + @@ -397,6 +399,14 @@ const OffboardingWizard = () => { icon={props.values.RemoveGroups ? faCheck : faTimes} /> + + Cancel all calendar invites + + Hide from Global Address List { icon={props.values.forward ? faCheck : faTimes} /> + + Delete User + + diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index 0b0ea525497e..c387cd4697c5 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -247,11 +247,13 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { modal: true, modalType: 'POST', modalBody: { - user: row.userPrincipalName, + username: row.userPrincipalName, + userid: row.userPrincipalName, TenantFilter: tenant.defaultDomainName, + DisableForwarding: true, message: row.message, }, - modalUrl: `/api/ExecDisableEmailForward`, + modalUrl: `/api/ExecEmailForward`, modalMessage: 'Are you sure you want to disable forwarding of this users emails?', }, { @@ -688,10 +690,12 @@ const Users = (row) => { modal: true, modalType: 'POST', modalBody: { - user: '!userPrincipalName', + username: '!userPrincipalName', + userid: '!userPrincipalName', TenantFilter: tenant.defaultDomainName, + DisableForwarding: true, }, - modalUrl: `/api/ExecDisableEmailForward`, + modalUrl: `/api/ExecEmailForward`, modalMessage: 'Are you sure you want to disable forwarding of these users emails?', }, { diff --git a/src/views/teams-share/sharepoint/SharepointList.jsx b/src/views/teams-share/sharepoint/SharepointList.jsx index a93bb7c01250..d5691b35f256 100644 --- a/src/views/teams-share/sharepoint/SharepointList.jsx +++ b/src/views/teams-share/sharepoint/SharepointList.jsx @@ -5,7 +5,9 @@ import React, { useState } from 'react' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { CellTip } from 'src/components/tables' +import { cellCopyButtonFormatter } from 'src/components/tables/CellCopyButton' import { CippActionsOffcanvas } from 'src/components/utilities' +import CippCopyToClipboard from 'src/components/utilities/CippCopyToClipboard' const SharepointList = () => { const tenant = useSelector((state) => state.app.currentTenant) @@ -167,6 +169,14 @@ const SharepointList = () => { exportSelector: 'Template', maxWidth: '200px', }, + { + name: 'Automapping URL', + selector: (row) => row['AutoMapUrl'], + sortable: true, + cell: cellCopyButtonFormatter(), + exportSelector: 'AutoMapUrl', + maxWidth: '170px', + }, { name: 'Actions', cell: Offcanvas, diff --git a/src/views/tenant/administration/AuthMethods.jsx b/src/views/tenant/administration/AuthMethods.jsx new file mode 100644 index 000000000000..adc16e49108c --- /dev/null +++ b/src/views/tenant/administration/AuthMethods.jsx @@ -0,0 +1,141 @@ +import React, { useState } from 'react' +import { CButton, CCardBody, CSpinner, CCard, CCardHeader, CCardTitle } from '@coreui/react' +import { useSelector } from 'react-redux' +import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { CippPageList, CippPage } from 'src/components/layout' +import { TitleButton } from 'src/components/buttons' +import { CippActionsOffcanvas } from 'src/components/utilities' +import { useGenericGetRequestQuery } from 'src/store/api/app' +import { CippTable, cellBooleanFormatter } from 'src/components/tables' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' + +const Offcanvas = (row, rowIndex, formatExtraData) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [ocVisible, setOCVisible] = useState(false) + const formatTargets = (targets) => { + if (Array.isArray(targets)) { + return targets.map((target) => JSON.stringify(target)).join(', ') + } + return targets + } + + return ( + <> + setOCVisible(true)}> + + + setOCVisible(false)} + /> + + ) +} + +const columns = [ + { + name: 'id', + selector: (row) => row['id'], + sortable: true, + exportSelector: 'id', + }, + { + name: 'state', + selector: (row) => row['state'], + cell: cellBooleanFormatter({ colourless: false }), + sortable: true, + exportSelector: 'state', + minWidth: '100px', + }, + { + name: 'includeTargets', + selector: (row) => row['includeTargets'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'includeTargets', + }, + { + name: 'excludeTargets', + selector: (row) => row['excludeTargets'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'excludeTargets', + }, + { + name: 'Actions', + cell: Offcanvas, + }, +] + +const AuthenticationMethods = () => { + const tenant = useSelector((state) => state.app.currentTenant) + const { data, isFetching, error, isSuccess, refetch } = useGenericGetRequestQuery({ + path: 'api/ListGraphRequest', + params: { + Endpoint: 'authenticationMethodsPolicy', + TenantFilter: tenant?.defaultDomainName, + }, + }) + return ( + <> + + + + Auth Methods + + + {isFetching && } + {isSuccess && ( + refetch()} + /> + )} + + + + + ) +} + +export default AuthenticationMethods diff --git a/src/views/tenant/administration/GDAPInviteWizard.jsx b/src/views/tenant/administration/GDAPInviteWizard.jsx index 956b8a374f00..5f303a3c827f 100644 --- a/src/views/tenant/administration/GDAPInviteWizard.jsx +++ b/src/views/tenant/administration/GDAPInviteWizard.jsx @@ -114,6 +114,12 @@ const GDAPInviteWizard = () => { const filteredResults = results.data.filter((role) => defaultRolesArray.some((defaultRole) => defaultRole.ObjectId === role.roleDefinitionId), ) + const uniqueFilteredResults = filteredResults.filter( + (role, index, self) => + index === self.findIndex((t) => t.roleDefinitionId === role.roleDefinitionId), + ) + filteredResults.length = 0 + Array.prototype.push.apply(filteredResults, uniqueFilteredResults) setEasyMode(true) const resultsarr = [] setLoopRunning(true) diff --git a/src/views/tenant/backup/CreateBackup.jsx b/src/views/tenant/backup/CreateBackup.jsx new file mode 100644 index 000000000000..faec25e074a3 --- /dev/null +++ b/src/views/tenant/backup/CreateBackup.jsx @@ -0,0 +1,268 @@ +import React, { useState } from 'react' +import { CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' +import { useSelector } from 'react-redux' +import { Field, Form } from 'react-final-form' +import { RFFCFormSwitch } from 'src/components/forms' +import { + useGenericGetRequestQuery, + useLazyGenericGetRequestQuery, + useLazyGenericPostRequestQuery, +} from 'src/store/api/app' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' +import { CippPage, CippPageList } from 'src/components/layout' +import 'react-datepicker/dist/react-datepicker.css' +import { ModalService, TenantSelector } from 'src/components/utilities' +import arrayMutators from 'final-form-arrays' +import { useListConditionalAccessPoliciesQuery } from 'src/store/api/tenants' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { cellBadgeFormatter, cellDateFormatter } from 'src/components/tables' + +const CreateBackup = () => { + const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const [refreshState, setRefreshState] = useState(false) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + + const onSubmit = (values) => { + const startDate = new Date() + startDate.setHours(0, 0, 0, 0) + const unixTime = Math.floor(startDate.getTime() / 1000) - 45 + const shippedValues = { + TenantFilter: tenantDomain, + Name: `CIPP Backup ${tenantDomain}`, + Command: { value: `New-CIPPBackup` }, + Parameters: { backupType: 'Scheduled', ScheduledBackupValues: { ...values } }, + ScheduledTime: unixTime, + Recurrence: { value: '1d' }, + } + genericPostRequest({ + path: '/api/AddScheduledItem?hidden=true&DisallowDuplicateName=true', + values: shippedValues, + }).then((res) => { + setRefreshState(res.requestId) + }) + } + const Offcanvas = (row, rowIndex, formatExtraData) => { + const handleDeleteSchedule = (apiurl, message) => { + ModalService.confirm({ + title: 'Confirm', + body:
{message}
, + onConfirm: () => + ExecuteGetRequest({ path: apiurl }).then((res) => { + setRefreshState(res.requestId) + }), + confirmLabel: 'Continue', + cancelLabel: 'Cancel', + }) + } + let jsonResults + try { + jsonResults = JSON.parse(row.Results) + } catch (error) { + jsonResults = row.Results + } + + return ( + <> + + + handleDeleteSchedule( + `/api/RemoveScheduledItem?&ID=${row.RowKey}`, + 'Do you want to delete this job?', + ) + } + size="sm" + variant="ghost" + color="danger" + > + + + + + ) + } + const columns = [ + { + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + cell: (row) => CellTip(row['Tenant']), + exportSelector: 'Tenant', + }, + { + name: 'Task State', + selector: (row) => row['TaskState'], + sortable: true, + cell: cellBadgeFormatter(), + exportSelector: 'TaskState', + }, + { + name: 'Last executed time', + selector: (row) => row['ExecutedTime'], + sortable: true, + cell: cellDateFormatter({ format: 'relative' }), + exportSelector: 'ExecutedTime', + }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '100px', + }, + ] + + const { + data: users = [], + isFetching: usersIsFetching, + error: usersError, + } = useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + TenantFilter: tenantDomain, + Endpoint: 'users', + $select: 'id,displayName,userPrincipalName,accountEnabled', + $count: true, + $top: 999, + $orderby: 'displayName', + }, + }) + + const { + data: caPolicies = [], + isFetching: caIsFetching, + error: caError, + } = useListConditionalAccessPoliciesQuery({ domain: tenantDomain }) + + return ( + + <> + + + + Create Backup Schedule + {postResults.isFetching && ( + + )} +
+ } + title="Add backup Schedule" + icon={faEdit} + > + { + return ( + +

+ Backups are stored in CIPPs storage and can be restored using the CIPP + Restore Backup Wizard. Backups run daily or on demand by clicking the backup + now button. +

+ + + + {(props) => } + + + +
+
+ + +

Identity

+ + +

Conditional Access

+ +

Intune

+ + + +

CIPP

+ + + +
+
+ {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} + {getResults.isFetching && ( + + Loading + + )} + {getResults.isSuccess && ( + {getResults.data?.Results} + )} + {getResults.isError && ( + + Could not connect to API: {getResults.error.message} + + )} +
    + ) + }} + /> + + + + + + + + + + ) +} + +export default CreateBackup diff --git a/src/views/tenant/backup/RestoreBackup.jsx b/src/views/tenant/backup/RestoreBackup.jsx new file mode 100644 index 000000000000..e2d3d1ec9a61 --- /dev/null +++ b/src/views/tenant/backup/RestoreBackup.jsx @@ -0,0 +1,239 @@ +import React, { useState } from 'react' +import { CCallout, CCol, CListGroup, CListGroupItem, CRow, CSpinner, CTooltip } from '@coreui/react' +import { Field, FormSpy } from 'react-final-form' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExclamationTriangle, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons' +import { useSelector } from 'react-redux' +import { CippCallout, CippWizard } from 'src/components/layout' +import PropTypes from 'prop-types' +import { Condition, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms' +import { TenantSelector } from 'src/components/utilities' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import 'react-datepicker/dist/react-datepicker.css' + +const Error = ({ name }) => ( + + touched && error ? ( + + + {error} + + ) : null + } + /> +) + +Error.propTypes = { + name: PropTypes.string.isRequired, +} + +const OffboardingWizard = () => { + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const { + data: currentBackups = [], + isFetching: currentBackupsIsFetching, + error: currentBackupsError, + } = useGenericGetRequestQuery({ + path: `/api/ExecListBackup?TenantFilter=${tenantDomain}&Type=Scheduled`, + }) + + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + + const handleSubmit = (values) => { + const startDate = new Date() + const unixTime = Math.floor(startDate.getTime() / 1000) - 45 + const shippedValues = { + TenantFilter: tenantDomain, + Name: `CIPP Restore ${tenantDomain}`, + Command: { value: `New-CIPPRestore` }, + Parameters: { Type: 'Scheduled', RestoreValues: { ...values } }, + ScheduledTime: unixTime, + PostExecution: { + Webhook: values.webhook, + Email: values.email, + PSA: values.psa, + }, + } + genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => {}) + } + + return ( + + +
    +

    Step 1

    +
    Choose a tenant
    +
    +
    + {(props) => } +
    +
    + +
    +

    Step 2

    +
    Select the backup to restore
    +
    +
    +
    + ({ + value: backup.RowKey, + name: `${backup.RowKey}`, + }))} + placeholder={!currentBackupsIsFetching ? 'Select a backup' : 'Loading...'} + name="backup" + /> + {currentBackupsError && Failed to load list of Current Backups} +
    +
    +
    + +
    +

    Step 3

    +
    Choose restore options
    +
    +
    +
    + + +

    Identity

    + + +

    Conditional Access

    + +
    + +

    Intune

    + + + +

    CIPP

    + + + +
    +
    +
    + + + + + + +
    Warning
    +

    + Overwriting existing entries will remove the current settings and replace them + with the backup settings. If you have selected to restore users, all properties + will be overwritten with the backup settings. +

    + +

    + To prevent and skip already existing entries, deselect the setting from the list + above, or disable overwrite. +

    +
    +
    +
    +
    + + + + + + + + +
    +
    +
    + +
    +

    Step 4

    +
    Confirm and apply
    +
    +
    +
    + {postResults.isFetching && ( + + Loading + + )} + {postResults.isSuccess && {postResults.data.Results}} + {!postResults.isSuccess && ( + + {/* eslint-disable react/prop-types */} + {(props) => ( + <> + + + + +
    Selected Tenant:
    + {tenantDomain} +
    + +
    Selected Backup:
    + {props.values.backup.value} +
    +
    +
    +
    +
    + + + + + Overwrite existing configuration + + + + Send results to Webhook + + + + Send results to E-Mail + + + + Send results to PSA + + + + + + + )} +
    + )} +
    +
    +
    +
    + ) +} + +export default OffboardingWizard diff --git a/version_latest.txt b/version_latest.txt index 99a8b57b6f85..5fe60723048a 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.9.3 +6.0.1