text: /*!
   * Copyright 2014 Apereo Foundation (AF) Licensed under the
   * Educational Community License, Version 2.0 (the "License"); you may
   * not use this file except in compliance with the License. You may
   * obtain a copy of the License at
   *
   *     http://opensource.org/licenses/ECL-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an "AS IS"
   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
   * or implied. See the License for the specific language governing
   * permissions and limitations under the License.
   */
  
  var _ = require('underscore');
  
  var AuthzAPI = require('oae-authz');
  var AuthzConstants = require('oae-authz/lib/constants').AuthzConstants;
  var AuthzUtil = require('oae-authz/lib/util');
  var OaeUtil = require('oae-util/lib/util');
  var PrincipalsGroupAPI = require('oae-principals/lib/api.group');
  var PrincipalsUtil = require('oae-principals/lib/util');
  var TenantsUtil = require('oae-tenants/lib/util');
  
  /**
   * Determine which visibility level of library the user in context should receive from the target library owner. The following
   * table presents a visibility matrix of how a feed would be issued for a group.
   *
   * Assume that Tenant A and B both have enabled tenant permeability and Tenant C has it disabled. The column headers defined the
   * status of the current user (whether he is anonymous, an authenticated user or if he's a member), the rows express the
   * visibility setting of the library owner. Each cell will then express which library stream should be returned. A `/` means
   * the user does not have access to the library
   *
   *                ||                Public Tenant A                  ||                 Public Tenant B                ||      Private Tenant C
   *                ||     Anon    |   Loggedin user   |   Member      ||      Anon    |   Loggedin user   |   Member    ||      Anon    |   Loggedin user   |   Member
   * ------------------------------------------------------------------------------------------------------------------- -------------------------------------------------
   * public group   ||     public  |     logged in     |  private      ||     public   |      public       |   private   ||     public   |      public           private
   *                ||             |                                   ||              |                   |             ||              |
   * loggedin group ||       /     |     logged in     |  private      ||        /     |        /          |   private   ||        /     |        /              private
   *                ||             |                                   ||              |                   |             ||              |
   * private group  ||       /     |        /          |  private      ||        /     |        /          |   private   ||        /     |        /              private
   *
   *
   * @param  {Context}    ctx                         Standard context object containing the current user and the current tenant
   * @param  {String}     libraryId                   The authz id used to index the library. Note that this is not always the id of the "library owner" resource, as in cases like folders, it uses a surrogate group id to be represented in authz
   * @param  {Object}     libraryOwner                A standard resource object that represents the resource that "owns" the library
   * @param  {Tenant}     libraryOwner.tenant         The tenant of the resource that owns the library
   * @param  {String}     libraryOwner.visibility     The visibility of the resource that owns the library
   * @param  {String}     libraryOwner.joinable       Whether or not the resource that owns the library is joinable
   * @param  {Function}   callback                    Standard callback function
   * @param  {Object}     callback.err                An error that occurred, if any
   * @param  {Boolean}    callback.hasAccess          A boolean flag that expresses whether the current user has access to the library
   * @param  {String}     callback.visibility         The best visibility (as enumerated in `AuthzConstants.visibility`) of library the user in context can see from the target library owner. If the user has no access, this value will be falsey
   */
  var resolveTargetLibraryAccess = module.exports.resolveTargetLibraryAccess = function(ctx, libraryId, libraryOwner, callback) {
      var roleHigh = 'manager';
      var roleLow = 'member';
      var rolesPriority = [roleLow, roleHigh];
  
      // We only need to know "high role" and "low role" for this, so the `rolesPriority` we use is
      // rather arbitrary
      AuthzAPI.resolveImplicitRole(ctx.user(), libraryOwner, rolesPriority, function(err, implicitRole) {
          if (err) {
              return callback(err);
          } else if (implicitRole === roleHigh) {
              // We are implicitly a manager (i.e., we are administrator of the library's tenant)
              return callback(null, true, AuthzConstants.visibility.PRIVATE);
          }
  
          // Only check `AuthzAPI.hasAnyRole` if it is applicable. This is useful to avoid branching
          // asynchronously the cases for user libraries, anonymous users and resource libraries and
          // duplicating logic among them. This consolidated set of business logic should cover all
          // cases
          var needsExplicitCheck = (!AuthzUtil.isUserId(libraryId) && ctx.user());
          OaeUtil.invokeIfNecessary(needsExplicitCheck, AuthzAPI.hasAnyRole, (ctx.user() && ctx.user().id), libraryId, function(err, hasAnyRole) {
              if (err) {
                  return callback(err);
              } else if (hasAnyRole) {
                  // If the current user has an explicit role on the library resource, they can always
                  // see private items
                  return callback(null, true, AuthzConstants.visibility.PRIVATE);
              } else if (implicitRole && TenantsUtil.isLoggedIn(ctx, libraryOwner.tenant.alias)) {
                  // If we have implicit access and we can are logged in to the library's tenant, we
                  // can see loggedin items
                  return callback(null, true, AuthzConstants.visibility.LOGGEDIN);
              } else if (implicitRole) {
                  // If we have implicit access but aren't authenticated to the library's tenant, we
                  // can see public items
                  return callback(null, true, AuthzConstants.visibility.PUBLIC);
              } else if (ctx.user() && TenantsUtil.canInteract(ctx.user().tenant.alias, libraryOwner.tenant.alias) &&
                  libraryOwner.joinable === AuthzConstants.joinable.YES) {
                  // One weird case is if the user is able to "join" the resource (e.g., a group),
                  // then they should also be able to see its public items
                  return callback(null, true, AuthzConstants.visibility.PUBLIC);
              }
  
              // We have covered all cases where we are able to see the resource, at this point we are
              // not allowed to see its library
              return callback(null, false);
          });
      });
  };
  
  /**
   * Given a library and a resource, determine which visibility bucket to place the resource. This
   * uses tenant and visibility settings in order to determine which visiting users should be able
   * to see the resource
   *
   * @param  {String}     libraryId               The id of the library in which the resource will be placed
   * @param  {Object}     resource                The resource that will be inserted into the library
   * @param  {String}     resource.id             The id of the resource that will be inserted into the library
   * @param  {String}     resource.visibility     The visibility of the resource that will be inserted into the library
   * @return {String}                             The target visibility bucket in which the resource should be placed
   */
  var resolveLibraryBucketVisibility = module.exports.resolveLibraryBucketVisibility = function(libraryId, resource) {
      var effectiveVisibility = resource.visibility;
      if (effectiveVisibility === AuthzConstants.visibility.LOGGEDIN) {
          // If the visibility is loggedin, it only goes into the owner's 'loggedin' library if it is
          // from their *own* tenant. Otherwise, we put it only into the library bucket that matches
          // its own visibility (public or private)
          var libraryOwnerTenantAlias = AuthzUtil.getResourceFromId(libraryId).tenantAlias;
          var resourceTenantAlias = AuthzUtil.getResourceFromId(resource.id).tenantAlias;
          if (libraryOwnerTenantAlias !== resourceTenantAlias) {
              effectiveVisibility = AuthzConstants.visibility.PRIVATE;
          }
      }
  
      return effectiveVisibility;
  };
  
  