medbook:collaborations

v2.4.12Published 10 years ago

This package has not had recent updates. Please investigate it's current state before committing to using it in your project.

MedBook Collaborations

medbook:collaborations makes it easy to write collaboration security into Meteor methods and publications. Remember: a collaboration has access to items up the collaboration tree but not down.

Diagrams

A possible collaboration scenario

Collaboration scenario

... and the resulting access...

Collaboration scenario resulting access

The User object

To fetch a MedBook user object, use MedBook.findUser(). To throw an error if the user is not logged in, use MedBook.ensureUser(). You should use ensureUser unless you need to do something if no user is logged in.

While it is technically possible to attach a transform directly to the Meteor.users collection (Meteor.users._transform = ...), this approach is not recommended. See here for more info.

1// outside publish functions
2let user = MedBook.ensureUser(Meteor.userId());
3// will not reach here if no user logged in
4
5// inside publish functions
6let user = MedBook.ensureUser(this.userId);
7
8// if specific behavior is required if no user is logged in
9let user = MedBook.findUser(Meteor.userId());
10if (user) {
11  // do something
12} else {
13  // do something else
14}

To get the personal collaboration associated with a user, use user.personalCollaboration.

1let user = MedBook.ensureUser(Meteor.userId());
2let userCollab = user.personalCollaboration(); // "user:username@domain.suffix"

To get a list of the collaborations a user is a part of, use getCollaborations. On both client and server, this returns user.collaborations.memberOf, however on the server this list is updated before it is returned. (Internally, collaboration.getAssociatedCollaborators() is used.) Currently, the memberOf list is updated every time this function is called. In the future, we may add throttling to this update function (ex. updating only a minute after the last update).

1let user = MedBook.ensureUser(Meteor.userId());
2let collaborations = user.getCollaborations();
3// ex. ["user:test@test.com", "Testing lab UCSC", "Cool RNA-Seq project"]

To check if a user has access to an object, use hasAccess (returns boolean). To throw an error if the user doesn't have access, use ensureAccess. These functions take one parameter: either a collaboration object or a collaboration name (string). Use ensureAccess unless you need to do something if the user doesn't have access.

In determining if a user has access to an object, two fields are checked. A user is considered to have access if the user_id field matches the _id of the currently logged in user. A user is also considered to have access if they have access to one or more of the collaborations in the collaborations field.

1Meteor.methods({
2  // do something, but only if they have access to the "CKCC" collaboration
3  CKCCDoSomething: function () {
4    let user = MedBook.ensureUser(Meteor.userId()); // can throw "user-not-found"
5
6    if (user.hasAccess("CKCC")) {
7      console.log("We are doing something with the CKCC collaboration!");
8    } else {
9      console.log("Someone tried to do something but didn't have access.");
10    }
11  },
12
13  // remove a sample group by _id
14  removeSampleGroup: function (sampleGroupId) {
15    check(sampleGroupId, String); // can throw match error
16
17    let user = MedBook.ensureUser(Meteor.userId()); // can throw "user-not-found"
18    let sampleGroup = SampleGroups.findOne(sampleGroupId);
19    user.ensureAccess(sampleGroup); // throws "permission-denied" if no access
20
21    SampleGroups.remove(sampleGroupId); // we made it!
22  }
23});
24
25// publish a specific study
26Meteor.publish("specificStudy", function (study_label) {
27  check(study_label, String);
28
29  let user = MedBook.ensureUser(this.userId); // can throw "user-not-found"
30  let study = Studies.findOne({ id: study_label });
31  user.ensureAccess(study); // throws "permission-denied" if no access
32
33  return Studies.find({ id: study_label });
34});

To check if a user is an admin for a collaboration, use isAdmin (returns boolean). To throw an error if the user isn't an admin, use ensureAdmin. Like hasAccess and ensureAccess, these functions take one parameter: either a collaboration object or a collaboration name (string).

1Meteor.methods({
2  // removes a collaboration
3  removeCollaboration: function (collaborationName) {
4    let user = MedBook.ensureUser(Meteor.userId());
5    user.ensureAdmin(collaborationName); // throws "permission-denied"
6
7    // The collaboration remove code in MedBook is actually slightly more
8    // complicated because we don't want users to be able to create a
9    // collaboration with the name of a collaboration that has been deleted.
10    Collaborations.remove({name: collaborationName});
11  }
12});

The Collaboration object

Two methods are available on the server for objects fetched from the Collaborations collection (ex. Collaborations.findOne()). getAssociatedCollaborators does a downwards tree traversal, returning a list of collaborations (including personal collaborations) that have access to the source collaboration. getAssociatedCollaborations does an upwards tree traversal, returning a list of collaborations that the source collaboration has access to. Both of these functions take no parameters and return an array of collaboration name strings.

These two functions are provided for medbook:collaboration's internal API and advanced users. Use them with care!