diff --git a/package.json b/package.json index 96a3916..56834b8 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "build": "microbundle build --tsconfig ./tsconfig.json --name VueBrowserAcl", "build:watch": "microbundle watch --tsconfig ./tsconfig.json --name VueBrowserAcl", "test": "jest --config jest.config.json", - "test:watch": "jest jest --config jest.config.json --watchAll", + "test:watch": "jest --config jest.config.json --watchAll", "version": "npm run build && git add -A ./dist", "postversion": "git push && git push --tags" }, @@ -37,19 +37,18 @@ "browser-acl": "^0.9.1" }, "devDependencies": { - "@babel/core": "^7.9.6", - "@babel/preset-env": "^7.9.6", - "@nuxt/types": "^0.7.6", - "@types/jest": "^25.2.3", - "@vue/test-utils": "^1.0.3", - "babel-jest": "^26.0.1", - "jest": "^26.0.1", - "microbundle": "^0.12.0", - "ts-jest": "^26.0.0", - "typescript": "^3.9.3", - "vue": "^2.6.11", - "vue-router": "^3.2.0", - "vue-template-compiler": "^2.6.11" + "@babel/core": "^7.12.13", + "@babel/preset-env": "^7.12.13", + "@types/jest": "^26.0.20", + "@vue/test-utils": "^1.1.3", + "babel-jest": "^26.6.3", + "jest": "^26.6.3", + "microbundle": "^0.13.0", + "ts-jest": "^26.5.0", + "typescript": "^4.1.3", + "vue": "^2.6.12", + "vue-router": "^3.5.1", + "vue-template-compiler": "^2.6.12" }, "prettier": { "semi": false, diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index 750f698..5a37d96 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -3,6 +3,7 @@ import { User } from '../../types' import Acl from 'browser-acl' import { Verb } from '../../types' import VueAcl from '../index' +import VueRouter, { RouteConfig } from 'vue-router' let _user: User | null @@ -26,7 +27,7 @@ describe('Bad setup', () => { beforeEach(() => { jest.spyOn(console, 'error') // @ts-ignore - console.error.mockImplementation(() => {}) + console.error.mockImplementation(() => { }) }) afterEach(() => { @@ -54,6 +55,110 @@ describe('Bad setup', () => { }) }) + +const routes: RouteConfig[] = [ + { + path: "*", + redirect: '/home' + }, + { + path: '/home', + component: { + render: (c) => c('div', "Normal Route") + }, + meta: { + can: 'see-home' + } + }, + { + path: '/nocanroute', + component: { + render: (c) => c('div', "Shouldn't be here in strict mode") + } + }, + { + path: '/fallback', + component: { + render: (c) => c('div', "Fallback Route") + }, + meta: { + can: true + } + + } +] + +describe('Router integration', () => { + test('Strict mode directs to fallback route with no meta', async () => { + const localVue = createLocalVue() + localVue.use(VueRouter) + + const router = new VueRouter({ routes: routes, mode: 'hash' }) + + router.push(""); + localVue.use(VueAcl, getUser, (acl: Acl) => { + acl.rule('see-home', true) + }, { router: router, failRoute: '/fallback', strict: true, assumeGlobal: true }) + + const wrapper = mount( + { + template: ` + + ` + }, + { localVue, router }, + ) + + await wrapper.vm.$nextTick(); + + expect(wrapper.html()).toContain('Normal Route') + + router.push('/nocanroute').catch((err) => { + expect(err).toBe('[Error: Redirected when going from "/home" to "/nocanroute" via a navigation guard.]') + }) + + await wrapper.vm.$nextTick(); + + const html = wrapper.html(); + + expect(html).toContain('Fallback Route') + + }), + test('Normal mode passes route with no meta', async () => { + const localVue = createLocalVue() + localVue.use(VueRouter) + + const router = new VueRouter({ routes: routes, mode: 'hash'}) + + router.push(""); + localVue.use(VueAcl, getUser, (acl: Acl) => { + acl.rule('see-home', true) + }, { router: router, failRoute: '/fallback'}) + + const wrapper = mount( + { + template: ` + + ` + }, + { localVue, router }, + ) + + await wrapper.vm.$nextTick(); + + expect(wrapper.html()).toContain('Normal Route') + + router.push('/nocanroute'); + + await wrapper.vm.$nextTick(); + + const html = wrapper.html(); + + expect(html).toContain("Shouldn't be here in strict mode") + + }) +}) + describe('Global rules', () => { test('True for all', () => { const localVue = createLocalVue() diff --git a/src/index.ts b/src/index.ts index e9ac3c8..c5c58c8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -148,10 +148,20 @@ const VueAcl: VueAcl = { } router.beforeEach((to: Route, from: Route, next: any) => { + //prevent infinite loop if the fallback route contains no can meta + if(opt.strict && to.path === opt.failRoute) { + return next(); + } + const metas = to.matched .filter((route) => route.meta && findCan(route.meta)) .map((route) => route.meta) - + + // enforce strict mode + if(metas.length === 0 && opt.strict) { + next(opt.failRoute) + } + const chain = chainCans(metas, to, from) chain.then((result) => {