meteor-vue / vue-meteor

🌠 Vue first-class integration in Meteor
897 stars 112 forks source link

Vue-router and alanning:roles #103

Open suezzo opened 7 years ago

suezzo commented 7 years ago

Hello, I am working on admin panel for my app. I use alanning:roles package. I've created authentication for user, but I have problem with adding authorization. My code looked like below:

router.beforeEach((transition) => {
  if(!Meteor.userId()) {
    router.go('/login')
  }
  transition.next();
});

This was working perfect. I tried to add authorization based on roles:

router.beforeEach((transition) => {
  if(!Meteor.userId() || Roles.userIsInRole(Meteor.userId(), 'admin')) {
    router.go('/login')
  }
  transition.next();
});

Roles.userIsInRole(Meteor.userId(), 'admin') always returns false. I have no idea how possible solution could look. Any help will be much appreciated.

Thanks in advance

Akryum commented 7 years ago

Could try this code?

Tracker.autorun(() => {
  console.log('role.admin', Roles.userIsInRole(Meteor.userId(), 'admin'))
})
suezzo commented 7 years ago

I tried your code, and at first it shows false in console, and after 1-2 secs it shows true. I tried to wrap my code in Tracker.autorun, but when condition is processing returned value is false, so now it redirects to /login everytime.

qroac commented 7 years ago

I am about to use vue instead of react for next projects and found this ticket.
Is it possible to tell the router to wait?

In my last project I had a simmilar issue using kadira:flow-router and react.
It happens because the roles package relies on a roles-subscription that isn't ready when the router does its work. I solved it for flow-router with this snippet on beginning of the client startup scripts:

FlowRouter.wait();
Tracker.autorun(() => {
  // Wait for the roles subscrition to be ready before rendering the UI
  if (Roles.subscription.ready() && !FlowRouter._initialized){
    return FlowRouter.initialize();
  }
});

Is it possible to do something simmilar with vue-router, too?

Akryum commented 7 years ago

You can use asynchronous navigation guards.

CharlesMcKeever commented 7 years ago

@suezzo did the suggestion to use navigation guards solve the issue you were having with alanning:roles and Vue in Meteor? I'm considering using the same setup and am curious to know. Thanks in advance.

janat08 commented 7 years ago

asynchonous navigation guards, i don't know what they are. using guard with meteor-subs-cache that just lets you hook for all subs doesn't appear to mean that cursor fetch resolves before then. Setting something that checks if stuff has resolved would be helpful, or plugging vue into meteor tracker better so that it knows to rerun.

thearabbit commented 7 years ago

I have the same problem. @Akryum, could example for this (asynchronous navigation guards). I tried

router.beforeResolve((to, from, next) => {
            // Check user
            if (!Meteor.loggingIn() && !Meteor.userId()) {
                next({path: '/login'});
            } else {
                // Check role
                Tracker.autorun(() => {
                    let loggedInUser = Meteor.userId();
                    let role = Roles.userIsInRole(loggedInUser, ['posts.insert'], 'Entry');

                    if (role) {
                        next();
                    }else{
                       alert('no role'); 
                   }
                });
            }
    });

Please advice...

thearabbit commented 7 years ago

It always alert no role when refresh page.

janat08 commented 7 years ago

It should alert since else condition will just run on first load. What's not working is that it will fire next() letting user load the templates even if role is in fact undefined.

thearabbit commented 7 years ago

Thanks for your reply. could example how to solve (I don't understand)?

janat08 commented 7 years ago

I have no idea. In your case try to use subscription ready handle from roles package. I myself can't tell why if subscription fires ready handle the data isn't available in vue.

thearabbit commented 7 years ago

do you have any solution to manage Role in Vue? for example we check it outside of router...

janat08 commented 7 years ago

The idea is not render anything until you got your data. Might be issue with autopublish or something.

thearabbit commented 7 years ago

Now I tries

// Method
export const userIsInRole = new ValidatedMethod({
    name: 'userIsInRole',
    mixins: [CallPromiseMixin],
    validate: new SimpleSchema({
        role: {type: String},
    }).validator(),
    run({role}) {
        if (!this.isSimulation) {
            return Roles.userIsInRole(Meteor.userId(), role);
        }
    }
});

---------
// router
router.beforeResolve((to, from, next) => {
            // Check user
            if (!Meteor.loggingIn() && !Meteor.userId()) {
                next({path: '/login'});
            } else {
                userIsInRole.callPromise({role: to.name}).then((result) => {
                    if (result) {
                        next();
                    } else {
                        next(false);
                        alert('no roles');
                    }
                }).catch((err) => {
                    console.log(err.reason);
                });
            }
    });

It work fine, please advice...