UNPKG

@azure/static-web-apps-cli

Version:
141 lines 6.98 kB
import chalk from "chalk"; import fs from "fs"; import path from "path"; import { DEFAULT_CONFIG } from "../../../config.js"; import { decodeCookie } from "../../../core/utils/cookie.js"; import { logger } from "../../../core/utils/logger.js"; import { ALLOWED_HTTP_METHODS_FOR_STATIC_CONTENT, AUTH_STATUS, SWA_CLI_APP_PROTOCOL } from "../../../core/constants.js"; import { isAuthRequest } from "../../handlers/auth.handler.js"; import { doesRequestPathMatchLegacyRoute, doesRequestPathMatchRoute } from "../route-processor.js"; export function tryFindFileForRequest(rawRequestPath) { logger.silly(`finding file for request`); // percent decode request path let requestPath = decodeURIComponent(rawRequestPath); if (requestPath.endsWith("/")) { requestPath = getIndexHtml(requestPath); } logger.silly(` - requestPath: ${chalk.yellow(requestPath)}`); if (DEFAULT_CONFIG.outputLocation) { const filePath = path.join(DEFAULT_CONFIG.outputLocation, requestPath); logger.silly(` - filePath: ${chalk.yellow(filePath)}`); const isFileExists = fs.existsSync(filePath); logger.silly(` - exists: ${chalk.yellow(isFileExists)}`); return isFileExists ? requestPath : null; } return null; } export function isRouteRequiringUserRolesCheck(req, matchingRoute, isFunctionRequest, authStatus) { logger.silly(`checking authorizations for route`); if (!matchingRoute) { logger.silly(` - no matching rule`); logger.silly(` - access authorized`); return true; } if (matchingRoute.allowedRoles?.length === 0) { logger.silly(` - no allowedRoles provided`); logger.silly(` - access authorized`); return true; } const shouldCheckRoles = Boolean(authStatus != AUTH_STATUS.HostNameAuthLogin && matchingRoute.allowedRoles && matchingRoute.allowedRoles.length > 0 && !matchingRoute.allowedRoles.includes("anonymous")); logger.silly(` - shouldCheckRoles: ${chalk.yellow(shouldCheckRoles)}`); const shouldLookupAuthCookie = shouldCheckRoles || isFunctionRequest || authStatus == AUTH_STATUS.AuthMe || authStatus == AUTH_STATUS.HostNameAuthPurge; logger.silly(` - shouldLookupAuthCookie: ${chalk.yellow(shouldLookupAuthCookie)}`); if (shouldLookupAuthCookie) { const clientPrincipalInternal = req.headers?.cookie ? decodeCookie(req.headers?.cookie) : null; const doesAuthCookieExist = !!clientPrincipalInternal; if (shouldCheckRoles && !doesAuthCookieExist) { logger.silly(` - secure route found but cookies not found`); logger.silly(` - access not authorized`); return false; } const userRoles = clientPrincipalInternal?.userRoles; logger.silly(` - userRoles: ${chalk.yellow(userRoles?.length ? userRoles : "<empty>")}`); logger.silly(` - allowedRoles: ${chalk.yellow(matchingRoute.allowedRoles)}`); logger.silly(matchingRoute); const matchedRoles = userRoles?.filter((value) => matchingRoute.allowedRoles?.includes(value)); logger.silly(` - matchedRoles: ${chalk.yellow(matchedRoles?.length ? matchedRoles : "<empty>")}`); const isUserAuthenticatedOrAnonymous = matchedRoles?.length > 0; logger.silly(` - isUserAuthenticatedOrAnonymous: ${chalk.yellow(isUserAuthenticatedOrAnonymous)}`); if (shouldCheckRoles && !isUserAuthenticatedOrAnonymous) { logger.silly(` - secure route found but roles don't match`); logger.silly({ allowedRoles: matchingRoute.allowedRoles }); logger.silly(` - access not authorized`); return false; } } logger.silly(` - access authorized`); return true; } export function applyRedirectResponse(req, res, matchedRoute) { const redirect = matchedRoute?.redirect; if (redirect && redirect !== req.url) { const statusCodeToServe = parseInt(`${matchedRoute?.statusCode}`, 10) === 301 ? 301 : 302; res.statusCode = statusCodeToServe; res.setHeader("Location", redirect); logger.silly(` - will redirect to ${chalk.yellow(redirect)} (statusCode: ${chalk.yellow(statusCodeToServe)})`); } } export function tryGetMatchingRoute(req, userConfig) { const host = `${SWA_CLI_APP_PROTOCOL}://${req?.headers?.host}`; const sanitizedUrl = new URL(req.url, host); const requestPathFileExtension = path.extname(sanitizedUrl.toString()); const isFileRequest = !!requestPathFileExtension; const requestMethod = req.method; const isLegacyConfigFile = userConfig?.isLegacyConfigFile; if (userConfig?.routes?.length === 0) { return; } let routeDef = undefined; for (let i = 0; i < userConfig?.routes?.length; i++) { routeDef = userConfig?.routes[i]; let route = routeDef?.route; if (!route) { // this is an invalid route, ignore it continue; } const isMatchingRoute = isLegacyConfigFile ? doesRequestPathMatchLegacyRoute(sanitizedUrl.pathname, routeDef, isAuthRequest(req), isFileRequest) : doesRequestPathMatchRoute(sanitizedUrl.pathname, routeDef, requestMethod, routeDef?.methods, AUTH_STATUS.NoAuth /* TODO get the right auth status */); if (isMatchingRoute) { // if the rule isn't a redirect rule, no need to check for circular redirect if (!routeDef?.redirect) { return routeDef; } // this rule will result in an infinite redirect loop so keep searching for another rule const redirectUrl = new URL(routeDef.redirect || "/", host); if (sanitizedUrl.toString() === redirectUrl.pathname || sanitizedUrl.toString() === redirectUrl.toString()) { continue; } routeDef = { ...routeDef, redirect: redirectUrl.toString(), }; return routeDef; } } return; } export function isRequestMethodValid(req, isFunctionRequest, isAuth, isDataApiReq) { logger.silly(`checking HTTP method: ${chalk.yellow(req.method)}`); if (isFunctionRequest || isAuth || isDataApiReq) { logger.silly(` - function or auth or data-api request detected, method is valid`); return true; } if (ALLOWED_HTTP_METHODS_FOR_STATIC_CONTENT.includes(req.method)) { logger.silly(` - method is valid (allow-list: ${chalk.yellow(ALLOWED_HTTP_METHODS_FOR_STATIC_CONTENT.join(","))})`); return true; } // Deny everything else logger.silly(` - method is invalid. Deny request`); return false; } export function getIndexHtml(requestPath) { if (requestPath?.toLocaleLowerCase().endsWith("index.html") || requestPath?.includes(".")) { return requestPath; } return requestPath?.endsWith("/") ? `${requestPath}index.html` : `${requestPath}/index.html`; } //# sourceMappingURL=routes.js.map