{"version":3,"file":"PrivateRoute.mjs","names":["React","useMemo","Route","RouteProps","useTheme","useTranslation","RedirectRequest","InteractionType","InteractionStatus","MsalAuthenticationTemplate","useMsal","useIsUnityAuthenticated","useUser","PageForbiddenRoute","useMenuContext","useRegisterRoute","LoadingIndicator","PasswordResetRedirect","getLoginRequest","useTenant","storeRedirectPath","useAxiosGet","ConfigService","PageLoading","FORBIDDEN_REASONS","Record","key","defaultValue","INACTIVE_MENU","MISSING_ROLES","PrivateRouteProps","rolesMap","Map","menuActiveMap","PrivateRoute","propRolesMap","propMenuActiveMap","routeProps","location","isPublic","isAuthenticated","inProgress","user","tenant","theme","t","menuContext","size","unknownRoute","path","mapProvesAccess","Boolean","has","pathname","get","length","isSystemAdmin","isUnitySystemAdmin","shouldCheckRoute","userId","activeTenant","tenantId","data","routeAccessResponse","loading","routeAccessLoading","error","routeAccessError","tenantV1ApiUrl","encodeURIComponent","userRoles","Array","isArray","roles","map","role","roleId","filter","userHasRoles","routeRoles","some","element","includes","menuIsActive","isUserInitialized","isTenantInitialized","isThemeInitialized","isInitialized","shouldWaitForMenu","leftMenu","leftMenuError","shouldWaitForProtectedContext","shouldWaitForTheme","Startup","HandleRedirect","None","search","loginRequest","Redirect","props","hasMsalError","errorMessage","test","window","hash","replace","undefined","isRouteForbidden","reason","forbiddenMessage"],"sources":["../../src/routes/PrivateRoute.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport { Route, RouteProps } from 'react-router-dom';\nimport { useTheme } from '../utilities/theme';\nimport { useTranslation } from 'react-i18next';\nimport type { RedirectRequest } from '@azure/msal-browser';\nimport { InteractionType, InteractionStatus } from '@azure/msal-browser';\nimport { MsalAuthenticationTemplate, useMsal } from '@azure/msal-react';\nimport { useIsUnityAuthenticated } from '../utilities/auth/useIsUnityAuthenticated';\nimport { useUser } from '../utilities/auth/useUser';\nimport { PageForbiddenRoute } from './components/PageForbiddenRoute';\nimport { useMenuContext } from '../utilities/menus/MenuProvider';\nimport { useRegisterRoute } from './useRegisterRoute';\nimport { LoadingIndicator } from '../UI/loading/LoadingIndicator';\nimport { PasswordResetRedirect } from './components/PasswordResetRedirect';\nimport { getLoginRequest } from '../utilities/auth/signInAuthProvider';\nimport { useTenant } from '../utilities/tenant';\nimport { storeRedirectPath } from '../utilities/redirect/LocalRedirectUrlStorage';\nimport { useAxiosGet } from '../utilities';\nimport { ConfigService } from '../configService';\nimport { PageLoading } from '../UI';\n\nconst FORBIDDEN_REASONS: Record<string, { key: string; defaultValue: string }> =\n  {\n    INACTIVE_MENU: {\n      key: 'routes.forbidden.inactiveMenu',\n      defaultValue:\n        'Trying to access inactive menu. Please check your menu configurations'\n    },\n    MISSING_ROLES: {\n      key: 'routes.forbidden.missingRoles',\n      defaultValue:\n        'You do not have the required roles to access this page. Please contact an administrator to get access'\n    }\n  };\n\n/**\n * Route guard for protected pages.\n *\n * - Triggers login redirect when the user is not authenticated.\n * - Blocks rendering until user/tenant/theme are initialized after login.\n * - Enforces menu activation and role-based access.\n */\nexport interface PrivateRouteProps extends RouteProps {\n  /**\n   * Optional map of pathname -> required role IDs.\n   * If a path is present and has roles, the user must have at least one.\n   */\n  rolesMap?: Map<string, string[]>;\n  /**\n   * Optional map of pathname -> menu active status.\n   * If a path is present and inactive, access is denied.\n   */\n  menuActiveMap?: Map<string, boolean>;\n}\n\nexport const PrivateRoute = ({\n  rolesMap: propRolesMap,\n  menuActiveMap: propMenuActiveMap,\n  ...routeProps\n}: PrivateRouteProps) => {\n  const location = useRegisterRoute({ isPublic: false });\n  const isAuthenticated = useIsUnityAuthenticated();\n  const { inProgress } = useMsal();\n  const user = useUser();\n  const tenant = useTenant();\n  const theme = useTheme();\n  const { t } = useTranslation();\n  const menuContext = useMenuContext?.();\n  const rolesMap = menuContext?.rolesMap?.size ? menuContext.rolesMap : propRolesMap;\n  const menuActiveMap = menuContext?.menuActiveMap?.size ? menuContext.menuActiveMap : propMenuActiveMap;\n\n  // The catch-all route: no menu backs it, so there is nothing to authorize helps avoid checking mistyped paths.\n  const unknownRoute = routeProps?.path === '*';\n\n  // The maps can prove access which saves a request. The backend\n  // filters restricted paths out of the maps, so a path being present means the user\n  // may access it — but only when the menu is active and the role list is empty\n  // (empty array = no role restrictions).\n  const mapProvesAccess =\n    Boolean(rolesMap?.has(routeProps.location?.pathname)) &&\n    menuActiveMap?.get(routeProps.location?.pathname) === true &&\n    !rolesMap?.get(routeProps.location?.pathname)?.length;\n\n  const isSystemAdmin = Boolean(user?.isUnitySystemAdmin);\n\n  // Inverted into useAxiosGet's isManual below, so the request only fires once\n  const shouldCheckRoute =\n    isAuthenticated &&\n    Boolean(routeProps.location?.pathname) &&\n    Boolean(user?.userId) &&\n    Boolean(tenant?.activeTenant?.tenantId) &&\n    !mapProvesAccess &&\n    !unknownRoute;\n\n  const [\n    {\n      data: routeAccessResponse,\n      loading: routeAccessLoading,\n      error: routeAccessError\n    }\n  ] = useAxiosGet(\n    ConfigService.tenantV1ApiUrl,\n    `menus/tenant/${tenant?.activeTenant?.tenantId}/routeForbidden?route=${encodeURIComponent(routeProps.location?.pathname ?? '')}&userId=${user?.userId}`,\n    {},\n    !shouldCheckRoute\n  ) as [{ data?: string; loading: boolean; error?: unknown }, unknown];\n\n  const userRoles = useMemo(() => {\n    return Array.isArray(user?.roles)\n      ? user.roles.map((role) => role.roleId).filter(Boolean)\n      : [];\n  }, [user?.roles]);\n\n  const userHasRoles = useMemo(() => {\n    if (rolesMap?.size > 0) {\n      if (!rolesMap.has(routeProps.location?.pathname)) {\n        return false;\n      }\n      const routeRoles = rolesMap.get(routeProps.location?.pathname);\n      if (!routeRoles?.length) {\n        return true;\n      }\n      return routeRoles.some((element) => userRoles.includes(element));\n    }\n    return true;\n  }, [userRoles, rolesMap, routeProps.location?.pathname]);\n\n  const menuIsActive = useMemo(() => {\n    if (\n      menuActiveMap?.size > 0 &&\n      menuActiveMap.has(routeProps.location?.pathname)\n    ) {\n      return menuActiveMap.get(routeProps.location?.pathname);\n    }\n  }, [menuActiveMap, routeProps.location?.pathname]);\n\n  const activeTenant = tenant?.activeTenant ?? null;\n  const isUserInitialized = Boolean(user?.userId);\n  const isTenantInitialized = Boolean(activeTenant?.tenantId);\n  const isThemeInitialized = Boolean(theme?.isInitialized);\n  const shouldWaitForMenu = isAuthenticated && !user?.isUnitySystemAdmin && !menuContext?.leftMenu && !menuContext?.leftMenuError;\n\n  const shouldWaitForProtectedContext =\n    isAuthenticated && (!isUserInitialized || !isTenantInitialized);\n  const shouldWaitForTheme = isAuthenticated && !isThemeInitialized;\n  if (shouldWaitForProtectedContext || shouldWaitForTheme) {\n    return <LoadingIndicator />;\n  }\n\n  if (!isAuthenticated) {\n    // Prevent login redirect loops while MSAL is still processing the redirect response.\n    if (inProgress === InteractionStatus.Startup || inProgress === InteractionStatus.HandleRedirect) {\n      return <LoadingIndicator />;\n    }\n    if (inProgress !== InteractionStatus.None) {\n      return <LoadingIndicator />;\n    }\n    storeRedirectPath(location.pathname, location.search);\n    let loginRequest: RedirectRequest | null = null;\n    try {\n      loginRequest = getLoginRequest();\n    } catch {\n      loginRequest = null;\n    }\n\n    if (!loginRequest) {\n      return <LoadingIndicator />;\n    }\n\n    return (\n      <MsalAuthenticationTemplate\n        interactionType={InteractionType.Redirect}\n        authenticationRequest={loginRequest}\n        loadingComponent={() => <LoadingIndicator />}\n        errorComponent={(props) => {\n          if (inProgress !== InteractionStatus.None) {\n            return <LoadingIndicator />;\n          }\n          const hasMsalError =\n            Boolean(props?.error?.errorMessage) ||\n            /(^|&)(error|error_description)=/.test(\n              window.location.hash?.replace(/^#/, '') ?? ''\n            );\n          if (!hasMsalError) {\n            return <LoadingIndicator />;\n          }\n          return <PasswordResetRedirect {...props} />;\n        }}\n      >\n        <Route {...routeProps} />\n      </MsalAuthenticationTemplate>\n    );\n  }\n\n  if (\n    (shouldCheckRoute &&\n      routeAccessResponse === undefined &&\n      !routeAccessError) ||\n    routeAccessLoading ||\n    shouldWaitForMenu\n  ) {\n    return <PageLoading />;\n  }\n\n  // An inactive menu blocks system admins too, but missing roles do not, so the reason has to be checked explicitly\n  const isRouteForbidden =\n    shouldCheckRoute &&\n    Boolean(routeAccessResponse) &&\n    (!isSystemAdmin || routeAccessResponse === 'INACTIVE_MENU');\n\n  if (isRouteForbidden) {\n    const reason = FORBIDDEN_REASONS[routeAccessResponse];\n    const forbiddenMessage = reason\n      ? t(reason.key, { defaultValue: reason.defaultValue })\n      : routeAccessResponse;\n    return (\n      <Route\n        path='*'\n        render={() => (\n          <PageForbiddenRoute message={forbiddenMessage} showButton={false} />\n        )}\n      />\n    );\n  }\n\n  if (isUserInitialized) {\n    return <Route {...routeProps} />;\n  }\n\n  return <LoadingIndicator />;\n};\n"],"mappings":"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"}