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

Allow admin to blacklist groups for the files_sharing app #31672

Merged
merged 9 commits into from
Jun 21, 2018

Conversation

jvillafanez
Copy link
Member

@jvillafanez jvillafanez commented Jun 7, 2018

Description

Allow admins to blacklist groups so user can't share with those groups. Blacklist will be done per backend + group displayname for now.

Related Issue

#31578

Motivation and Context

How Has This Been Tested?

Manually tested for now

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@codecov
Copy link

codecov bot commented Jun 7, 2018

Codecov Report

Merging #31672 into master will increase coverage by 0.01%.
The diff coverage is 91.3%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master   #31672      +/-   ##
============================================
+ Coverage     63.16%   63.17%   +0.01%     
- Complexity    18444    18461      +17     
============================================
  Files          1158     1161       +3     
  Lines         69284    69327      +43     
  Branches       1260     1260              
============================================
+ Hits          43760    43800      +40     
- Misses        25155    25158       +3     
  Partials        369      369
Flag Coverage Δ Complexity Δ
#javascript 52.39% <ø> (ø) 0 <ø> (ø) ⬇️
#phpunit 64.41% <91.3%> (+0.01%) 18461 <14> (+17) ⬆️
Impacted Files Coverage Δ Complexity Δ
apps/files_sharing/lib/API/OCSShareWrapper.php 5.71% <0%> (-0.17%) 9 <0> (ø)
...files_sharing/lib/Controller/ShareesController.php 89.87% <100%> (+0.08%) 102 <0> (+2) ⬆️
apps/files_sharing/lib/API/Share20OCS.php 93.41% <100%> (+0.04%) 183 <0> (+1) ⬆️
apps/files_sharing/templates/settings.php 100% <100%> (ø) 0 <0> (?)
...s/files_sharing/lib/Panels/Admin/SettingsPanel.php 100% <100%> (ø) 4 <4> (?)
apps/files_sharing/lib/SharingBlacklist.php 92.3% <92.3%> (ø) 10 <10> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9edd97a...bf8f99d. Read the comment docs.

@jvillafanez jvillafanez force-pushed the sharing_blacklist_groups branch from 1d21dda to 28c782d Compare June 7, 2018 12:36
Copy link
Contributor

@PVince81 PVince81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See review comments.

I'm not convinced about the need to have a cache here.

*/

