使用'Meteor'的'roles'包无法将角色添加给用户

15

我想使用Atmosphere上提供的'roles'包,但是我无法在Accounts.onCreateUser()中使用它,我可以在github上找到示例。当我注册一个用户时,我想要为他们添加一个角色,但是在测试角色是否被分配时,它没有起作用。

这是我的代码

/server/users.js

Accounts.onCreateUser(function(options, user){
  var role = ['admin'];
  Roles.addUsersToRoles(user, role);
  return user;
});

/客户端/page.js

Template.hello.events({
  'click input': function () {
    var loggedInUser = Meteor.user();
    if (Roles.userIsInRole(loggedInUser, ['admin'])) {
      console.log("Hi Admin!");
    }else{
      console.log("Please Log In");
    }
  }
});
7个回答

13

如果您查看Roles包中使用的代码,您会发现他们使用传入的用户/用户ID来对用户集合进行查询(这里,从大约623行开始):

try {
  if (Meteor.isClient) {
    // On client, iterate over each user to fulfill Meteor's
    // 'one update per ID' policy
    _.each(users, function (user) {
      Meteor.users.update({_id: user}, update)
    })
  } else {
    // On the server we can use MongoDB's $in operator for
    // better performance
    Meteor.users.update(
      {_id: {$in: users}},
      update,
      {multi: true})
  }
}

onCreateUser被调用时,用户对象尚未插入到集合中(文档链接:返回的文档将直接插入到Meteor.users集合中),因此当Roles执行查询时,它无法找到该对象。

为了解决这个问题,您必须等待用户被插入到集合中。如果您查看Roles包中的所有示例,它们都会显示如何解决此问题。像这里(第二个示例,附加了注释),以及许多其他示例:

// insert user and retrieve the id
id = Accounts.createUser({
  email: user.email,
  password: "apple1",
  profile: { name: user.name }
});

// now we can verify that the user was inserted and add permissions
if (user.roles.length > 0) {
  Roles.addUsersToRoles(id, user.roles);
}

希望这能为您解决问题。基本上只需要插入用户,然后添加权限即可。


非常好的答案。我以前看到过这个问题,也愿意听取建议。也许可以在文档中特别指出这一点? - alanning
@alanning 谢谢!是的,这可能会解决一些混淆问题。我个人建议在文档中添加一些东西。即使只是在大气页面上的客户端部分之后添加一个注释,也可能会解决一些这种情况。否则,希望人们能够开始找到与此相关的SO帖子。 - Firo
3
使用accounts-ui 创建的用户怎么样? - Islam A. Hassan
3
@Dan,@DoctorPangloss的回答让您可以使用onCreateUser,这可能很方便。我们在应用程序中使用自己的自定义Meteor方法来创建用户,因此按照@Firo建议的方式执行。我添加了一个新函数的示例,该函数直接在用户对象上设置角色此处,但只要角色使用的数据库结构保持不变(DP在其中一条评论中提到),该示例才能正常工作。 - alanning
1
下一个版本的角色可能会带有一个标志,让您可以指示用户角色应直接更新到用户对象中,以便可以直接在 onCreateUser 中使用。目前还没有计划确定。 - alanning
显示剩余6条评论

9

accounts包插入用户文档后,如需添加内容,请尝试以下模式。请注意,在此上下文中执行meteor add random以生成userId是安全的。

Accounts.onCreateUser(function (options, user) {
    // Semantics for adding things to users after the user document has been inserted
    var userId = user._id = Random.id();
    var handle = Meteor.users.find({_id: userId}, {fields: {_id: 1}}).observe({
        added: function () {
            Roles.addUsersToRoles(userId, ['admin']);
            handle.stop();
            handle = null;
        }
    });

    // In case the document is never inserted
    Meteor.setTimeout(function() {
        if (handle) {
            handle.stop();
        }
    }, 30000);

    return user;
});

这种方法相比于 user.roles = ['admin'],即 dojomouse 的回答有什么优势? - Dan
1
@dojomouse的答案对于allaning:roles如何工作做出了一些假设。如果他们不是将角色存储在字符串数组中,会怎么样?如果其他代码修改了角色会怎么样?如果您更新了roles并且他添加了一种方法来完成此操作呢?在对dojomouse解决方案的评论中,实际上有一个例子,说明如果直接修改该数组,将百分之百地破坏某些东西。这样,只要addUsersToRoles不改变,您就不会弄乱其他东西。 - DoctorPangloss
这比Joe上面的答案复杂得多,它不需要手动设置用户ID,添加另一个不必要的依赖项或者像那么多行代码,尽管它为可能在onCreateUser之后需要的更复杂的功能提供了一个整洁的观察者模式。 - Ruby_Pry
当您需要使用组时,此答案有效。@alanning在评论https://dev59.com/1GEh5IYBdhLWcg3wPRSq#XKygEYcBWogLw_1bcmN3中提到的另一种选项不适用于组。 - Samudra

