diff --git a/package.json b/package.json index 5a62c63..07c98cf 100644 --- a/package.json +++ b/package.json @@ -77,5 +77,8 @@ "ts-jest": "^26.4.4", "ts-node": "^9.0.0", "typescript": "^4.1.2" + }, + "dependencies": { + "path-to-regexp": "^6.2.0" } } diff --git a/src/authentication/protected-routes.handler.ts b/src/authentication/protected-routes.handler.ts index 06be588..b2321ba 100644 --- a/src/authentication/protected-routes.handler.ts +++ b/src/authentication/protected-routes.handler.ts @@ -1,5 +1,7 @@ import AdminJS, { Router as AdminRouter } from "adminjs"; import { Router } from "express"; +import { convertToExpressRoute } from "../convertRoutes"; +import { pathToRegexp } from "path-to-regexp"; export const withProtectedRoutesHandler = ( router: Router, @@ -8,7 +10,7 @@ export const withProtectedRoutesHandler = ( const { rootPath } = admin.options; router.use((req, res, next) => { - if (AdminRouter.assets.find((asset) => req.originalUrl.match(asset.path))) { + if (isAdminAsset(req.originalUrl)) { next(); } else if ( req.session.adminUser || @@ -17,18 +19,36 @@ export const withProtectedRoutesHandler = ( req.originalUrl.startsWith(admin.options.logoutPath) ) { next(); - } else { + } else if (isAdminRoute(req.originalUrl, rootPath)) { // If the redirection is caused by API call to some action just redirect to resource const [redirectTo] = req.originalUrl.split("/actions"); req.session.redirectTo = redirectTo.includes(`${rootPath}/api`) ? rootPath : redirectTo; + req.session.save((err) => { if (err) { next(err); } res.redirect(admin.options.loginPath); }); + } else { + next(); } }); }; + +export const isAdminRoute = (url: string, adminRootUrl: string): boolean => { + const adminRoutes = AdminRouter.routes + .map((route) => convertToExpressRoute(route.path)) + .filter((route) => route !== ""); + const isAdminRootUrl = url === adminRootUrl; + + return ( + isAdminRootUrl || + !!adminRoutes.find((route) => pathToRegexp(route).test(url)) + ); +}; + +const isAdminAsset = (url: string) => + AdminRouter.assets.find((asset) => url.match(asset.path)); diff --git a/src/buildRouter.ts b/src/buildRouter.ts index 1b71d95..e45e651 100644 --- a/src/buildRouter.ts +++ b/src/buildRouter.ts @@ -5,6 +5,7 @@ import path from "path"; import { WrongArgumentError } from "./errors"; import { log } from "./logger"; import { FormidableOptions } from "./types"; +import { convertToExpressRoute } from "./convertRoutes"; const INVALID_ADMINJS_INSTANCE = "You have to pass an instance of AdminJS to the buildRouter() function"; @@ -28,7 +29,7 @@ export const buildRouter = ( routes.forEach((route) => { // we have to change routes defined in AdminJS from {recordId} to :recordId - const expressPath = route.path.replace(/{/g, ":").replace(/}/g, ""); + const expressPath = convertToExpressRoute(route.path); const handler: RequestHandler = async (req, res, next) => { try { diff --git a/src/convertRoutes.ts b/src/convertRoutes.ts new file mode 100644 index 0000000..847b788 --- /dev/null +++ b/src/convertRoutes.ts @@ -0,0 +1,2 @@ +export const convertToExpressRoute = (adminRoute: string): string => + adminRoute.replace(/{/g, ":").replace(/}/g, ""); diff --git a/test/protected-routes.test.ts b/test/protected-routes.test.ts new file mode 100644 index 0000000..0b626b1 --- /dev/null +++ b/test/protected-routes.test.ts @@ -0,0 +1,36 @@ +import { isAdminRoute } from "../src/authentication/protected-routes.handler"; + +describe("Protected routes", () => { + describe("#isAdminRoute", () => { + it("should detect admin routes", () => { + const adminRoutes = [ + "/", + "/resources/someResource", + "/api/resources/someResource/search/searchQuery", + "/resources/someResource/actions/someAction", + "/api/resources/someResource/actions/someAction", + "/api/resources/someResource/actions/someAction/searchQuery", + "/api/resources/someResource/actions/someAction", + "/resources/someResource/records/someRecordId/someAction", + "/api/resources/someResource/records/someRecordId/someAction", + "/api/resources/someResource/records/someRecordId/someAction", + "/resources/someResource/bulk/someAction", + "/api/resources/someResource/bulk/someAction", + "/api/resources/someResource/bulk/someAction", + "/api/resources/someResource/search/", + "/api/dashboard", + "/pages/somePage", + "/api/pages/somePage", + "/api/pages/somePage", + ]; + + adminRoutes.forEach((route) => { + expect(isAdminRoute(route, "/")).toBeTruthy(); + }); + }); + + it("should detect non-admin routes", () => { + expect(isAdminRoute("/api/my-endpoint", "/")).toBeFalsy(); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index eab804c..f3248ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8431,6 +8431,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" + integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== + path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"