<commit_before>
/**
 * Namespace for multitenancy magic. Here's how it works:
 *
 * # Server
 *
 * - All CRUD methods need a current user otherwise 403. The only
 *   exception is if the client code is masquarading.
 * - `find`, `findOne`, `update` and `remove` queries are constrained
 *   to where.$in the current user's organisations. Server should
 *   publish all documents that the user has access to across all of
 *   their organisations. Credits to MH for this suggestion.
 * - You can also specify the _orgId in the selectors of `find`,
 *   `findOne`, `update` and `remove` methods. If you specify an orgId
 *   that you can't access, 403. This is useful for bypassing query
 *   constraints on the server side.
 * - `insert` doc must have an `_orgId` otherwise 403 (this is added
 *   transparently in the client side's `insert` hook).
 * - `update` shouldn't allow _orgIds to be modified.
 *
 * # Client
 *
 * - Has a global 'currentOrgId' variable stored in a Session.
 * - CRUD collection hooks which append the orgId to all query
 *   selectors.
 * - CRUD methods require both a current logged in user. `insert`
 *   will require that an orgId is set.
 * - The `orgId` might be updated on `$startRouteChange`using
 *   the $stateParams.
 *
 * # Why not constrain all the queries on the server side?
 *
 * - We need to avoid maintaining state about which specific
 *   organisation a user is viewing on the server side; as its
 *   per-client data it makes sense to maintain it on the client.
 * - Doing so on the server runs into all sorts of design issues
 *   when the same client is viewing different organisations through
 *   different browser windows.
 *
 * # How do I perform operations on the server side?
 *
 * - You have 2 options:
 *
 *   1) Pretty much as you did before, requiring a current logged in user
 *      and explicitly specifying the orgId in CRUD ops where necessary
 *      (e.g. calling `insert`, to specifying where you want the doc inserted)
 *   2) Masquarading. Use the `masqOp` method to fake being part of an
 *      organisation for the duration of the query. This _doesn't_ require
 *      a current logged in user and is perfect for system config server code.
 *      e.g. for creating some sample data !
 *
 * - `find`, `findOne`, `update` and `remove` will all check whether
 *   the document that you're accessing is part of an organisation
 *   that you're a member of.
 * - Be mindful of the `_orgId` attribute on the server. You should
 *   ensure that your queries don't reference documents ambiguous
 *   to an organisation by specifying the orgId if you have to.
 * - `insert` will require you to specify an `_orgId` explicitly.
 *
 * @todo    In some cases, we may want to create a subscription to
 *          a partitioned collection without actually being logged in
 *          in preparation for a login. Might be nice to have a 'hardFail'
 *          param that we can pass to the ctor to prevent the col from
 *          throwing.
 * @todo    What if a user is added to an org ? Probably use
 *          Cursor.observeChanges to make the `constrainFind`
 *          reactive.
 * @author  <NAME>
 */
MultiTenancy = {};

'use strict';

/**
 * Assert that the application is running on the correct host
 *
 * @throws  Meteor.Error
 * @param   server        Boolean
 */
var assertHost = function(server) {
  if (!!server !== Meteor.isServer) {
    var hostName = (isServer ? 'server' : 'client');
    throw new Meteor.Error(404, 'Only available on the ' + hostName);
  }
};

/**
 * @constructor
 * Proxies Mongo.Collection ctor. Applies multi-tenancy hooks to the
 * collection.
 *
 * @note  No update / remove hooks are required. find / findOne are
 *        called before update and remove anyway.
 * @host  Server | Client
 * @param name    String
 * @see   Mongo.Collection
 */
