var resolveImplicitRole = module.exports.resolveImplicitRole = function(ctx, resourceAlias, resourceVisibility, rolesPriority, callback) { var user = ctx.user(); // Anonymous cannot interact with anything and get the lowest role if the resource visibility is set to public. // Otherwise, they do not get a role. if (!user) { var implicitRole = null; if (resourceVisibility === 'public') { implicitRole = rolesPriority[0]; } return callback(null, implicitRole, false); // Check admin access } else if (user.isGlobalAdmin() || user.isTenantAdmin(resourceAlias)) { // The user is an administrator, give them the highest allowable access return callback(null, rolesPriority[rolesPriority.length - 1], true); } // If we aren't an explicit manager, ability to interact is based on tenant privacy boundaries var actorAlias = (user) ? user.tenant.alias : ctx.tenant().alias; var canInteract = TenantsUtil.canInteract(actorAlias, resourceAlias); // Check implicit member access if (resourceVisibility === 'public') { // Public resources can be seen by anyone return callback(null, rolesPriority[0], canInteract); } else if (resourceVisibility === 'loggedin' && TenantsUtil.isLoggedIn(ctx, resourceAlias)) { // We are from the same tenant as the "loggedin" resource, so we can view it and interact return callback(null, rolesPriority[0], canInteract); } return callback(); };
var _canJoin = function(ctx, group, hasRole) { // You cannot join a group of which you're already a member if (hasRole) { return false; // Anonymous users can never join a group } else if (!ctx.user()) { return false; // TODO: For now we only support joining a group if it's joinable property has been set to YES } else if (group.joinable !== AuthzConstants.joinable.YES) { return false; // By this point we know the user is logged in and that people are allowed to join the group. // If the user is on the same tenant as the group, he can always request to join } else if (ctx.user().tenant.alias === group.tenant.alias) { return true; // If the user is from another tenant, he can only join the group if the tenant permeability // settings allow it AND the group's visibility setting is set to public } else if (TenantsUtil.canInteract(ctx.user().tenant.alias, group.tenant.alias) && group.visibility === AuthzConstants.visibility.PUBLIC) { return true; // In any other case, you cannot join the group } else { return false; } };
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); });
var _canView = function(ctx, group, hasRole) { if (group.visibility === AuthzConstants.visibility.PUBLIC) { return true; // If we have a role in the group we can always view it } else if (hasRole) { return true; // For the following checks, we're dealing with users who are not a member of the group // An anonymous user can never see a non-public group } else if (!ctx.user()) { return false; // A group that is joinable can always be seen by an authenticated user who belongs to a tenant that can interact. Even if it is private } else if (group.joinable !== AuthzConstants.joinable.NO && TenantsUtil.canInteract(ctx.tenant().alias, group.tenant.alias)) { return true; // If the group is visible to logged in users, I get access if I'm logged in } else if (group.visibility === AuthzConstants.visibility.LOGGEDIN && TenantsUtil.isLoggedIn(ctx, group.tenant.alias)) { return true; // If the user is admin of the group's tenant, they have access } else if (ctx.user().isAdmin(group.tenant.alias)) { return true; } // In all other cases the user can't view the group return false; };
_.each(routes, function(route) { var routeTenantAlias = AuthzUtil.getResourceFromId(route.resourceId).tenantAlias; if (TenantsUtil.canInteract(entityTenantAlias, routeTenantAlias)) { includedRoutes.push(route); } else { excludedRoutes.push(route); } });
PrincipalsUtil.getPrincipals(ctx, _.keys(members), function(err, principals) { if (err) { return callback(err); } // Verify no user addition violates tenant boundaries by being added for (var principalId in principals) { var principal = principals[principalId]; if (members[principalId] && !TenantsUtil.canInteract(principal.tenant.alias, group.tenant.alias)) { return callback({'code': 400, 'msg': 'External user ' + principal.id + ' cannot be added as a member of this group.'}); } } AuthzAPI.updateRoles(group.id, members, callback); });
var resolveImplicitRole = module.exports.resolveImplicitRole = function(ctx, resourceId, resourceTenantAlias, resourceVisibility, rolesPriority, callback) { var user = ctx.user(); // Anonymous cannot interact with anything and get the lowest role if the resource visibility is set to public. // Otherwise, they do not get a role. if (!user) { var implicitRole = null; if (resourceVisibility === AuthzConstants.visibility.PUBLIC) { implicitRole = rolesPriority[0]; } return callback(null, implicitRole, false); // The user has maximum access on themself } else if (user.id === resourceId) { return callback(null, rolesPriority[rolesPriority.length - 1], true); // Check admin access } else if (user.isGlobalAdmin() || user.isTenantAdmin(resourceTenantAlias)) { // The user is an administrator, give them the highest allowable access return callback(null, rolesPriority[rolesPriority.length - 1], true); } // Determine the implicit interaction capabilities between the user and the resource var actorTenantAlias = (user) ? user.tenant.alias : ctx.tenant().alias; var canInteract = TenantsUtil.canInteract(actorTenantAlias, resourceTenantAlias); // Check implicit member access if (resourceVisibility === AuthzConstants.visibility.PUBLIC) { // Public resources can be seen by anyone return callback(null, rolesPriority[0], canInteract); } else if (resourceVisibility === AuthzConstants.visibility.LOGGEDIN && TenantsUtil.isLoggedIn(ctx, resourceTenantAlias)) { // We are from the same tenant as the "loggedin" resource, so we can view it and interact return callback(null, rolesPriority[0], canInteract); } return callback(); };
var illegalPrincipalIds = _.filter(principalIds, function(principalId) { var principalTenantAlias = AuthzUtil.getPrincipalFromId(principalId).tenantAlias; // The principalId is invalid if the violates boundaries between the actor and the principal or the resource and the principal return (!TenantsUtil.canInteract(actorTenantAlias, principalTenantAlias) || !TenantsUtil.canInteract(resourceTenantAlias, principalTenantAlias)); });
var canInteract = module.exports.canInteract = function(ctx, resourceTenantAlias, principalIds, callback) { var actorTenantAlias = ctx.user().tenant.alias; // Check the tenant violations: // * A direct violation between actorTenantAlias and resourceTenantAlias if (!TenantsUtil.canInteract(actorTenantAlias, resourceTenantAlias)) { return callback(null, false); } // * A direct violation between actorTenantAlias and principalIds[i].tenant var illegalPrincipalIds = _.filter(principalIds, function(principalId) { var principalTenantAlias = AuthzUtil.getPrincipalFromId(principalId).tenantAlias; // The principalId is invalid if the violates boundaries between the actor and the principal or the resource and the principal return (!TenantsUtil.canInteract(actorTenantAlias, principalTenantAlias) || !TenantsUtil.canInteract(resourceTenantAlias, principalTenantAlias)); }); if (illegalPrincipalIds.length > 0) { return callback(null, false, illegalPrincipalIds); } // Now check if there are any access violations between the actor and a principal PrincipalsUtil.getPrincipals(ctx, principalIds, function(err, principalObjects) { if (err) { return callback(err); } var illegalPrincipalIds = []; var canInteract = true; /*! * Checks if the current user can interact with al the * principals in an array (recursively). * * @param {Principal[]} The array of principals to check. Will be empty when done. */ var checkPrincipal = function(principals) { if (principals.length === 0) { // All the principals have been checked. // Only fill in the illegalPrincipalIds parameter if there are actually any illegal ones. return callback(null, canInteract, (illegalPrincipalIds.length > 0) ? illegalPrincipalIds : null); } var principal = principals.pop(); // If the target-principal is a user we need to check tenant permeability and user visibility settings. // The resolveImplicitRole function will take care of that. if (PrincipalsUtil.isUser(principal.id)) { resolveImplicitRole(ctx, principal.tenant.alias, principal.visibility, ['viewer', 'manager'], function(err, implicitRole, canInteractWithUser) { if (err) { return callback(err); } if (!canInteractWithUser) { canInteract = false; illegalPrincipalIds.push(principal.id); } // Move on to the next principal. checkPrincipal(principals); }); // If the target-principal is a group we need to check if the current user can view that group and tenant interactions are allowed. // In case the group is not public we'd need to determine our role on that group, re-using resolveEffectiveRole will // make sure we can interact with the group and we have a role on it if necessary. } else { resolveEffectiveRole(ctx, principal.id, principal.tenant.alias, principal.visibility, ['viewer', 'manager'], function(err, effectiveRole, canInteractWithGroup) { if (err) { return callback(err); } if (!canInteractWithGroup) { canInteract = false; illegalPrincipalIds.push(principal.id); } // Move on to the next principal. checkPrincipal(principals); }); } }; var principals = _.values(principalObjects); checkPrincipal(principals); }); };
var illegalPrincipalIds = _.filter(principals, function(principal) { return !TenantsUtil.canInteract(resourceTenantAlias, AuthzUtil.getResourceFromId(principal.id).tenantAlias); });
var invalidPrincipals = _.filter(checkTenantInteraction, function(resource) { return (!TenantsUtil.canInteract(ctx.user().tenant.alias, resource.tenant.alias)); });
var resolveTargetLibraryAccess = module.exports.resolveTargetLibraryAccess = function(ctx, libraryOwner, callback) { var libraryTenantAlias = AuthzUtil.getResourceFromId(libraryOwner.id).tenantAlias; // Admin users always get private libraries if (ctx.user() && (ctx.user().isGlobalAdmin() || ctx.user().isTenantAdmin(libraryTenantAlias))) { return callback(null, true, LibraryConstants.visibility.PRIVATE); } // User is not an admin of the target library tenant, so we'll have to do some acrobatics to resolve the visibility // Check if we are looking at a user library if (PrincipalsUtil.isUser(libraryOwner.id)) { // If the current user is requesting his own library we can return the private stream. if (ctx.user() && ctx.user().id === libraryOwner.id) { return callback(null, true, LibraryConstants.visibility.PRIVATE); // A non-library owner is requesting the library, we first need to check the owner's visibility setting. } else if (libraryOwner.visibility === LibraryConstants.visibility.PRIVATE || (libraryOwner.visibility === LibraryConstants.visibility.LOGGEDIN && !TenantsUtil.isLoggedIn(ctx, libraryTenantAlias))) { return callback(null, false); // We give the user the public stream in case the user is not logged in to the user's tenant } else if (!TenantsUtil.isLoggedIn(ctx, libraryTenantAlias)) { return callback(null, true, LibraryConstants.visibility.PUBLIC); // We give the user the logged in stream in case the current user is not the same as the library we're looking at } else if (ctx.user().id !== libraryOwner.id) { return callback(null, true, LibraryConstants.visibility.LOGGEDIN); } // Check if we are looking at a group library } else if (PrincipalsUtil.isGroup(libraryOwner.id)) { // Anonymous user can only access the public stream if the group has been set to public if (!ctx.user() || !TenantsUtil.canInteract(ctx.user().tenant.alias, libraryTenantAlias)) { if (libraryOwner.visibility === LibraryConstants.visibility.PUBLIC) { return callback(null, true, LibraryConstants.visibility.PUBLIC); } else { return callback(null, false); } } // We've established that the user is logged in. // We need to determine the role the user has in the group so we can show the correct stream. AuthzAPI.hasAnyRole(ctx.user().id, libraryOwner.id, function(err, hasAnyRole) { if (err) { return callback(err); } if (hasAnyRole) { return callback(null, true, LibraryConstants.visibility.PRIVATE); } else if (!TenantsUtil.isLoggedIn(ctx, libraryTenantAlias)) { if (libraryOwner.visibility === LibraryConstants.visibility.PUBLIC) { return callback(null, true, LibraryConstants.visibility.PUBLIC); } else { return callback(null, false); } } else if (libraryOwner.visibility === LibraryConstants.visibility.PRIVATE) { return callback(null, false); } else { return callback(null, true, LibraryConstants.visibility.LOGGEDIN); } }); // If the passed in principal id is neither a group or user, we return an error } else { return callback({'code': 400, 'msg': 'An unrecognized principal has been provided'}); } };