Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documents in a folder should be shared with its group by default [v2] #2096

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 102 additions & 33 deletions packages/oae-authz/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import util from 'util';
import _ from 'underscore';
import { dropRepeats, isEmpty, contains, not, equals } from 'ramda';

import * as Cassandra from 'oae-util/lib/cassandra';
import * as OaeUtil from 'oae-util/lib/util';
Expand Down Expand Up @@ -160,8 +161,8 @@ const getAllRoles = function(principalId, resourceId, callback) {
};

/**
* Given a principal and a resource, determine all the roles that the principal has on the resource, by virtue of
* indirect group inheritance.
* Given a principal and a resource, determine all the roles that the principal has on the resource,
* by virtue of indirect group inheritance.
*
* @param {String} principalId The principal id. This can be a user or a group
* @param {String} resourceId The resource id. This can be a group as well.
Expand All @@ -171,34 +172,99 @@ const getAllRoles = function(principalId, resourceId, callback) {
* @api private
*/
const _getIndirectRoles = function(principalId, resourceId, callback) {
/**
* If the current membership is 'inherit' that means that the permissions
* the user should hold on the resource indirectly should inherit
* from the permissions the user has as a member of that group.
*
* Example:
* FolderF is created by UserA
* UserA adds UserB to the folder group as manager
* UserA creates Resource R (some content item) inside folderF
* The folder group will hold "inherit" as a role on ResourceR (table AuthzMembers)
* UserB will have indirect permissions on folderF as manager
*
* .─.
* ( )
* creates `─'
* ┌───────────── User A
* │
* │
* ▼
* ┌───────────────┐
* │ Folder F │
* └───────────────┘
* ▲
* │ .─.
* │ ( )
* │ manager `─'
* ├──────────── User B
* │
* │ .─.
* │ ( )
* │ viewer `─'
* └──────────── User C
*
*
*
* .─.
* ( )
* `─' creates
* User A ──────────┐
* │
* │
* ▼
* ┌───────────────┐ inside ┌───────────────┐
* │ Folder F │◀──────────────│ Content item │
* └───────────────┘ └───────────────┘
* ▲ ▲
* │ .─.
* │ ( ) │
* │ manager `─' manager
* ├────────── User B ─ ─ ─ ─ ─ ┤
* │
* │ .─. │
* │ ( )
* │ viewer `─' viewer │
* └────────── User C ─ ─ ─ ─ ─
*
*/
// Get the groups that are directly associated to the resource
_getResourceGroupMembers(resourceId, (err, groups) => {
if (err) {
return callback(err);
}
_getResourceGroupMembers(resourceId, (err, resourceGroupRoles) => {
if (err) return callback(err);

if (_.isEmpty(groups)) {
return callback(null, []);
}
if (isEmpty(resourceGroupRoles)) return callback(null, []);

// Check whether any of these groups are part of the user's direct memberships
const groupIds = _.keys(groups);
const groupIds = _.keys(resourceGroupRoles);

// Make sure that the user's memberships have been exploded and cached
_checkGroupMembershipsForUser(principalId, groupIds, (err, memberships) => {
if (err) {
return callback(err);
}

// Add the roles of the matching groups
const allRoles = [];
for (const element of memberships) {
if (!_.contains(allRoles, groups[element])) {
allRoles.push(groups[element]);
}
}
_checkGroupMembershipsForUser(principalId, groupIds, (err, groupsPrincipalBelongsTo) => {
if (err) return callback(err);

const getIndirectMembershipRole = (memberships, allRoles, callback) => {
const eachMembership = memberships.pop();

if (not(eachMembership)) return callback(null, dropRepeats(allRoles));

const roleOnResource = resourceGroupRoles[eachMembership];
// if (equals(roleOnGroup, 'inherit')) {
// check what the direct role of the principal on the group is, and use that
_getDirectRole(principalId, eachMembership, (err, directRoleOnGroup) => {
if (err) return callback(err);
allRoles.push(directRoleOnGroup);
// debug
console.log('The group role is: ' + roleOnResource);
console.log('The indirect role through the folder is ' + directRoleOnGroup);
return getIndirectMembershipRole(memberships, allRoles, callback);
});
// } else {
// allRoles.push(roleOnGroup);
// return getIndirectMembershipRole(memberships, allRoles, callback);
// }
};

return callback(null, allRoles);
getIndirectMembershipRole(groupsPrincipalBelongsTo, [], callback);
});
});
};
Expand Down Expand Up @@ -347,8 +413,9 @@ const _hasRole = function(principalId, resourceId, role, callback) {
};

/**
* Assign one or multiple principals a role on a resource instance. If the user already has a role, it will simply be updated. When
* false is passed in as a role, the role for that principal will be removed.
* Assign one or multiple principals a role on a resource instance.
* If the user already has a role, it will simply be updated.
* When false is passed in as a role, the role for that principal will be removed.
*
* @param {String} resourceId The resource id
* @param {Object} changes An object keyed by principal id, whose values are the role changes to apply on the resource
Expand Down Expand Up @@ -389,8 +456,9 @@ const updateRoles = function(resourceId, changes, callback) {
};

/**
* Assign multiple principals a role on a resource instance. If the user already has a role, it will simply be updated. When
* false is passed in as a role, the role for that principal will be removed.
* Assign multiple principals a role on a resource instance.
* If the user already has a role, it will simply be updated.
* When false is passed in as a role, the role for that principal will be removed.
*
* @param {String} resourceId The resource id
* @param {Object} changes An object keyed by principal id, whose values are the role changes to apply on the resource
Expand Down Expand Up @@ -643,7 +711,8 @@ const _checkGroupMembershipsForUser = function(userId, groupIds, callback) {
};

/**
* Get all the Authz groups of which a principal is a member. This includes all group ancestors to which the user is indirectly a member.
* Get all the Authz groups of which a principal is a member.
* This includes all group ancestors to which the user is indirectly a member.
* Once these have been retrieved, they will be cached inside of Cassandra for fast permission checks
*
* @param {String} principalId The principal id for whom we want to explode the group memberships
Expand Down Expand Up @@ -888,8 +957,8 @@ const getPrincipalMembershipsGraph = function(principalId, callback) {
};

/**
* Gets all the Authz groups of which a principal (either user or group) is a member. This includes all group ancestors to
* which the user is indirectly a member.
* Gets all the Authz groups of which a principal (either user or group) is a member.
* This includes all group ancestors to which the user is indirectly a member.
*
* @param {String} principalId The principal id for which to retrieve all the group memberships
* @param {String} start Determines the point at which group memberships members are returned for paging purposes. If not provided, the first x elements will be returned
Expand Down Expand Up @@ -1472,9 +1541,9 @@ const computeMemberRolesAfterChanges = function(resourceId, memberRoles, opts, c

/**
* Determine the **effective** role a user has in a resource. Though a user can have multiple roles on a resource
* by virtue of indirect group membership, this determines the highest level of access granted. This check is not only
* implicit, but includes explicit role membership lookup. Therefore, its output alone can be used as an authoritative
* source of access information.
* by virtue of indirect group membership, this determines the highest level of access granted.
* This check is not only implicit, but includes explicit role membership lookup.
* Therefore, its output alone can be used as an authoritative source of access information.
*
* @param {User} [user] The user for which to check access. If unspecified, implies an anonymous user
* @param {Resource} resource The resource against which to check for access
Expand Down
11 changes: 8 additions & 3 deletions packages/oae-content/lib/activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ ContentAPI.emitter.on(
/*!
* Post a content-revision activity when a user has made an edit to a collaborative document (even though there is technically no new revision)
*/
ContentAPI.emitter.on(ContentConstants.events.EDITED_COLLABSHEET, (ctx, contentObj) => {
// debug
console.log('This is content-revision activity');
});

ContentAPI.emitter.on(ContentConstants.events.EDITED_COLLABDOC, (ctx, contentObj) => {
const millis = Date.now();
const actorResource = new ActivityModel.ActivitySeedResource('user', ctx.user().id, {
Expand All @@ -246,9 +251,9 @@ ContentAPI.emitter.on(ContentConstants.events.EDITED_COLLABDOC, (ctx, contentObj
ActivityAPI.postActivity(ctx, activitySeed);
});

/// ////////////////////////////
// CONTENT-RESTORED-REVISION //
/// ////////////////////////////
/**
* Content-restored-revision
*/

ActivityAPI.registerActivityType(ContentConstants.activity.ACTIVITY_CONTENT_RESTORED_REVISION, {
groupBy: [{ object: true }],
Expand Down
Loading