MultiTenancy.Collection = function(name) {

  var col = new Mongo.Collection(name);
  var constrainFind;
  var constrainInsert;

  var sanatizeUpdate = function(userId, doc, fieldNames, modifier) {
    if (modifier.$set) {
      delete modifier.$set._orgId;
    }
  };

  var assertUser = function(userId) {
    // @note Should we using the userId param passed into the hooks?
    if (!userId) {
      throw new Meteor.Error(403, 'Login required to access ' + name);
    }
  };

  if (Meteor.isServer) {

    var findOrgIds = function(userId) {
      return MultiTenancy.memberships.direct.find({
        userId: userId
      }, {
        _orgId: 1
      }).fetch().map(function(mem) {
        return mem._orgId;
      });
    };
    var assertUserOrg = function(userId, orgId) {
      var orgIds = findOrgIds(userId);
      if(!~orgIds.indexOf(doc._orgId)) {
        throw new Meteor.Error(403, 'User does not permission to access this doc in ' + name);
      }
    };
    var bypassQuery = function(doc) {
      console.log('Can we bypass ?');
      var masqId = MultiTenancy.masqOrgId.get();
      console.log('Look: ' + masqId);
      if (masqId) {
        console.log('Appending to ');
        console.log(doc);
        doc._orgId = masqId;
        return true;
      }
      return false;
    };
    constrainFind = function(userId, sel) {
      console.log('Finding ' + name);
      sel = sel || {};
      if (bypassQuery(sel)) {
        return;
      }
      console.log('Assert user');
      assertUser(userId);
      console.log('Finding org ids');
      var orgIds = findOrgIds(userId);
      console.log('Do we already hav an org id in ');
      console.log(sel);
      if (sel._orgId) {<commit_msg>feature(core): Fixed error in insert constraint hook<commit_after>
/**
 * Namespace for multitenancy magic. Here's how it works:
 *
 * # Server
 *
 * - All CRUD methods need a current user otherwise 403. The only
 *   exception is if the client code is masquarading.
 * - `find`, `findOne`, `update` and `remove` queries are constrained
 *   to where.$in the current user's organisations. Server should
 *   publish all documents that the user has access to across all of
 *   their organisations. Credits to MH for this suggestion.
 * - You can also specify the _orgId in the selectors of `find`,
 *   `findOne`, `update` and `remove` methods. If you specify an orgId
 *   that you can't access, 403. This is useful for bypassing query
 *   constraints on the server side.
 * - `insert` doc must have an `_orgId` otherwise 403 (this is added
 *   transparently in the client side's `insert` hook).
 * - `update` shouldn't allow _orgIds to be modified.
 *
 * # Client
 *
 * - Has a global 'currentOrgId' variable stored in a Session.
 * - CRUD collection hooks which append the orgId to all query
 *   selectors.
 * - CRUD methods require both a current logged in user. `insert`
 *   will require that an orgId is set.
 * - The `orgId` might be updated on `$startRouteChange`using
 *   the $stateParams.
 *
 * # Why not constrain all the queries on the server side?
 *
 * - We need to avoid maintaining state about which specific
 *   organisation a user is viewing on the server side; as its
 *   per-client data it makes sense to maintain it on the client.
 * - Doing so on the server runs into all sorts of design issues
 *   when the same client is viewing different organisations through
 *   different browser windows.
 *
 * # How do I perform operations on the server side?
 *
 * - You have 2 options:
 *
 *   1) Pretty much as you did before, requiring a current logged in user
 *      and explicitly specifying the orgId in CRUD ops where necessary
 *      (e.g. calling `insert`, to specifying where you want the doc inserted)
 *   2) Masquarading. Use the `masqOp` method to fake being part of an
 *      organisation for the duration of the query. This _doesn't_ require
 *      a current logged in user and is perfect for system config server code.
 *      e.g. for creating some sample data !
 *
 * - `find`, `findOne`, `update` and `remove` will all check whether
 *   the document that you're accessing is part of an organisation
 *   that you're a member of.
 * - Be mindful of the `_orgId` attribute on the server. You should
 *   ensure that your queries don't reference documents ambiguous
 *   to an organisation by specifying the orgId if you have to.
 * - `insert` will require you to specify an `_orgId` explicitly.
 *
 * @todo    In some cases, we may want to create a subscription to
 *          a partitioned collection without actually being logged in
 *          in preparation for a login. Might be nice to have a 'hardFail'
 *          param that we can pass to the ctor to prevent the col from
 *          throwing.
 * @todo    What if a user is added to an org ? Probably use
 *          Cursor.observeChanges to make the `constrainFind`
 *          reactive.
 * @author  <NAME>
 */
MultiTenancy = {};

'use strict';

/**
 * Assert that the application is running on the correct host
 *
 * @throws  Meteor.Error
 * @param   server        Boolean
 */
var assertHost = function(server) {
  if (!!server !== Meteor.isServer) {
    var hostName = (isServer ? 'server' : 'client');
    throw new Meteor.Error(404, 'Only available on the ' + hostName);
  }
};

/**
 * @constructor
 * Proxies Mongo.Collection ctor. Applies multi-tenancy hooks to the
 * collection.
 *
 * @note  No update / remove hooks are required. find / findOne are
 *        called before update and remove anyway.
 * @host  Server | Client
 * @param name    String
 * @see   Mongo.Collection
 */
MultiTenancy.Collection = function(name) {

  var col = new Mongo.Collection(name);
  var constrainFind;
  var constrainInsert;

  var sanatizeUpdate = function(userId, doc, fieldNames, modifier) {
    if (modifier.$set) {
      delete modifier.$set._orgId;
    }
  };

  var assertUser = function(userId) {
    // @note Should we using the userId param passed into the hooks?
    if (!userId) {
      throw new Meteor.Error(403, 'Login required to access ' + name);
    }
  };

  if (Meteor.isServer) {

    var findOrgIds = function(userId) {
      return MultiTenancy.memberships.direct.find({
        userId: userId
      }, {
        _orgId: 1
      }).fetch().map(function(mem) {
        return mem._orgId;
      });
    };
    var assertUserOrg = function(userId, orgId) {
      var orgIds = findOrgIds(userId);
      if(!~orgIds.indexOf(orgId)) {
        throw new Meteor.Error(403, 'User does not permission to access this doc in ' + name);
      }
    };
    var bypassQuery = function(doc) {
      console.log('Can we bypass ?');
      var masqId = MultiTenancy.masqOrgId.get();
      console.log('Look: ' + masqId);
      if (masqId) {
        console.log('Appending to ');
        console.log(doc);
        doc._orgId = masqId;
        return true;
      }
      return false;
    };
    constrainFind = function(userId, sel) {
      console.log('Finding ' + name);
      sel = sel || {};
      if (bypassQuery(sel)) {
        return;
      }
      console.log('Assert user');
      assertUser(userId);
      console.log('Finding org ids');
      var orgIds = findOrgIds(userId);
      console.log('Do we already hav an org id in ');
      console.log(sel);
      if (sel._orgId) {
{'fraction_non_alphanumeric': 0.09458004307250538, 'fraction_numerical': 0.003948312993539124, 'mean_word_length': 2.7601214574898787, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5971741', 'n_tokens_mistral': 3271, 'n_tokens_neox': 3163, 'n_words': 1322}