$(document).ready(function(){
$('#files_sharing .files_sharing_settings').change(function(){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the generic functions didn't work ? might be possible to adjust the generic existing JS function for saving settings to include textarea additionally to input

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of any generic function to save values in the appconfig table. I've just found the OC.AppConfig.setValue function to perform the operation (code already adjusted to use it, soon), but there isn't auto binding, so I'll need to bind the event manually.

@@ -396,6 +401,9 @@ public function createShare() {
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid group'));
}
if ($this->sharingBlacklist->isGroupBlacklisted($this->groupManager->get($shareWith))) {
return new \OC\OCS\Result(null, 403, $this->l->t('The group is blacklisted for sharing'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this qualifies as information disclosure, maybe just return 404 "Please specify a valid group" as above

cc @settermjd

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly? I'm don't believe that this is information disclosure. However, something more generic, yet still informative, such as "This group is not available for sharing, would work better.

* Set with the known configuration keys. Only these keys should be able to be set and retrieved
*/
private $knownKeys = [
'blacklisted_group_displaynames' => true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like early optimization/refactoring before it is even needed, but ok

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove the class because get and set for the appconfig table will go through core (using OC.Appconfig in js). No need to handle this on our own.

}, $groupBackendList);

$tmpl = new Template('files_sharing', 'settings');
$tmpl->assign('backendNames', $backendNames);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great that you put this in 👍

use OCP\IConfig;
use OCP\IGroup;

class SharingBlacklist {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add PHPDoc. Is this the actual black list array or just a utility class ? This is what a developer wonders about when seeing only the class name used in various places


/**
* Check if the target group is blacklisted
* @param IGroup $group the group to check
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing @return

$blacklistedComponents = explode("\n", $configuredBlacklist);

$result = [];
foreach ($blacklistedComponents as $blacklistedComponent) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this operation so expensive that we need a cache with all complexity that comes with it ?

in general I don't think we expect more than 10 or so blacklisted groups

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sharees we'll need to check if each group is blacklisted. This leads potentially to a lot of calls to this function.
Since the blacklist will be stored as a large string, each call will need to fetch the string and process it in order to make the decission. This might be heavy we need to check a lot of groups.
In addition, I'd rather not rely on the appconfig to be cached to have a decent performance.

I also don't expect a lot of blacklisted groups, but we might need to include more options for the blacklist, such as blacklist by group id, and I'd prefer the check to be as fast as possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fair enough. Make sure the unit tests also take the cache into account and prove that it properly invalidates, if applicable.

<?php endforeach; ?>
</ul>
</div>
<textarea placeholder="OC\Group\Database::localGroups&#10OCA\User_LDAP\Group_Proxy::ldapGroup" class="files_sharing_settings" name="blacklisted_group_displaynames"><?php p($_['blacklistedDisplaynames']); ?></textarea>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this weird &#10 entity here ? likely a newline. I suggest to put this in a template variable instead and initialize it from the PHP template in which you can write it in a more readable format that includes \n

@jvillafanez
Copy link
Member Author

Code should be mostly ready, but there are tests missing for the SettingsPanel.php and SharingBlacklist.php and the getBackends method of the IGroupManager

@mmattel
Copy link
Contributor

mmattel commented Jun 11, 2018

Documentation relevant

$groupBackend = \get_class($group->getBackend());
$groupDisplayname = $group->getDisplayName();

if (isset($this->blacklistCache['displaynames'][$groupBackend][$groupDisplayname])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still concerned we do the matching on the displaynames. Can't we store ID's but allow admins to specify displaynames?

@jvillafanez jvillafanez force-pushed the sharing_blacklist_groups branch from 6061a60 to 5364d96 Compare June 12, 2018 14:47
@jvillafanez jvillafanez force-pushed the sharing_blacklist_groups branch from 5364d96 to bf8f99d Compare June 12, 2018 15:28
@jvillafanez jvillafanez changed the title [WIP] Allow admin to blacklist groups for the files_sharing app Allow admin to blacklist groups for the files_sharing app Jun 12, 2018
@jvillafanez
Copy link
Member Author

Backport to stable10 in #31740

Copy link
Contributor

@PVince81 PVince81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apart from the settings cache this looks good

}

private function fetchBlacklistedReceiverGroupIds() {
$configuredBlacklist = $this->config->getAppValue('files_sharing', 'blacklisted_receiver_groups', '[]');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we now removed the class names from the group ids, maybe we don't need the cache any more ?

based on @tomneedham's comment here #31578 (comment) we can use the same approach as "exclude groups from sharing" which also doesn't expect a higher number of groups than the blacklist.

also now the logic for reading the settings has simplified itself since we don't store the prefix any more

@jvillafanez can you remove the cache and read directly from the setting ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll still need this. The problem is with sharees: we'll fetch a list of groups and each group needs to be checked whether is blacklisted or not; without proper caching we'll need to fetch the list from the configuration and then parse the list for each group.
Even if we rely on the configuration being cached, we still need to parse the stored json several times.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there no way to change the caller code then to only fetch the blacklist array once ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might use a filterBlacklistedGroups(array $groups) function, but the calls would be a bit weird:

  • The share API just needs to check one group, so you'll need to wrap it in an array, which feels bad.
  • For the sharees, the intention would be to return the groups that aren't blacklisted. However, in case we need to do something with the blacklisted groups (maybe log them) things will get complicated both for the caller and the implementation. Note that, in any case, we'll need to traverse the sharees list twice.

I agree the code is a bit complex, but it should be fine if this complexity is isolated in this class. On the other hand, the usage is simple. Maybe we can document in the class what is the expected usage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was more thinking that the caller code calls "give me all the blacklisted groups", store it in a local var and then use that when checking against each group. Unless the caller codes is spread across various classes, this could work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll move the complexity to the callers. you still need to compare the target group with the blacklist. This comparison code will be spread into several classes, and the way you'll likely compare will use the in_array function instead of more efficients approaches (you don't want to add more complexity to the caller).

Current code has a faster performance an a cleaner interface at a cost of including a cache.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current code has a faster performance an a cleaner interface at a cost of including a cache.

Okay, let's leave it then

Copy link
Contributor

@PVince81 PVince81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good.

What happens if shares exist that were shared with a group that has now been blacklisted ? I assume these must still appear in the shares list and be unshareable by users. I don't think we need to add a mechanism to also exclude existing shares/mounts that point to blacklisted groups.

@tomneedham thoughts on the latter ?

@PVince81
Copy link
Contributor

@jvillafanez let's move on with this. The additional concern, if addressed in any way, will not affect the code of this PR.

@PVince81
Copy link
Contributor

I've added a comment here to address as part of the ticket/concept before we close it: #31578 (comment)

@PVince81 PVince81 merged commit 2df5b94 into master Jun 21, 2018
@PVince81 PVince81 deleted the sharing_blacklist_groups branch June 21, 2018 08:11
@PVince81
Copy link
Contributor

@jvillafanez please adjust the backport PR and ping me there when done


/**
* Class to handle a blacklist for sharing. The main functionality is to check if a particular group
* has been blacklisted for sharing, which means that noone should share with that group.
Copy link
Contributor

@settermjd settermjd Jul 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"should share", or be "should able to share"?

@@ -284,7 +290,7 @@ protected function getGroups($search) {
foreach ($groups as $group) {
// FIXME: use a more efficient approach
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of a more efficient approach off the top of my head. However, you could use an implementation of a FilterIterator to simplify the logic here, making it that much simpler to maintain over time.

return [
[$groupMock1, '["Mygroup"]'],
[$groupMock1, '["Mygroup", "my_other_group"]'],
[$groupMock1, '["one group", "Mygroup"]'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this, effectively, the same as the line above?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were issues in various places with group names that contain a space - maybe that was a concern here?

@lock
Copy link

lock bot commented Jul 30, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jul 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants