Closed Kinvaras closed 7 years ago
Hi @Kinvaras,
Thanks for the feedback, I am really happy that you like vue-kindergarten
!
I think in this case it's better to user per-component guards:
<template>
<div>Hello</div>
</template>
<script>
import myPerimeter from '@/perimeters/my';
import MyComponentGoverness from '@/governesses/MyComponentGoverness';
export default {
perimeters: [
myPerimeter,
],
governess: new MyComponentGoverness(),
beforeRouteEnter(to, from, next) {
next((vm) => {
vm.$guard('view', { to, from, next });
});
},
};
</script>
Please see my comment for more information.
Hi @JiriChara,
Thanks for your reply. Actually, you have confirmed what I found on my own (using per-component governesses).
Just a little difference, I usually tend to separate the concerns as far as I can. In this case, all my component-specific governesses are used directly in the routes declaration, instead of the components themselves.
This way, components and governesses remain decoupled. In the following example, I use meta
attribute of a route record to forward the right governess:
// router.js
// Callback for redirection based on role
// This way, you're automatically redirected to the right route
const indexRedirect = function (to) {
if (store.getters['auth/isAdmin']) {
return {
name: 'admin'
}
}
return {
name: 'dashboard'
};
};
const routes = [
{
path: '/',
component: AppContent,
children: [
{
path: '',
name: 'index',
component: Layout,
meta: {
governess: AuthenticatedRouteGoverness
},
redirect: indexRedirect,
children: [
{
path: 'dashboard',
name: 'dashboard',
component: Dashboard,
meta: {
governess: RegularUserRouteGoverness
}
},
{
path: 'admin',
name: 'admin',
component: Admin,
meta: {
governess: AdminRouteGoverness
}
}
]
},
{
path: 'login',
name: 'login',
component: Login,
meta: {
governess: UnauthenticatedRouteGoverness
}
},
{
path: 'unauthorized',
name: 'unauthorized',
component: Unauthorized
},
{
path: '*',
name: 'default',
redirect: {
name: 'index'
}
}
]
}
];
const router = new VueRouter({
mode: 'history',
routes
});
This code shows how I declare which governess to use. The next step consists on instantiating this governess using router.beforeEach()
the same way you use it for perimeters.
// router.js
router.beforeEach((to, from, next) => {
let hasBeenSandboxed = false;
let Governess = RouteGoverness;
to.matched.forEach((routeRecord) => {
const perimeter = perimeters[`${routeRecord.name}Perimeter`];
if (perimeter) {
if (routeRecord.meta && routeRecord.meta.governess) {
Governess = routeRecord.meta.governess;
}
const sandbox = createSandbox(child(store), {
governess: new Governess({from, to, next}),
perimeters: [
perimeter
]
});
hasBeenSandboxed = true;
return sandbox.guard('route');
}
});
if (!hasBeenSandboxed) {
return next();
}
});
By the way, I added a check on whether the record has been sandboxed to avoid multiple potentially opposite routing decision:
if (!hasBeenSandboxed) {
return next();
}
If no governess is present for a record, I use a default one (the classical RouteGoverness
you provide in your example).
Here's a sample of RouteGoverness
and AuthenticatedRouteGoverness
to give an idea:
// RouteGoverness.js
import { HeadGoverness } from 'vue-kindergarten';
export default class RouteGoverness extends HeadGoverness {
constructor({from, to, next}) {
super();
this.next = next;
this.from = from;
this.to = to;
}
guard(action) {
if (super.isNotAllowed(action)) {
return this.redirectTo('unauthorized');
}
return this.next();
}
redirectTo(routeName) {
this.next({
name: routeName
});
}
};
// AuthenticatedRouteGoverness.js
import RouteGoverness from './RouteGoverness';
export default class AuthenticatedRouteGoverness extends RouteGoverness {
constructor({from, to, next}) {
super({from, to, next});
}
guard(action) {
if (super.isNotAllowed(action)) {
return this.redirectTo('login');
}
return this.next();
}
};
As you can see, AuthenticatedRouteGoverness extends RouteGoverness so it can access redirectTo()
method.
The same logic can be extended to perimeters I guess.
Hope this can help ;)
@Kinvaras this is pretty cool! I will try to find some time this or next week to add this to README, or to create a documentation page.
I'm glad I could help, you gave some strong advices as well ;)
I have updated my example so the router is now consistent for efficient routing.
I added an indexRedirect
callback for some sort of "automatic" routing.
The "index" named route now acts as a magic route, you can simply redirect your user to it (using the name, not the path) in order for kindergarten to handle a role based redirection for you
@Kinvaras good work! I am closing this issue for now. Feel free to re-open or raise a new issue if you have any more questions.
Hi @JiriChara,
First, thanks for this lib, which is very interesting to use.
I am stuck in my authorization based application using your lib. In your excellent article, you say:
In order to handle a "not allowed" case correctly, I have implemented the
guard()
method of my RouteGoverness according to your example. However, I don't have just one default behavior but several different behaviors based on "why you were not allowed to do this".In other words, I would like to be able to:
/login
if the child is not authenticated/home
if the child is authenticated/admin/home
if the child is adminand so on...
The logic is actually executed by the
guard()
method of my RouteGoverness and I am pretty sure there's a better way to do it. On top of that, it doesn't work as expected.Here's a sample of what I tried to do:
Is there a way to do it efficiently or am I wrong about the process ? Thanks.