import React, { createContext, useReducer, useMemo, ReactNode } from "react"; import { IConfig, IUserSettings } from "interfaces/config"; import { IEnrollSecret } from "interfaces/enroll_secret"; import { APP_CONTEXT_ALL_TEAMS_SUMMARY, ITeamSummary, APP_CONTEXT_NO_TEAM_SUMMARY, APP_CONTEXT_NO_TEAM_ID, } from "interfaces/team"; import { IUser } from "interfaces/user"; import permissions from "utilities/permissions"; import sort from "utilities/sort"; import { hasLicenseExpired, willExpireWithinXDays } from "utilities/helpers"; enum ACTIONS { SET_AVAILABLE_TEAMS = "SET_AVAILABLE_TEAMS", SET_USER_SETTINGS = "SET_USER_SETTINGS", SET_CURRENT_USER = "SET_CURRENT_USER", SET_CURRENT_TEAM = "SET_CURRENT_TEAM", SET_CONFIG = "SET_CONFIG", SET_ENROLL_SECRET = "SET_ENROLL_SECRET", SET_ANDROID_ENTERPRISE_DELETED = "SET_ANDROID_ENTERPRISE_DELETED", SET_ABM_EXPIRY = "SET_ABM_EXPIRY", SET_APNS_EXPIRY = "SET_APNS_EXPIRY", SET_VPP_EXPIRY = "SET_VPP_EXPIRY", SET_SANDBOX_EXPIRY = "SET_SANDBOX_EXPIRY", SET_NO_SANDBOX_HOSTS = "SET_NO_SANDBOX_HOSTS", SET_FILTERED_HOSTS_PATH = "SET_FILTERED_HOSTS_PATH", SET_FILTERED_SOFTWARE_PATH = "SET_FILTERED_SOFTWARE_PATH", SET_FILTERED_QUERIES_PATH = "SET_FILTERED_QUERIES_PATH", SET_FILTERED_POLICIES_PATH = "SET_FILTERED_POLICIES_PATH", } interface ISetAvailableTeamsAction { type: ACTIONS.SET_AVAILABLE_TEAMS; user: IUser | null; availableTeams: ITeamSummary[]; } interface ISetUserSettingsAction { type: ACTIONS.SET_USER_SETTINGS; userSettings: IUserSettings; } interface ISetConfigAction { type: ACTIONS.SET_CONFIG; config: IConfig; } interface ISetCurrentTeamAction { type: ACTIONS.SET_CURRENT_TEAM; currentTeam: ITeamSummary | undefined; } interface ISetCurrentUserAction { type: ACTIONS.SET_CURRENT_USER; currentUser: IUser; } interface ISetEnrollSecretAction { type: ACTIONS.SET_ENROLL_SECRET; enrollSecret: IEnrollSecret[]; } interface ISetAndroidEnterpriseDeletedAction { type: ACTIONS.SET_ANDROID_ENTERPRISE_DELETED; isDeleted: boolean; } interface IAbmExpiry { earliestExpiry: string; needsAbmTermsRenewal: boolean; } interface ISetABMExpiryAction { type: ACTIONS.SET_ABM_EXPIRY; abmExpiry: IAbmExpiry; } interface ISetAPNsExpiryAction { type: ACTIONS.SET_APNS_EXPIRY; apnsExpiry: string; } interface ISetVppExpiryAction { type: ACTIONS.SET_VPP_EXPIRY; vppExpiry: string; } interface ISetSandboxExpiryAction { type: ACTIONS.SET_SANDBOX_EXPIRY; sandboxExpiry: string; } interface ISetNoSandboxHostsAction { type: ACTIONS.SET_NO_SANDBOX_HOSTS; noSandboxHosts: boolean; } interface ISetFilteredHostsPathAction { type: ACTIONS.SET_FILTERED_HOSTS_PATH; filteredHostsPath: string; } interface ISetFilteredSoftwarePathAction { type: ACTIONS.SET_FILTERED_SOFTWARE_PATH; filteredSoftwarePath: string; } interface ISetFilteredQueriesPathAction { type: ACTIONS.SET_FILTERED_QUERIES_PATH; filteredQueriesPath: string; } interface ISetFilteredPoliciesPathAction { type: ACTIONS.SET_FILTERED_POLICIES_PATH; filteredPoliciesPath: string; } type IAction = | ISetAvailableTeamsAction | ISetUserSettingsAction | ISetConfigAction | ISetCurrentTeamAction | ISetCurrentUserAction | ISetEnrollSecretAction | ISetAndroidEnterpriseDeletedAction | ISetABMExpiryAction | ISetAPNsExpiryAction | ISetVppExpiryAction | ISetSandboxExpiryAction | ISetNoSandboxHostsAction | ISetFilteredHostsPathAction | ISetFilteredSoftwarePathAction | ISetFilteredQueriesPathAction | ISetFilteredPoliciesPathAction; type Props = { children: ReactNode; }; type InitialStateType = { availableTeams?: ITeamSummary[]; userSettings?: IUserSettings; config: IConfig | null; currentUser: IUser | null; currentTeam?: ITeamSummary; enrollSecret: IEnrollSecret[] | null; isPreviewMode?: boolean; isSandboxMode?: boolean; isFreeTier?: boolean; isPremiumTier?: boolean; isMacMdmEnabledAndConfigured?: boolean; isWindowsMdmEnabledAndConfigured?: boolean; isAndroidMdmEnabledAndConfigured?: boolean; isGlobalAdmin?: boolean; isGlobalMaintainer?: boolean; isGlobalObserver?: boolean; isOnGlobalTeam?: boolean; isAnyTeamObserverPlus?: boolean; isAnyTeamMaintainer?: boolean; isAnyTeamMaintainerOrTeamAdmin?: boolean; isTeamObserver?: boolean; isTeamMaintainer?: boolean; isTeamMaintainerOrTeamAdmin?: boolean; isAnyTeamAdmin?: boolean; isTeamAdmin?: boolean; isOnlyObserver?: boolean; isObserverPlus?: boolean; isNoAccess?: boolean; isAndroidEnterpriseDeleted: boolean; isAppleBmExpired: boolean; isApplePnsExpired: boolean; isVppExpired: boolean; needsAbmTermsRenewal: boolean; willAppleBmExpire: boolean; willApplePnsExpire: boolean; willVppExpire: boolean; abmExpiry?: IAbmExpiry; apnsExpiry?: string; vppExpiry?: string; sandboxExpiry?: string; noSandboxHosts?: boolean; filteredHostsPath?: string; filteredSoftwarePath?: string; filteredQueriesPath?: string; filteredPoliciesPath?: string; isVppEnabled?: boolean; setAvailableTeams: ( user: IUser | null, availableTeams: ITeamSummary[] ) => void; setUserSettings: (userSettings: IUserSettings) => void; setCurrentUser: (user: IUser) => void; setCurrentTeam: (team?: ITeamSummary) => void; setConfig: (config: IConfig) => void; setEnrollSecret: (enrollSecret: IEnrollSecret[]) => void; setAndroidEnterpriseDeleted: (isDeleted: boolean) => void; setAPNsExpiry: (apnsExpiry: string) => void; setABMExpiry: (abmExpiry: IAbmExpiry) => void; setVppExpiry: (vppExpiry: string) => void; setSandboxExpiry: (sandboxExpiry: string) => void; setNoSandboxHosts: (noSandboxHosts: boolean) => void; setFilteredHostsPath: (filteredHostsPath: string) => void; setFilteredSoftwarePath: (filteredSoftwarePath: string) => void; setFilteredQueriesPath: (filteredQueriesPath: string) => void; setFilteredPoliciesPath: (filteredPoliciesPath: string) => void; }; export type IAppContext = InitialStateType; export const initialState = { availableTeams: undefined, userSettings: undefined, config: null, currentUser: null, currentTeam: undefined, enrollSecret: null, isPreviewMode: false, isSandboxMode: false, isFreeTier: undefined, isPremiumTier: undefined, isMacMdmEnabledAndConfigured: undefined, isWindowsMdmEnabledAndConfigured: undefined, isAndroidMdmEnabledAndConfigured: undefined, isGlobalAdmin: undefined, isGlobalMaintainer: undefined, isGlobalObserver: undefined, isOnGlobalTeam: undefined, isAnyTeamObserverPlus: undefined, isAnyTeamMaintainer: undefined, isAnyTeamMaintainerOrTeamAdmin: undefined, isTeamObserver: undefined, isTeamMaintainer: undefined, isTeamMaintainerOrTeamAdmin: undefined, isAnyTeamAdmin: undefined, isTeamAdmin: undefined, isOnlyObserver: undefined, isObserverPlus: undefined, isNoAccess: undefined, filteredHostsPath: undefined, filteredSoftwarePath: undefined, filteredQueriesPath: undefined, filteredPoliciesPath: undefined, isAndroidEnterpriseDeleted: false, isAppleBmExpired: false, isApplePnsExpired: false, isVppExpired: false, needsAbmTermsRenewal: false, willAppleBmExpire: false, willApplePnsExpire: false, willVppExpire: false, setAvailableTeams: () => null, setUserSettings: () => null, setCurrentUser: () => null, setCurrentTeam: () => null, setConfig: () => null, setEnrollSecret: () => null, setAndroidEnterpriseDeleted: () => null, setAPNsExpiry: () => null, setABMExpiry: () => null, setVppExpiry: () => null, setSandboxExpiry: () => null, setNoSandboxHosts: () => null, setFilteredHostsPath: () => null, setFilteredSoftwarePath: () => null, setFilteredQueriesPath: () => null, setFilteredPoliciesPath: () => null, }; const detectPreview = () => { return window.location.origin === "http://localhost:1337"; }; // helper function - this is run every // time currentUser, currentTeam, config, or teamId is changed const setPermissions = ( user: IUser | null, config: IConfig | null, teamId = APP_CONTEXT_NO_TEAM_ID ) => { if (!user || !config) { return {}; } if (teamId < APP_CONTEXT_NO_TEAM_ID) { teamId = APP_CONTEXT_NO_TEAM_ID; } return { isSandboxMode: permissions.isSandboxMode(config), isFreeTier: permissions.isFreeTier(config), isPremiumTier: permissions.isPremiumTier(config), isMacMdmEnabledAndConfigured: permissions.isMacMdmEnabledAndConfigured( config ), isWindowsMdmEnabledAndConfigured: permissions.isWindowsMdmEnabledAndConfigured( config ), isAndroidMdmEnabledAndConfigured: permissions.isAndroidMdmEnabledAndConfigured( config ), isGlobalAdmin: permissions.isGlobalAdmin(user), isGlobalMaintainer: permissions.isGlobalMaintainer(user), isGlobalObserver: permissions.isGlobalObserver(user), isOnGlobalTeam: permissions.isOnGlobalTeam(user), isAnyTeamObserverPlus: permissions.isAnyTeamObserverPlus(user), isAnyTeamMaintainer: permissions.isAnyTeamMaintainer(user), isAnyTeamMaintainerOrTeamAdmin: permissions.isAnyTeamMaintainerOrTeamAdmin( user ), isAnyTeamAdmin: permissions.isAnyTeamAdmin(user), isTeamObserver: permissions.isTeamObserver(user, teamId), isTeamMaintainer: permissions.isTeamMaintainer(user, teamId), isTeamAdmin: permissions.isTeamAdmin(user, teamId), isTeamMaintainerOrTeamAdmin: permissions.isTeamMaintainerOrTeamAdmin( user, teamId ), isOnlyObserver: permissions.isOnlyObserver(user), isObserverPlus: permissions.isObserverPlus(user, teamId), isNoAccess: permissions.isNoAccess(user), }; }; const reducer = (state: InitialStateType, action: IAction) => { switch (action.type) { case ACTIONS.SET_USER_SETTINGS: { const { userSettings } = action; return { ...state, userSettings, }; } case ACTIONS.SET_AVAILABLE_TEAMS: { const { user, availableTeams } = action; let sortedTeams = availableTeams.sort( (a: ITeamSummary, b: ITeamSummary) => sort.caseInsensitiveAsc(a.name, b.name) ); sortedTeams = sortedTeams.filter( (t) => t.name !== APP_CONTEXT_ALL_TEAMS_SUMMARY.name && t.name !== APP_CONTEXT_NO_TEAM_SUMMARY.name ); if (user && permissions.isOnGlobalTeam(user)) { sortedTeams.unshift( APP_CONTEXT_ALL_TEAMS_SUMMARY, APP_CONTEXT_NO_TEAM_SUMMARY ); } return { ...state, availableTeams: sortedTeams, }; } case ACTIONS.SET_CURRENT_USER: { const { currentUser } = action; return { ...state, currentUser, ...setPermissions(currentUser, state.config, state.currentTeam?.id), }; } case ACTIONS.SET_CURRENT_TEAM: { const { currentTeam } = action; return { ...state, currentTeam, ...setPermissions(state.currentUser, state.config, currentTeam?.id), }; } case ACTIONS.SET_CONFIG: { const { config } = action; return { ...state, config, ...setPermissions(state.currentUser, config, state.currentTeam?.id), }; } case ACTIONS.SET_ENROLL_SECRET: { const { enrollSecret } = action; return { ...state, enrollSecret, }; } case ACTIONS.SET_ANDROID_ENTERPRISE_DELETED: { const { isDeleted } = action; return { ...state, isAndroidEnterpriseDeleted: isDeleted, }; } case ACTIONS.SET_ABM_EXPIRY: { const { abmExpiry } = action; const { earliestExpiry, needsAbmTermsRenewal } = abmExpiry; return { ...state, abmExpiry, isAppleBmExpired: hasLicenseExpired(earliestExpiry), willAppleBmExpire: willExpireWithinXDays(earliestExpiry, 30), needsAbmTermsRenewal, }; } case ACTIONS.SET_APNS_EXPIRY: { const { apnsExpiry } = action; return { ...state, apnsExpiry, isApplePnsExpired: hasLicenseExpired(apnsExpiry), willApplePnsExpire: willExpireWithinXDays(apnsExpiry, 30), }; } case ACTIONS.SET_VPP_EXPIRY: { const { vppExpiry } = action; return { ...state, vppExpiry, isVppExpired: hasLicenseExpired(vppExpiry), willVppExpire: willExpireWithinXDays(vppExpiry, 30), }; } case ACTIONS.SET_SANDBOX_EXPIRY: { const { sandboxExpiry } = action; return { ...state, sandboxExpiry, }; } case ACTIONS.SET_NO_SANDBOX_HOSTS: { const { noSandboxHosts } = action; return { ...state, noSandboxHosts, }; } case ACTIONS.SET_FILTERED_HOSTS_PATH: { const { filteredHostsPath } = action; return { ...state, filteredHostsPath, }; } case ACTIONS.SET_FILTERED_SOFTWARE_PATH: { const { filteredSoftwarePath } = action; return { ...state, filteredSoftwarePath, }; } case ACTIONS.SET_FILTERED_QUERIES_PATH: { const { filteredQueriesPath } = action; return { ...state, filteredQueriesPath, }; } case ACTIONS.SET_FILTERED_POLICIES_PATH: { const { filteredPoliciesPath } = action; return { ...state, filteredPoliciesPath, }; } default: return state; } }; export const AppContext = createContext(initialState); const AppProvider = ({ children }: Props): JSX.Element => { const [state, dispatch] = useReducer(reducer, initialState); const value = useMemo( () => ({ availableTeams: state.availableTeams, userSettings: state.userSettings, config: state.config, currentUser: state.currentUser, currentTeam: state.currentTeam, enrollSecret: state.enrollSecret, sandboxExpiry: state.sandboxExpiry, abmExpiry: state.abmExpiry, apnsExpiry: state.apnsExpiry, vppExpiry: state.vppExpiry, isAndroidEnterpriseDeleted: state.isAndroidEnterpriseDeleted, isAppleBmExpired: state.isAppleBmExpired, isApplePnsExpired: state.isApplePnsExpired, isVppExpired: state.isVppExpired, needsAbmTermsRenewal: state.needsAbmTermsRenewal, willAppleBmExpire: state.willAppleBmExpire, willApplePnsExpire: state.willApplePnsExpire, willVppExpire: state.willVppExpire, noSandboxHosts: state.noSandboxHosts, filteredHostsPath: state.filteredHostsPath, filteredSoftwarePath: state.filteredSoftwarePath, filteredQueriesPath: state.filteredQueriesPath, filteredPoliciesPath: state.filteredPoliciesPath, isPreviewMode: detectPreview(), isSandboxMode: state.isSandboxMode, isFreeTier: state.isFreeTier, isPremiumTier: state.isPremiumTier, isMacMdmEnabledAndConfigured: state.isMacMdmEnabledAndConfigured, isWindowsMdmEnabledAndConfigured: state.isWindowsMdmEnabledAndConfigured, isAndroidMdmEnabledAndConfigured: state.isAndroidMdmEnabledAndConfigured, isGlobalAdmin: state.isGlobalAdmin, isGlobalMaintainer: state.isGlobalMaintainer, isGlobalObserver: state.isGlobalObserver, isOnGlobalTeam: state.isOnGlobalTeam, isAnyTeamObserverPlus: state.isAnyTeamObserverPlus, isAnyTeamMaintainer: state.isAnyTeamMaintainer, isAnyTeamMaintainerOrTeamAdmin: state.isAnyTeamMaintainerOrTeamAdmin, isTeamObserver: state.isTeamObserver, isTeamMaintainer: state.isTeamMaintainer, isTeamAdmin: state.isTeamAdmin, isTeamMaintainerOrTeamAdmin: state.isTeamMaintainerOrTeamAdmin, isAnyTeamAdmin: state.isAnyTeamAdmin, isOnlyObserver: state.isOnlyObserver, isObserverPlus: state.isObserverPlus, isNoAccess: state.isNoAccess, setAvailableTeams: ( user: IUser | null, availableTeams: ITeamSummary[] ) => { dispatch({ type: ACTIONS.SET_AVAILABLE_TEAMS, user, availableTeams, }); }, setUserSettings: (userSettings: IUserSettings) => { dispatch({ type: ACTIONS.SET_USER_SETTINGS, userSettings }); }, setCurrentUser: (currentUser: IUser) => { dispatch({ type: ACTIONS.SET_CURRENT_USER, currentUser }); }, setCurrentTeam: (currentTeam: ITeamSummary | undefined) => { dispatch({ type: ACTIONS.SET_CURRENT_TEAM, currentTeam }); }, setConfig: (config: IConfig) => { dispatch({ type: ACTIONS.SET_CONFIG, config }); }, setEnrollSecret: (enrollSecret: IEnrollSecret[]) => { dispatch({ type: ACTIONS.SET_ENROLL_SECRET, enrollSecret }); }, setAndroidEnterpriseDeleted: (isDeleted: boolean) => { dispatch({ type: ACTIONS.SET_ANDROID_ENTERPRISE_DELETED, isDeleted, }); }, setABMExpiry: (abmExpiry: IAbmExpiry) => { dispatch({ type: ACTIONS.SET_ABM_EXPIRY, abmExpiry }); }, setAPNsExpiry: (apnsExpiry: string) => { dispatch({ type: ACTIONS.SET_APNS_EXPIRY, apnsExpiry }); }, setVppExpiry: (vppExpiry: string) => { dispatch({ type: ACTIONS.SET_VPP_EXPIRY, vppExpiry, }); }, setSandboxExpiry: (sandboxExpiry: string) => { dispatch({ type: ACTIONS.SET_SANDBOX_EXPIRY, sandboxExpiry }); }, setNoSandboxHosts: (noSandboxHosts: boolean) => { dispatch({ type: ACTIONS.SET_NO_SANDBOX_HOSTS, noSandboxHosts, }); }, setFilteredHostsPath: (filteredHostsPath: string) => { dispatch({ type: ACTIONS.SET_FILTERED_HOSTS_PATH, filteredHostsPath }); }, setFilteredSoftwarePath: (filteredSoftwarePath: string) => { dispatch({ type: ACTIONS.SET_FILTERED_SOFTWARE_PATH, filteredSoftwarePath, }); }, setFilteredQueriesPath: (filteredQueriesPath: string) => { dispatch({ type: ACTIONS.SET_FILTERED_QUERIES_PATH, filteredQueriesPath, }); }, setFilteredPoliciesPath: (filteredPoliciesPath: string) => { dispatch({ type: ACTIONS.SET_FILTERED_POLICIES_PATH, filteredPoliciesPath, }); }, }), [ state.abmExpiry, state.apnsExpiry, state.availableTeams, state.userSettings, state.config, state.currentTeam, state.currentUser, state.enrollSecret, state.filteredHostsPath, state.filteredPoliciesPath, state.filteredQueriesPath, state.filteredSoftwarePath, state.isAnyTeamAdmin, state.isAnyTeamMaintainer, state.isAnyTeamMaintainerOrTeamAdmin, state.isAnyTeamObserverPlus, state.isAndroidEnterpriseDeleted, state.isAppleBmExpired, state.isApplePnsExpired, state.isFreeTier, state.isGlobalAdmin, state.isGlobalMaintainer, state.isGlobalObserver, state.isMacMdmEnabledAndConfigured, state.isNoAccess, state.isObserverPlus, state.isOnGlobalTeam, state.isOnlyObserver, state.isPremiumTier, state.isSandboxMode, state.isTeamAdmin, state.isTeamMaintainer, state.isTeamMaintainerOrTeamAdmin, state.isTeamObserver, state.isVppExpired, state.isWindowsMdmEnabledAndConfigured, state.isAndroidMdmEnabledAndConfigured, state.needsAbmTermsRenewal, state.noSandboxHosts, state.sandboxExpiry, state.vppExpiry, state.willAppleBmExpire, state.willApplePnsExpire, state.willVppExpire, ] ); return {children}; }; export default AppProvider;