diff --git a/meteor-roles/meteor-roles-tests.ts b/meteor-roles/meteor-roles-tests.ts new file mode 100644 index 0000000000..dae900800c --- /dev/null +++ b/meteor-roles/meteor-roles-tests.ts @@ -0,0 +1,150 @@ +/// +/// +/// + +/** + * All code below was copied from the examples at https://github.com/alanning/meteor-roles/. + * When necessary, code was added to make the examples work (e.g. declaring a variable + * that was assumed to have been declared earlier) + */ + +var joesUserId = '1234'; +Roles.addUsersToRoles(joesUserId, ['manage-team','schedule-game'], 'manchester-united.com') +Roles.addUsersToRoles(joesUserId, ['player','goalie'], 'real-madrid.com') + +Roles.userIsInRole(joesUserId, 'manage-team', 'manchester-united.com') // => true +Roles.userIsInRole(joesUserId, 'manage-team', 'real-madrid.com') // => false + +Roles.addUsersToRoles(joesUserId, 'super-admin', Roles.GLOBAL_GROUP) + +var bobsUserId = '1234'; +Roles.addUsersToRoles(bobsUserId, ['manage-team','schedule-game']) +// internal representation - no groups +// user.roles = ['manage-team','schedule-game'] + +Roles.addUsersToRoles(joesUserId, ['manage-team','schedule-game'], 'manchester-united.com') +Roles.addUsersToRoles(joesUserId, ['player','goalie'], 'real-madrid.com') +// internal representation - groups +// NOTE: MongoDB uses periods to represent hierarchy so periods in group names +// are converted to underscores. +// +// user.roles = { +// 'manchester-united_com': ['manage-team','schedule-game'], +// 'real-madrid_com': ['player','goalie'] +// } + +Meteor.roles.find({}); + + + +var users = [ + {name:"Normal User",email:"normal@example.com",roles:[]}, + {name:"View-Secrets User",email:"view@example.com",roles:['view-secrets']}, + {name:"Manage-Users User",email:"manage@example.com",roles:['manage-users']}, + {name:"Admin User",email:"admin@example.com",roles:['admin']} +]; + +_.each(users, function (user) { + var id : string; + + id = Accounts.createUser({ + email: user.email, + password: "apple1", + profile: { name: user.name } + }); + + if (user.roles.length > 0) { + // Need _id of existing user record so this call must come + // after `Accounts.createUser` or `Accounts.onCreate` + Roles.addUsersToRoles(id, user.roles, 'default-group'); + } + +}); + + + +// server/publish.js + +// Give authorized users access to sensitive data by group +Meteor.publish('secrets', function (group : string) { + if (Roles.userIsInRole(this.userId, ['view-secrets','admin'], group)) { + +// return Meteor.secrets.find({group: group}); + + } else { + + // user not authorized. do not publish secrets + this.stop(); + return; + + } +}); + + +Accounts.validateNewUser(function (user : Meteor.User) { + var loggedInUser = Meteor.user(); + + if (Roles.userIsInRole(loggedInUser, ['admin','manage-users'])) { + // NOTE: This example assumes the user is not using groups. + return true; + } + + throw new Meteor.Error('403', "Not authorized to create new users"); +}); + + +// server/userMethods.js + +Meteor.methods({ + /** + * delete a user from a specific group + * + * @method deleteUser + * @param {String} targetUserId _id of user to delete + * @param {String} group Company to update permissions for + */ + deleteUser: function (targetUserId : string, group : string) { + var loggedInUser = Meteor.user() + + if (!loggedInUser || + !Roles.userIsInRole(loggedInUser, + ['manage-users', 'support-staff'], group)) { + throw new Meteor.Error('403', "Access denied") + } + + // remove permissions for target group + Roles.setUserRoles(targetUserId, [], group) + + // do other actions required when a user is removed... + } +}) + + + +// server/userMethods.js + +Meteor.methods({ + /** + * update a user's permissions + * + * @param {Object} targetUserId Id of user to update + * @param {Array} roles User's new permissions + * @param {String} group Company to update permissions for + */ + updateRoles: function (targetUserId : string, roles : string[], group : string) { + var loggedInUser = Meteor.user() + + if (!loggedInUser || + !Roles.userIsInRole(loggedInUser, + ['manage-users', 'support-staff'], group)) { + throw new Meteor.Error('403', "Access denied") + } + + Roles.setUserRoles(targetUserId, roles, group) + } +}) + + + + + diff --git a/meteor-roles/meteor-roles.d.ts b/meteor-roles/meteor-roles.d.ts new file mode 100644 index 0000000000..95e4223ac1 --- /dev/null +++ b/meteor-roles/meteor-roles.d.ts @@ -0,0 +1,264 @@ +/// + +// Type definitions for Meteor Roles 1.2.14 +// Project: https://github.com/alanning/meteor-roles/ +// Definitions by: Robbie Van Gorkom +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/** + * Provides functions related to user authorization. Compatible with built-in Meteor accounts packages. + * + * @module Roles + */ +declare module Roles { + /** + * Constant used to reference the special 'global' group that + * can be used to apply blanket permissions across all groups. + * + * @example + * Roles.addUsersToRoles(user, 'admin', Roles.GLOBAL_GROUP) + * Roles.userIsInRole(user, 'admin') // => true + * + * Roles.setUserRoles(user, 'support-staff', Roles.GLOBAL_GROUP) + * Roles.userIsInRole(user, 'support-staff') // => true + * Roles.userIsInRole(user, 'admin') // => false + * + * @property GLOBAL_GROUP + * @type String + * @static + * @final + */ + var GLOBAL_GROUP : string; + + /** + * Subscription handle for the currently logged in user's permissions. + * + * NOTE: The corresponding publish function, `_roles`, depends on + * `this.userId` so it will automatically re-run when the currently + * logged-in user changes. + * + * @example + * + * `Roles.subscription.ready()` // => `true` if user roles have been loaded + * + * @property subscription + * @type Object + * @for Roles + */ + var subscription : Subscription; + + /** + * Add users to roles. Will create roles as needed. + * + * NOTE: Mixing grouped and non-grouped roles for the same user + * is not supported and will throw an error. + * + * Makes 2 calls to database: + * 1. retrieve list of all existing roles + * 2. update users' roles + * + * @example + * Roles.addUsersToRoles(userId, 'admin') + * Roles.addUsersToRoles(userId, ['view-secrets'], 'example.com') + * Roles.addUsersToRoles([user1, user2], ['user','editor']) + * Roles.addUsersToRoles([user1, user2], ['glorious-admin', 'perform-action'], 'example.org') + * Roles.addUsersToRoles(userId, 'admin', Roles.GLOBAL_GROUP) + * + * @method addUsersToRoles + * @param {Array|String} users User id(s) or object(s) with an _id field + * @param {Array|String} roles Name(s) of roles/permissions to add users to + * @param {String} [group] Optional group name. If supplied, roles will be + * specific to that group. + * Group names can not start with '$' or numbers. + * Periods in names '.' are automatically converted + * to underscores. + * The special group Roles.GLOBAL_GROUP provides + * a convenient way to assign blanket roles/permissions + * across all groups. The roles/permissions in the + * Roles.GLOBAL_GROUP group will be automatically + * included in checks for any group. + */ + function addUsersToRoles( + user : string|string[]|Object|Object[], + roles : string|string[], + group? : string + ) : void; + + /** + * Create a new role. Whitespace will be trimmed. + * + * @method createRole + * @param {String} role Name of role + * @return {String} id of new role + */ + function createRole(role : string) : string; + + /** + * Delete an existing role. Will throw "Role in use" error if any users + * are currently assigned to the target role. + * + * @method deleteRole + * @param {String} role Name of role + */ + function deleteRole (role : string) : void; + + /** + * Retrieve set of all existing roles + * + * @method getAllRoles + * @return {Cursor} cursor of existing roles + */ + function getAllRoles() : Mongo.Cursor; + + /** + * Retrieve users groups, if any + * + * @method getGroupsForUser + * @param {String|Object} user User Id or actual user object + * @param {String} [role] Optional name of roles to restrict groups to. + * + * @return {Array} Array of user's groups, unsorted. Roles.GLOBAL_GROUP will be omitted + */ + function getGroupsForUser( + user : string|Object, + role? : string + ) : string[]; + + /** + * Retrieve users roles + * + * @method getRolesForUser + * @param {String|Object} user User Id or actual user object + * @param {String} [group] Optional name of group to restrict roles to. + * User's Roles.GLOBAL_GROUP will also be included. + * @return {Array} Array of user's roles, unsorted. + */ + function getRolesForUser( + user : string|Object, + group? : string + ) : Role[]; + + /** + * Retrieve all users who are in target role. + * + * NOTE: This is an expensive query; it performs a full collection scan + * on the users collection since there is no index set on the 'roles' field. + * This is by design as most queries will specify an _id so the _id index is + * used automatically. + * + * @method getUsersInRole + * @param {Array|String} role Name of role/permission. If array, users + * returned will have at least one of the roles + * specified but need not have _all_ roles. + * @param {String} [group] Optional name of group to restrict roles to. + * User's Roles.GLOBAL_GROUP will also be checked. + * @param {Object} [options] Optional options which are passed directly + * through to `Meteor.users.find(query, options)` + * @return {Cursor} cursor of users in role + */ + function getUsersInRole( + role : string|string[], + group? : string, + options? : { + sort?: Mongo.SortSpecifier; + skip?: number; + limit?: number; + fields?: Mongo.FieldSpecifier; + reactive?: boolean; + transform?: Function; + }) : Mongo.Cursor; + + /** + * Remove users from roles + * + * @example + * Roles.removeUsersFromRoles(users.bob, 'admin') + * Roles.removeUsersFromRoles([users.bob, users.joe], ['editor']) + * Roles.removeUsersFromRoles([users.bob, users.joe], ['editor', 'user']) + * Roles.removeUsersFromRoles(users.eve, ['user'], 'group1') + * + * @method removeUsersFromRoles + * @param {Array|String} users User id(s) or object(s) with an _id field + * @param {Array|String} roles Name(s) of roles to add users to + * @param {String} [group] Optional. Group name. If supplied, only that + * group will have roles removed. + */ + function removeUsersFromRoles( + user : string|string[]|Object|Object[], + roles? : string[], + group? : string + ) : void; + + /** + * Set a users roles/permissions. + * + * @example + * Roles.setUserRoles(userId, 'admin') + * Roles.setUserRoles(userId, ['view-secrets'], 'example.com') + * Roles.setUserRoles([user1, user2], ['user','editor']) + * Roles.setUserRoles([user1, user2], ['glorious-admin', 'perform-action'], 'example.org') + * Roles.setUserRoles(userId, 'admin', Roles.GLOBAL_GROUP) + * + * @method setUserRoles + * @param {Array|String} users User id(s) or object(s) with an _id field + * @param {Array|String} roles Name(s) of roles/permissions to add users to + * @param {String} [group] Optional group name. If supplied, roles will be + * specific to that group. + * Group names can not start with '$'. + * Periods in names '.' are automatically converted + * to underscores. + * The special group Roles.GLOBAL_GROUP provides + * a convenient way to assign blanket roles/permissions + * across all groups. The roles/permissions in the + * Roles.GLOBAL_GROUP group will be automatically + * included in checks for any group. + */ + function setUserRoles ( + user : string|string[]|Object|Object[], + roles : string|string[], + group? : string + ) : void; + + /** + * Check if user has specified permissions/roles + * + * @example + * // non-group usage + * Roles.userIsInRole(user, 'admin') + * Roles.userIsInRole(user, ['admin','editor']) + * Roles.userIsInRole(userId, 'admin') + * Roles.userIsInRole(userId, ['admin','editor']) + * + * // per-group usage + * Roles.userIsInRole(user, ['admin','editor'], 'group1') + * Roles.userIsInRole(userId, ['admin','editor'], 'group1') + * Roles.userIsInRole(userId, ['admin','editor'], Roles.GLOBAL_GROUP) + * + * // this format can also be used as short-hand for Roles.GLOBAL_GROUP + * Roles.userIsInRole(user, 'admin') + * + * @method userIsInRole + * @param {String|Object} user User Id or actual user object + * @param {String|Array} roles Name of role/permission or Array of + * roles/permissions to check against. If array, + * will return true if user is in _any_ role. + * @param {String} [group] Optional. Name of group. If supplied, limits check + * to just that group. + * The user's Roles.GLOBAL_GROUP will always be checked + * whether group is specified or not. + * @return {Boolean} true if user is in _any_ of the target roles + */ + function userIsInRole( + user : string|string[]|Object|Object[], + roles : string|string[], + group? : string + ) : boolean; + + interface Role { + name : string; + } +} // module + +declare module Meteor { + var roles : Mongo.Collection; +}