Meteor-Community-Packages / meteor-roles

Authorization package for Meteor, compatible with built-in accounts packages
http://meteor-community-packages.github.io/meteor-roles/
MIT License
921 stars 168 forks source link

Server side check return false #154

Closed sahanDissanayake closed 8 years ago

sahanDissanayake commented 8 years ago

Hi @alanning

I'm using this on react ssr

I do this check Roles.userIsInRole(Meteor.user(), 'admin')

This returns true on clientside but false on server side.. Any thoughts ?

aardvarkk commented 8 years ago

I'm having a similar issue, it seems? When the server does a hot reload a check fails on a page, but if I manually visit it the check succeeds. It's the exact same query (I've checked), but it returns no results in one case and returns the correct result in another? Could it have something to do with the state of the database while a hot code reload is happening?

alanning commented 8 years ago

I can't think of a case where client-side would give a positive result but server-side would fail. Usually its the opposite due to the time it takes for data to get to the client.

What are the contents of the user record's roles field in the database?

Can you post a reproduction somewhere for me to dig in further?

aardvarkk commented 8 years ago

I’m new to Meteor, but I will try to produce a reproduction for you. Thanks!

On Dec 13, 2015, at 9:23 PM, Adrian Lanning notifications@github.com wrote:

I can't think of a case where client-side would give a positive result but server-side would fail. Usually its the opposite due to the time it takes for data to get to the client.

What are the contents of the user record's roles field in the database?

Can you post a reproduction somewhere for me to dig in further?

— Reply to this email directly or view it on GitHub https://github.com/alanning/meteor-roles/issues/154#issuecomment-164237389.

aardvarkk commented 8 years ago

Is this the wrong way to go about redirecting users who try to access an invalid route with Iron Router? Perhaps this is the source of my problem?

Router.route("/private", {
  onBeforeAction: function() {
    if (!Roles.userIsInRole(Meteor.userId(), "admin")) {
      Router.go("/public");
    } else {
      this.next();
    }
  },
  action: function () {
    this.render("private");
  }
});
alanning commented 8 years ago

I think that should work if onBeforeAction is reactive but I haven't used that specific syntax before. You can check out how I did it in this iron-router example:

https://github.com/alanning/meteor-roles/blob/master/examples/iron-router/client/routing.js

Better yet, you can avoid doing it in the routes entirely if you implement it like this example:

https://github.com/alanning/meteor-roles/tree/master/examples/flow-router-advanced

The neat thing about this pattern is that you can do it the same way whether you use IronRouter or FlowRouter. It should make your app a lot easier to work with and refactor.

aardvarkk commented 8 years ago

I think in my case it's some kind of race condition issue between client and server. It's funny: I was able to recreate it last night but not this morning. Then I added a delay function in the routing to the server and now it seems to display the odd behaviour again. I'll paste code below for you to try. What happens is that you're effectively unable to access the the "private" page. You get redirected to the public page even if you're supposed to have access. Try going to /private even when logged in as the admin user and it doesn't work.

My best guess is that the client attempts to process the routing first, but since the roles aren't published it redirects itself back to the public page. The server (assuming it runs later, which is why I added the manual delay) somehow doesn't redirect the client back to the private page even though it should have access. Does that make sense? Does it sound like what's happening?

repro.html

<body>
{{> loginButtons}}
</body>

public.html

<template name="public">
You're on a public page
</template>

private.html

<template name="private">
You're on a private page
</template>

repro.js

if (Meteor.isServer) {
  if (Meteor.users.find().count() <= 0) {
    Accounts.createUser({
      username: "nonadmin",
      email: "email@address.com",
      password: "password"
    });
    var adminId = Accounts.createUser({
      username: "admin",
      email: "important@critical.com",
      password: "verysecret"
    });
    Roles.addUsersToRoles(adminId, "admin");
  }
}

Router.route("/public", function() {
  this.render("public");
});

Router.route("/private", {
  onBeforeAction: function() {

    // wait
    if (Meteor.isServer) {
      var Future = Npm.require('fibers/future');
      var future = new Future();
      Meteor.setTimeout(function() {
        future.return();
      }, 1000);
      future.wait();
    }

    if (!Roles.userIsInRole(Meteor.userId(), "admin")) {
      Router.go("/public");
    } else {
      this.next();
    }
  },
  action: function () {
    this.render("private");
  }
});
alanning commented 8 years ago

@aardvarkk Yes, it sounds like a race condition, not sure why its not re-running for you.

This is not the same thing as what @sahanDissanayake was reporting. Please see my reply to your other posting which includes links to a presentation explaining what is happening and examples of how to avoid it: https://github.com/alanning/meteor-roles/issues/152#issuecomment-164235804

@sahanDissanayake I'm closing this issue for now but feel free to reopen if you can post a public repro or more info on the scenario you were seeing.

alanning commented 8 years ago

To follow up on this, my talk on FlowRouter Auth is now up on the Meteor YouTube channel: https://www.youtube.com/watch?v=KK_ua6vxkP4