6

被接受的答案强制你编写由Meteor通过accounts-ui/password提供的登录逻辑的样板代码。 其他答案对底层实现进行了假设,而超时解决方案引入了竞态条件。

Why not do this:

Accounts.onCreateUser(function(options, user) {

    ...

    Meteor.setTimeout(function () {
        Roles.addUsersToRoles(user._id, roles, group);
    },0);
    ...
  
});

您可以在onCreateUser调用之后使用alanning的api来添加角色,无论何时触发它。 (已在meteor 1.0和roles 1.2.13上测试通过)

在这里使用setTimeout()函数的相关性是什么? - jlouzado
1
在onCreateUser的时候,用户实际上还没有被创建。由于JavaScript是单线程进程,Meteor.setTimeout保证函数内部在当前线程中创建用户之后被调用。 - Joe
1
我认为Meteor.defer()相对于setTimeout0有一些优势,但我不确定具体是什么优势。 :) - chmac
不,Meteor.defer()只是一种语法糖,没有其他作用。请参见https://github.com/meteor/meteor/issues/2176 - nilsi

5
我不明白如何在创建用户时将Roles.addUsersToRoles与onCreateUser函数集成。像您所做的那样在OnCreateUser中调用它是行不通的,就像您发现的那样。但在用户创建循环中调用addUsersToRoles的示例情况似乎不适用于新用户创建帐户的正常用例。
相反,我只需要这样做:
Accounts.onCreateUser(function(options, user){
  var role = ['admin'];
  user.roles = role
  return user;
});

1
虽然这对角色有效,但如果您同时拥有角色和组,则无效。通常会得到像这样的东西:“roles”:{ “__global_roles__”:[ “admin” ] }, - d4nyll
如果 roles 集合中还没有 'admin',这段代码能正常工作吗?它会将其添加到 roles 集合中吗? - Dan

2
这里的问题实际上是要寻找一个创建用户后的挂钩(而onCreateUser不是这样的)。
事实证明,这样的东西存在!它被称为postSignUpHook。

https://github.com/meteor-useraccounts/core/blob/master/Guide.md#options

我从这个 Stack Overflow 回答中找到了这个:

https://dev59.com/9mcs5IYBdhLWcg3wRB7h#34114000

这是向使用样板 UserAccounts 包创建的用户添加角色的首选方法(即如果您不使用 Accounts.createUser 自行创建用户)。
更新
最终,我使用了 matb33:collection-hooks,就像这条评论中所述:

https://github.com/alanning/meteor-roles/issues/35#issuecomment-198979601

只需将该代码放入Meteor.startup中,一切都会按预期工作。

这就是我正在寻找的解决方案! - kroe

1
我不想重写Meteor的账户包给出的逻辑,自己设置ID或使用setTimeout。相反,我有Tracker代码监视Meteor.user()调用,如果已登录则返回用户,否则返回null。因此,一旦用户登录,此代码将检查是否已分配角色,如果没有,则添加他们。我必须使用Meteor方法,因为我不希望在客户端处理角色,但您可以更改它。
/* client */
Tracker.autorun(function () {
  var user = Meteor.user();
  if (user && !Roles.getRolesForUser(user).length) {
    Meteor.call('addUserRole', user);
  }
});

/* server */
Meteor.methods({
  addUserRole: function (user) {
    Roles.addUsersToRoles(user, user.profile.edmodo.type);
  },
});

0

我找到的方法很简单。

我将角色作为数组传递,所以我使用了[role]。

在客户端,当用户注册时:

var role = $(event.target).find('#role').val();

/*
 * Add account - Client-Side
 */
Accounts.createUser(account, (error) => {
  if (error) {
    console.log(error);
  } else {
    // server-side call to add the user to the role
    // notice Meteor.userId() and [role]
    Meteor.call('assignUserToRole', Meteor.userId(), [role]);

    // everything went well, redirect to home
    FlowRouter.go('home');
  }
});

在我们的 Meteor 方法中(我正在使用 Lodash 的 intersect 方法来验证我想让用户选择的角色)。
  Meteor.methods({
    // when a account is created make sure we limit the roles
    assignUserToRole: (userId, roles) => {
      check(userId, String);
      check(roles, Array);

      var allowedRoles = ['student', 'parent', 'teacher'];
      if (_.intersection(allowedRoles, roles).length > 0) {
        Roles.addUsersToRoles(userId, roles, 'user');
      } else {
        Roles.addUsersToRoles(userId, ['student'], 'user');
      }
    }
  });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接