Appearance
角色
¥Roles
Meteor 授权包 - 与内置 accounts 包兼容。
¥Authorization package for Meteor - compatible with built-in accounts package.
自 Meteor 3.1.0 起可用(以前称为 alanning:roles)
¥Available since Meteor 3.1.0 (previously alanning:roles)
安装
¥Installation
要向你的应用添加角色,请在终端中运行以下命令:
¥To add roles to your application, run this command in your terminal:
bash
meteor add roles
概述
¥Overview
roles 包允许你将角色附加到用户,然后在决定是否授予 Meteor 方法的访问权限或发布数据时对照这些角色进行检查。核心概念很简单 - 你为用户创建角色分配,然后稍后验证这些角色。此软件包提供了辅助方法,使添加、删除和验证角色的过程更加轻松。
¥The roles package lets you attach roles to users and then check against those roles when deciding whether to grant access to Meteor methods or publish data. The core concept is simple - you create role assignments for users and then verify those roles later. This package provides helper methods to make the process of adding, removing, and verifying roles easier.
概念
¥Concepts
角色与权限
¥Roles vs Permissions
虽然名为 "roles",但你可以根据需要定义角色、范围或权限。它们本质上是分配给用户的标签,你可以稍后检查。
¥Although named "roles", you can define your roles, scopes or permissions however you like. They are essentially tags assigned to users that you can check later.
你可以拥有像 admin
或 webmaster
这样的传统角色,也可以拥有像 view-secrets
、users.view
或 users.manage
这样的更细粒度的权限。通常,更细粒度的权限更好,因为它们可以处理边缘情况而无需创建许多更高级别的角色。
¥You can have traditional roles like admin
or webmaster
, or more granular permissions like view-secrets
, users.view
, or users.manage
. Often, more granular permissions are better as they handle edge cases without creating many higher-level roles.
角色层级
¥Role Hierarchy
角色可以按层次结构组织:
¥Roles can be organized in a hierarchy:
角色可以有多个父角色和子角色(子角色)
¥Roles can have multiple parents and children (subroles)
如果为用户分配了父角色,则其所有后代角色也适用。
¥If a parent role is assigned to a user, all its descendant roles also apply
这允许创建聚合权限的 "超级角色"。
¥This allows creating "super roles" that aggregate permissions
层次结构设置示例:
¥Example hierarchy setup:
js
import { Roles } from "meteor/roles";
// Create base roles
await Roles.createRoleAsync("user");
await Roles.createRoleAsync("admin");
// Create permission roles
await Roles.createRoleAsync("USERS_VIEW");
await Roles.createRoleAsync("POST_EDIT");
// Set up hierarchy
await Roles.addRolesToParentAsync("USERS_VIEW", "admin");
await Roles.addRolesToParentAsync("POST_EDIT", "admin");
await Roles.addRolesToParentAsync("POST_EDIT", "user");
范围
¥Scopes
作用域允许用户拥有独立的角色集。用例包括:
¥Scopes allow users to have independent sets of roles. Use cases include:
应用内的不同社区
¥Different communities within your app
多租户应用中的多个租户
¥Multiple tenants in a multi-tenant application
不同的资源组
¥Different resource groups
用户可以同时拥有作用域角色和全局角色:
¥Users can have both scoped roles and global roles:
全局角色适用于所有范围
¥Global roles apply across all scopes
作用域角色仅在其特定作用域内有效
¥Scoped roles only apply within their specific scope
作用域彼此独立
¥Scopes are independent of each other
使用范围的示例:
¥Example using scopes:
js
// Assign scoped roles
await Roles.addUsersToRolesAsync(userId, ["manage-team"], "team-a");
await Roles.addUsersToRolesAsync(userId, ["player"], "team-b");
// Check scoped roles
await Roles.userIsInRoleAsync(userId, "manage-team", "team-a"); // true
await Roles.userIsInRoleAsync(userId, "manage-team", "team-b"); // false
// Assign global role
await Roles.addUsersToRolesAsync(userId, "super-admin", null);
// Global roles work in all scopes
await Roles.userIsInRoleAsync(userId, ["manage-team", "super-admin"], "team-b"); // true
角色管理
¥Role Management
Roles.createRoleAsync
Summary:
Create a new role.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
roleName | String | Name of role. | Yes |
options | Object | Options:
| No |
示例:
¥Example:
js
import { Roles } from "meteor/roles";
// Create a new role
await Roles.createRoleAsync("admin");
// Create if doesn't exist
await Roles.createRoleAsync("editor", { unlessExists: true });
修改角色
¥Modifying Roles
Roles.addRolesToParentAsync
Summary:
Add role parent to roles.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
rolesNames | Array or String | Name(s) of role(s). | Yes |
parentName | String | Name of parent role. | Yes |
示例:
¥Example:
js
// Make 'editor' a child role of 'admin'
await Roles.addRolesToParentAsync("editor", "admin");
// Add multiple child roles
await Roles.addRolesToParentAsync(["editor", "moderator"], "admin");
Roles.removeRolesFromParentAsync
Summary:
Remove role parent from roles. Other parents are kept (role can have multiple parents). For users which have the parent role set, removed subrole is removed automatically.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
rolesNames | Array or String | Name(s) of role(s). | Yes |
parentName | String | Name of parent role. | Yes |
示例:
¥Example:
js
// Remove 'editor' as child role of 'admin'
await Roles.removeRolesFromParentAsync("editor", "admin");
Roles.deleteRoleAsync
Summary:
Delete an existing role. If the role is set for any user, it is automatically unset.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
roleName | String | Name of role. | Yes |
示例:
¥Example:
js
// Delete role and all its assignments
await Roles.deleteRoleAsync("temp-role");
Roles.renameRoleAsync
Summary:
Rename an existing role.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
oldName | String | Old name of a role. | Yes |
newName | String | New name of a role. | Yes |
示例:
¥Example:
js
// Rename an existing role
await Roles.renameRoleAsync("editor", "content-editor");
分配角色
¥Assigning Roles
Roles.addUsersToRolesAsync
Summary:
Add users to roles. Adds roles to existing roles for each user.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
users | Array or String | User ID(s) or object(s) with an | Yes |
roles | Array or String | Name(s) of roles to add users to. Roles have to exist. | Yes |
options | Object or String | Options:
| No |
示例:
¥Example:
js
// Add global roles
await Roles.addUsersToRolesAsync(userId, ["admin", "editor"]);
// Add scoped roles
await Roles.addUsersToRolesAsync(userId, ["manager"], "department-a");
// Add roles to multiple users
await Roles.addUsersToRolesAsync([user1Id, user2Id], ["user"]);
Roles.setUserRolesAsync
Summary:
Set users' roles. Replaces all existing roles with a new set of roles.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
users | Array or String | User ID(s) or object(s) with an | Yes |
roles | Array or String | Name(s) of roles to add users to. Roles have to exist. | Yes |
options | Object or String | Options:
| No |
示例:
¥Example:
js
// Replace user's global roles
await Roles.setUserRolesAsync(userId, ["editor"]);
// Replace scoped roles
await Roles.setUserRolesAsync(userId, ["viewer"], "project-x");
// Clear all roles in scope
await Roles.setUserRolesAsync(userId, [], "project-x");
Roles.removeUsersFromRolesAsync
Summary:
Remove users from assigned roles.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
users | Array or String | User ID(s) or object(s) with an | Yes |
roles | Array or String | Name(s) of roles to remove users from. Roles have to exist. | Yes |
options | Object or String | Options:
| No |
示例:
¥Example:
js
// Remove global roles
await Roles.removeUsersFromRolesAsync(userId, ["admin"]);
// Remove scoped roles
await Roles.removeUsersFromRolesAsync(userId, ["manager"], "department-a");
// Remove roles from multiple users
await Roles.removeUsersFromRolesAsync([user1Id, user2Id], ["temp-role"]);
Roles.renameScopeAsync
Summary:
Rename a scope.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
oldName | String | Old name of a scope. | Yes |
newName | String | New name of a scope. | Yes |
示例:
¥Example:
js
// Rename a scope
await Roles.renameScopeAsync("department-1", "marketing");
Roles.removeScopeAsync
Summary:
Remove a scope and all its role assignments.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
name | String | The name of a scope. | Yes |
示例:
¥Example:
js
// Remove a scope and all its role assignments
await Roles.removeScopeAsync("old-department");
Roles.getAllRoles
Summary:
Retrieve cursor of all existing roles.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
queryOptions | Object | Options which are passed directly through to | No |
示例:
¥Example:
js
// Get all roles sorted by name
const roles = Roles.getAllRoles({ sort: { _id: 1 } });
// Get roles with custom query
const customRoles = Roles.getAllRoles({
fields: { _id: 1, children: 1 },
sort: { _id: -1 },
});
Roles.getUsersInRoleAsync
Summary:
Retrieve all users who are in target role.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
roles | Array or String | Name of role or an array of roles. | Yes |
options | Object or String | Options:
| No |
示例:
¥Example:
js
// Find all admin users
const adminUsers = await Roles.getUsersInRoleAsync("admin");
// Find users with specific roles in a scope
const scopedUsers = await Roles.getUsersInRoleAsync(
["editor", "writer"],
"blog"
);
// Find users with custom options
const users = await Roles.getUsersInRoleAsync("manager", {
scope: "department-a",
queryOptions: {
sort: { createdAt: -1 },
limit: 10,
},
});
检查角色
¥Checking Roles
Roles.userIsInRoleAsync
Summary:
Check if user has specified roles.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
user | String or Object | User ID or an actual user object. | Yes |
roles | Array or String | Name of role or an array of roles to check against. If array,
will return | Yes |
options | Object or String | Options:
Alternatively, it can be a scope name string. | No |
示例:
¥Example:
js
// Check global role
const isAdmin = await Roles.userIsInRoleAsync(userId, "admin");
// Check any of multiple roles
const canEdit = await Roles.userIsInRoleAsync(userId, ["editor", "admin"]);
// Check scoped role
const isManager = await Roles.userIsInRoleAsync(
userId,
"manager",
"department-a"
);
// Check role in any scope
const hasRole = await Roles.userIsInRoleAsync(userId, "viewer", {
anyScope: true,
});
Roles.getRolesForUserAsync
Summary:
Retrieve user's roles.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
user | String or Object | User ID or an actual user object. | Yes |
options | Object or String | Options:
Alternatively, it can be a scope name string. | No |
示例:
¥Example:
js
// Get user's global roles
const globalRoles = await Roles.getRolesForUserAsync(userId);
// Get scoped roles
const deptRoles = await Roles.getRolesForUserAsync(userId, "department-a");
// Get all roles including inherited
const allRoles = await Roles.getRolesForUserAsync(userId, {
anyScope: true,
fullObjects: true,
});
Roles.isParentOfAsync
Summary:
Find out if a role is an ancestor of another role.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
parentRoleName | String | The role you want to research. | Yes |
childRoleName | String | The role you expect to be among the children of parentRoleName. | Yes |
示例:
¥Example:
js
// Check if admin is a parent of editor
const isParent = await Roles.isParentOfAsync("admin", "editor");
// Can be used to check inheritance chains
const hasPermission = await Roles.isParentOfAsync("super-admin", "post-edit");
Roles.getScopesForUserAsync
Summary:
Retrieve users scopes, if any.
Arguments:
Source codeName | Type | Description | Required |
---|---|---|---|
user | String or Object | User ID or an actual user object. | Yes |
roles | Array or String | Name of roles to restrict scopes to. | No |
示例:
¥Example:
js
// Get all scopes for user
const allScopes = await Roles.getScopesForUserAsync(userId);
// Get scopes where user has specific roles
const editorScopes = await Roles.getScopesForUserAsync(userId, ["editor"]);
发布角色
¥Publishing Roles
角色分配需要发布才能在客户端使用。发布示例:
¥Role assignments need to be published to be available on the client. Example publication:
js
// Publish user's own roles
Meteor.publish(null, function () {
if (this.userId) {
return Meteor.roleAssignment.find({ "user._id": this.userId });
}
this.ready();
});
// Publish roles for specific scope
Meteor.publish("scopeRoles", function (scope) {
if (this.userId) {
return Meteor.roleAssignment.find({ scope: scope });
}
this.ready();
});
仅限客户端 API
¥Client only APIs
在客户端,除了异步方法之外,你还可以使用 sync
版本的函数:
¥On the client alongside the async methods, you can use the sync
versions of the functions:
Roles.userIsInRole(userId, roles, scope)
Roles.getRolesForUser(userId, scope)
Roles.getScopesForUser(userId)
Roles.isParentOf(parent, child)
Roles.getUsersInRole(role, scope)
Roles.getAllRoles(options)
Roles.createRole(role, options)
Roles.addUsersToRoles(userId, roles, scope)
Roles.setUserRoles(userId, roles, scope)
Roles.removeUsersFromRoles(userId, roles, scope)
Roles.addRolesToParent(child, parent)
Roles.removeRolesFromParent(child, parent)
Roles.deleteRole(role)
Roles.renameRole(oldRole, newRole)
Roles.renameScope(oldScope, newScope)
Roles.removeScope(scope)
与模板一起使用
¥Using with Templates
roles 包自动为模板提供 isInRole
助手:
¥The roles package automatically provides an isInRole
helper for templates:
迁移到核心版本
¥Migration to Core Version
如果你当前正在使用 alanning:roles
包,请按照以下步骤迁移到核心版本:
¥If you are currently using the alanning:roles
package, follow these steps to migrate to the core version:
首先确保你使用的是
alanning:roles
3.6 版本。¥Make sure you are on version 3.6 of
alanning:roles
first运行所有待处理的旧版本迁移
¥Run any pending migrations from previous versions
将所有服务器端角色操作切换为使用以下函数的异步版本:
¥Switch all server-side role operations to use the async versions of the functions:
createRoleAsync
deleteRoleAsync
addUsersToRolesAsync
setUserRolesAsync
removeUsersFromRolesAsync
等等
¥etc.
移除
alanning:roles
软件包:¥Remove
alanning:roles
package:bashmeteor remove alanning:roles
添加核心角色包:
¥Add the core roles package:
bashmeteor add roles
更新导入以使用新软件包:
¥Update imports to use the new package:
jsimport { Roles } from "meteor/roles";
这些函数的同步版本在客户端仍然可用。
¥The sync versions of these functions are still available on the client.
安全注意事项
¥Security Considerations
客户端角色检查仅供参考 - 始终在服务器上验证权限
¥Client-side role checks are for convenience only - always verify permissions on the server
仅发布用户需要的角色数据
¥Publish only the role data that users need
使用作用域正确隔离角色分配
¥Use scopes to properly isolate role assignments
验证角色名称和作用域以防止注入攻击
¥Validate role names and scopes to prevent injection attacks
考虑使用更细粒度的权限,而不是广泛的角色分配
¥Consider using more granular permissions over broad role assignments
示例用法
¥Example Usage
方法安全性
¥Method Security
js
// server/methods.js
Meteor.methods({
deletePost: async function (postId) {
check(postId, String);
const canDelete = await Roles.userIsInRoleAsync(
this.userId,
["admin", "moderator"],
"posts"
);
if (!canDelete) {
throw new Meteor.Error("unauthorized", "Not authorized to delete posts");
}
Posts.remove(postId);
},
});
发布安全性
¥Publication Security
js
// server/publications.js
Meteor.publish("secretDocuments", async function (scope) {
check(scope, String);
const canView = await Roles.userIsInRoleAsync(
this.userId,
["view-secrets", "admin"],
scope
);
if (canView) {
return SecretDocs.find({ scope: scope });
}
this.ready();
});
用户管理
¥User Management
js
// server/users.js
Meteor.methods({
promoteToEditor: async function (userId, scope) {
check(userId, String);
check(scope, String);
const canPromote = await Roles.userIsInRoleAsync(
this.userId,
"admin",
scope
);
if (!canPromote) {
throw new Meteor.Error("unauthorized");
}
await Roles.addUsersToRolesAsync(userId, ["editor"], scope);
},
});