Переглянути джерело

allow admin to disable sharing for specific groups of users

tags/v7.0.0alpha2
Bjoern Schiessle 10 роки тому
джерело
коміт
12338e0ef0

+ 4
- 0
apps/files/css/files.css Переглянути файл

@@ -358,6 +358,10 @@ table td.filename form { font-size:14px; margin-left:48px; margin-right:48px; }
padding: 28px 14px 19px !important;
}

#fileList .action.action-share-notification span, img, a {
cursor: default !important;
}

a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; }

/* Actions for selected files */

+ 24
- 8
apps/files_sharing/js/share.js Переглянути файл

@@ -27,13 +27,29 @@ $(document).ready(function() {
}

$('#fileList').on('fileActionsReady',function(){
var $fileList = $(this);
var allShared = $fileList.find('[data-share-owner] [data-Action="Share"]');
allShared.addClass('permanent');
allShared.find('span').text(function(){
var $owner = $(this).closest('tr').attr('data-share-owner');
return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner});
});
// if no share action exists because the admin disabled sharing for this user
// we create a share notification action to inform the user about files
// shared with him otherwise we just update the existing share action.
var allShared;
if (oc_appconfig.core.sharingDisabledForUser) {
var $fileList = $(this);
allShared = $fileList.find('[data-share-owner]');
var shareNotification = '<a class="action action-share-notification permanent"' +
' data-action="Share-Notification" href="#" original-title="">' +
' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>';
$(allShared).find('.fileactions').append(function() {
var owner = $(this).closest('tr').attr('data-share-owner');
var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner});
return shareNotification + '<span> ' + shareBy + '</span></span>';
});
} else {
allShared = $fileList.find('[data-share-owner] [data-Action="Share"]');
allShared.addClass('permanent');
allShared.find('span').text(function(){
var $owner = $(this).closest('tr').attr('data-share-owner');
return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner});
});
}

// FIXME: these calls are also working on hard-coded
// list selectors...
@@ -48,7 +64,7 @@ $(document).ready(function() {
}
});

FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) {
FileActions.register('all', 'Share', OC.PERMISSION_SHARE, OC.imagePath('core', 'actions/share'), function(filename) {
var tr = FileList.findFileEl(filename);
var itemType = 'file';
if ($(tr).data('type') == 'dir') {

+ 9
- 5
apps/files_sharing/lib/permissions.php Переглянути файл

@@ -30,6 +30,7 @@ class Shared_Permissions extends Permissions {
* @return int (-1 if file no permissions set)
*/
public function get($fileId, $user) {

if ($fileId == -1) {
// if we ask for the mount point return -1 so that we can get the correct
// permissions by the path, with the root fileId we have no idea which share is meant
@@ -37,11 +38,14 @@ class Shared_Permissions extends Permissions {
}
$source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE,
null, true);

$permission = -1;

if ($source) {
return $source['permissions'];
} else {
return -1;
$permission = $this->updatePermissions($source['permissions']);
}

return $permission;
}

/**
@@ -55,7 +59,7 @@ class Shared_Permissions extends Permissions {
$source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE,
null, false);
if ($source) {
return $source['permissions'];
return $this->updatePermissions($source['permissions']);
} else {
return -1;
}
@@ -106,7 +110,7 @@ class Shared_Permissions extends Permissions {
$result = $query->execute(array($parentId));
$filePermissions = array();
while ($row = $result->fetchRow()) {
$filePermissions[$row['fileid']] = $permissions;
$filePermissions[$row['fileid']] = $this->updatePermissions($permissions);
}
return $filePermissions;
}

+ 8
- 0
apps/files_sharing/lib/sharedstorage.php Переглянути файл

@@ -108,6 +108,11 @@ class Shared extends \OC\Files\Storage\Common {
if (pathinfo($target, PATHINFO_EXTENSION) === 'part') {
$permissions |= \OCP\PERMISSION_DELETE;
}

if (\OC_Util::isSharingDisabledForUser()) {
$permissions &= ~\OCP\PERMISSION_SHARE;
}

return $permissions;
}

@@ -198,6 +203,9 @@ class Shared extends \OC\Files\Storage\Common {
}

public function isSharable($path) {
if (\OCP\Util::isSharingDisabledForUser()) {
return false;
}
return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE);
}


+ 72
- 0
apps/files_sharing/tests/api.php Переглянути файл

@@ -171,6 +171,78 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base {
$appConfig->setValue('core', 'shareapi_enforce_links_password', 'no');
}

/**
* @medium
*/
function testSharePermissions() {

// sharing file to a user should work if shareapi_exclude_groups is set
// to no
\OC_Appconfig::setValue('core', 'shareapi_exclude_groups', 'no');
$_POST['path'] = $this->filename;
$_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2;
$_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER;

$result = Share\Api::createShare(array());

$this->assertTrue($result->succeeded());
$data = $result->getData();

$share = $this->getShareFromId($data['id']);

$items = \OCP\Share::getItemShared('file', $share['item_source']);

$this->assertTrue(!empty($items));

$fileinfo = $this->view->getFileInfo($this->filename);

$result = \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);

$this->assertTrue($result);

// exclude groups, but not the group the user belongs to. Sharing should still work
\OC_Appconfig::setValue('core', 'shareapi_exclude_groups', 'yes');
\OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', 'admin,group1,group2');

$_POST['path'] = $this->filename;
$_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2;
$_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER;

$result = Share\Api::createShare(array());

$this->assertTrue($result->succeeded());
$data = $result->getData();

$share = $this->getShareFromId($data['id']);

$items = \OCP\Share::getItemShared('file', $share['item_source']);

$this->assertTrue(!empty($items));

$fileinfo = $this->view->getFileInfo($this->filename);

$result = \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);

$this->assertTrue($result);

// now we exclude the group the user belongs to ('group'), sharing should fail now
\OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', 'admin,group');

$_POST['path'] = $this->filename;
$_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2;
$_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER;

$result = Share\Api::createShare(array());

$this->assertFalse($result->succeeded());

// cleanup
\OC_Appconfig::setValue('core', 'shareapi_exclude_groups', 'no');
\OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', '');
}


/**
* @medium

+ 2
- 0
apps/files_sharing/tests/base.php Переглянути файл

@@ -109,6 +109,8 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase {

if ($create) {
\OC_User::createUser($user, $password);
\OC_Group::createGroup('group');
\OC_Group::addToGroup($user, 'group');
}

\OC_Util::tearDownFS();

+ 2
- 2
apps/files_sharing/tests/proxy.php Переглянути файл

@@ -25,9 +25,9 @@ require_once __DIR__ . '/base.php';
use OCA\Files\Share;

/**
* Class Test_Files_Sharing_Api
* Class Test_Files_Sharing_Proxy
*/
class Test_Files_Sharing_Api extends Test_Files_Sharing_Base {
class Test_Files_Sharing_Proxy extends Test_Files_Sharing_Base {

const TEST_FOLDER_NAME = '/folder_share_api_test';


+ 1
- 0
core/js/config.php Переглянути файл

@@ -80,6 +80,7 @@ $array = array(
'defaultExpireDate' => $defaultExpireDate,
'defaultExpireDateEnforced' => $enforceDefaultExpireDate,
'enforcePasswordForPublicLink' => \OCP\Util::isPublicLinkPasswordRequired(),
'sharingDisabledForUser' => \OCP\Util::isSharingDisabledForUser(),
)
)
),

+ 16
- 3
lib/private/files/cache/permissions.php Переглянути файл

@@ -36,7 +36,7 @@ class Permissions {
$sql = 'SELECT `permissions` FROM `*PREFIX*permissions` WHERE `user` = ? AND `fileid` = ?';
$result = \OC_DB::executeAudited($sql, array($user, $fileId));
if ($row = $result->fetchRow()) {
return $row['permissions'];
return $this->updatePermissions($row['permissions']);
} else {
return -1;
}
@@ -78,7 +78,7 @@ class Permissions {
$result = \OC_DB::executeAudited($sql, $params);
$filePermissions = array();
while ($row = $result->fetchRow()) {
$filePermissions[$row['fileid']] = $row['permissions'];
$filePermissions[$row['fileid']] = $this->updatePermissions($row['permissions']);
}
return $filePermissions;
}
@@ -99,7 +99,7 @@ class Permissions {
$result = \OC_DB::executeAudited($sql, array($parentId, $user));
$filePermissions = array();
while ($row = $result->fetchRow()) {
$filePermissions[$row['fileid']] = $row['permissions'];
$filePermissions[$row['fileid']] = $this->updatePermissions($row['permissions']);
}
return $filePermissions;
}
@@ -140,4 +140,17 @@ class Permissions {
}
return $users;
}

/**
* check if admin removed the share permission for the user and update the permissions
*
* @param int $permissions
* @return int
*/
protected function updatePermissions($permissions) {
if (\OCP\Util::isSharingDisabledForUser()) {
$permissions &= ~\OCP\PERMISSION_SHARE;
}
return $permissions;
}
}

+ 4
- 0
lib/private/files/storage/common.php Переглянути файл

@@ -81,6 +81,10 @@ abstract class Common implements \OC\Files\Storage\Storage {
}

public function isSharable($path) {
if (\OC_Util::isSharingDisabledForUser()) {
return false;
}

return $this->isReadable($path);
}


+ 9
- 1
lib/private/share/share.php Переглянути файл

@@ -485,15 +485,23 @@ class Share extends \OC\Share\Constants {
$itemSourceName = $itemSource;
}

// verify that the file exists before we try to share it
// check if file can be shared
if ($itemType === 'file' or $itemType === 'folder') {
$path = \OC\Files\Filesystem::getPath($itemSource);
// verify that the file exists before we try to share it
if (!$path) {
$message = 'Sharing %s failed, because the file does not exist';
$message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName));
\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR);
throw new \Exception($message_t);
}
// verify that the user has share permission
if (!\OC\Files\Filesystem::isSharable($path)) {
$message = 'You are not allowed to share %s';
$message_t = $l->t('You are not allowed to share %s', array($itemSourceName));
\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR);
throw new \Exception($message_t);
}
}

//verify that we don't share a folder which already contains a share mount point

+ 23
- 0
lib/private/util.php Переглянути файл

@@ -96,6 +96,29 @@ class OC_Util {
return ($enforcePassword === 'yes') ? true : false;
}

/**
* check if sharing is disabled for the current user
*
* @return boolean
*/
public static function isSharingDisabledForUser() {
if (\OC_Appconfig::getValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
$user = \OCP\User::getUser();
$groupsList = \OC_Appconfig::getValue('core', 'shareapi_exclude_groups_list', '');
$excludedGroups = explode(',', $groupsList);
$usersGroups = \OC_Group::getUserGroups($user);
if (!empty($usersGroups)) {
$remainingGroups = array_diff($usersGroups, $excludedGroups);
// if the user is only in groups which are disabled for sharing then
// sharing is also disabled for the user
if (empty($remainingGroups)) {
return true;
}
}
}
return false;
}

/**
* Get the quota of a user
* @param string $user

+ 9
- 0
lib/public/util.php Переглянути файл

@@ -116,6 +116,15 @@ class Util {
}
}

/**
* check if sharing is disabled for the current user
*
* @return boolean
*/
public static function isSharingDisabledForUser() {
return \OC_Util::isSharingDisabledForUser();
}

/**
* get l10n object
* @param string $application

+ 18
- 0
settings/admin.php Переглянути файл

@@ -10,6 +10,7 @@ OC_Util::checkAdminUser();
OC_Util::addStyle( "settings", "settings" );
OC_Util::addScript( "settings", "admin" );
OC_Util::addScript( "settings", "log" );
OC_Util::addScript( 'core', 'multiselect' );
OC_App::setActiveNavigationEntry( "admin" );

$tmpl = new OC_Template( 'settings', 'admin', 'user');
@@ -48,6 +49,23 @@ $tmpl->assign('shareAPIEnabled', OC_Appconfig::getValue('core', 'shareapi_enable
$tmpl->assign('shareDefaultExpireDateSet', OC_Appconfig::getValue('core', 'shareapi_default_expire_date', 'no'));
$tmpl->assign('shareExpireAfterNDays', OC_Appconfig::getValue('core', 'shareapi_expire_after_n_days', '7'));
$tmpl->assign('shareEnforceExpireDate', OC_Appconfig::getValue('core', 'shareapi_enforce_expire_date', 'no'));
$excludeGroups = OC_Appconfig::getValue('core', 'shareapi_exclude_groups', 'no') === 'yes' ? true : false;
$tmpl->assign('shareExcludeGroups', $excludeGroups);
$allGroups = OC_Group::getGroups();
$excludedGroupsList = OC_Appconfig::getValue('core', 'shareapi_exclude_groups_list', '');
$excludedGroups = $excludedGroupsList !== '' ? explode(',', $excludedGroupsList) : array();
$groups = array();
foreach ($allGroups as $group) {
if (in_array($group, $excludedGroups)) {
$groups[$group] = array('gid' => $group,
'excluded' => true);
} else {
$groups[$group] = array('gid' => $group,
'excluded' => false);
}
}
ksort($groups);
$tmpl->assign('groups', $groups);


// Check if connected using HTTPS

+ 18
- 0
settings/ajax/excludegroups.php Переглянути файл

@@ -0,0 +1,18 @@
<?php
OC_JSON::checkSubAdminUser();
OCP\JSON::callCheck();

$selectedGroups = isset($_POST["selectedGroups"]) ? json_decode($_POST["selectedGroups"]) : array();
$changedGroup = isset($_POST["changedGroup"]) ? $_POST["changedGroup"] : '';

if ($changedGroup !== '') {
if(($key = array_search($changedGroup, $selectedGroups)) !== false) {
unset($selectedGroups[$key]);
} else {
$selectedGroups[] = $changedGroup;
}
} else {
\OCP\Util::writeLog('core', 'Can not update list of excluded groups from sharing, parameter missing', \OCP\Util::WARN);
}

\OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', implode(',', $selectedGroups));

+ 9
- 0
settings/css/settings.css Переглянути файл

@@ -158,6 +158,15 @@ table.shareAPI .indent { padding-left: 2em; }
vertical-align: text-bottom;
}

#selectGroups select {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: inline-block;
height: 36px;
padding: 7px 10px
}

span.success {
background: #37ce02;
border-radius: 8px;

+ 49
- 0
settings/js/admin.js Переглянути файл

@@ -1,4 +1,49 @@
var SharingGroupList = {
applyMultipleSelect: function(element) {
var checked = [];
if ($(element).hasClass('groupsselect')) {
if (element.data('userGroups')) {
checked = element.data('userGroups');
}
var checkHandeler = function(group) {
$.post(OC.filePath('settings', 'ajax', 'excludegroups.php'),
{changedGroup: group, selectedGroups: JSON.stringify(checked)},
function() {});
};


var addGroup = function(select, group) {
$(this).each(function(index, element) {
if ($(element).find('option[value="' + group + '"]').length === 0 &&
select.data('msid') !== $(element).data('msid')) {
$(element).append('<option value="' + escapeHTML(group) + '">' +
escapeHTML(group) + '</option>');
}
});
};

var label = null;
element.multiSelect({
createCallback: addGroup,
createText: label,
selectedFirst: true,
checked: checked,
oncheck: checkHandeler,
onuncheck: checkHandeler,
minWidth: 100
});

}
}
};

$(document).ready(function(){

$('select#excludedGroups[multiple]').each(function (index, element) {
SharingGroupList.applyMultipleSelect($(element));
});


$('#loglevel').change(function(){
$.post(OC.filePath('settings','ajax','setloglevel.php'), { level: $(this).val() },function(){
OC.Log.reload();
@@ -84,4 +129,8 @@ $(document).ready(function(){
OC.msg.finishedAction('#sendtestmail_msg', data);
});
});

$('#shareapiExcludeGroups').change(function() {
$("#selectExcludedGroups").toggleClass('hidden', !this.checked);
});
});

+ 2
- 0
settings/routes.php Переглянути файл

@@ -84,3 +84,5 @@ $this->create('settings_admin_mail_test', '/settings/admin/mailtest')
->action('OC\Settings\Admin\Controller', 'sendTestMail');
$this->create('settings_ajax_setsecurity', '/settings/ajax/setsecurity.php')
->actionInclude('settings/ajax/setsecurity.php');
$this->create('settings_ajax_excludegroups', '/settings/ajax/excludegroups.php')
->actionInclude('settings/ajax/excludegroups.php');

+ 18
- 4
settings/templates/admin.php Переглянути файл

@@ -240,9 +240,6 @@ if (!$_['internetconnectionworking']) {

</div>
<em><?php p($l->t('Allow users to share items to the public with links')); ?></em>



</td>
</tr>
<tr>
@@ -271,7 +268,24 @@ if (!$_['internetconnectionworking']) {
<em><?php p($l->t('Allow users to send mail notification for shared files')); ?></em>
</td>
</tr>

<tr>
<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('class="hidden"');?>>
<input type="checkbox" name="shareapi_exclude_groups" id="shareapiExcludeGroups"
value="1" <?php if ($_['shareExcludeGroups']) print_unescaped('checked="checked"'); ?> />
<label for="shareapiExcludeGroups"><?php p($l->t('Exclude groups from sharing'));?></label><br/>
<div id="selectExcludedGroups" class="<?php ($_['shareExcludeGroups']) ? p('indent') : p('hidden indent'); ?>">
<select
class="groupsselect"
id="excludedGroups" data-placeholder="groups"
title="<?php p($l->t('Groups'))?>" multiple="multiple">
<?php foreach($_["groups"] as $group): ?>
<option value="<?php p($group['gid'])?>" <?php if($group['excluded']) { p('selected="selected"'); }?>><?php p($group['gid']);?></option>
<?php endforeach;?>
</select>
</div>
<em><?php p($l->t('These groups will still be able to receive shares, but not to initiate them.')); ?></em>
</td>
</tr>
</table>
</div>


+ 55
- 0
tests/lib/util.php Переглянути файл

@@ -235,4 +235,59 @@ class Test_Util extends PHPUnit_Framework_TestCase {
array(' .', false),
);
}

/**
* @dataProvider dataProviderForTestIsSharingDisabledForUser
* @param array $groups existing groups
* @param array $membership groups the user belong to
* @param array $excludedGroups groups which should be excluded from sharing
* @param bool $expected expected result
*/
function testIsSharingDisabledForUser($groups, $membership, $excludedGroups, $expected) {
$uid = "user1";
\OC_User::setUserId($uid);

\OC_User::createUser($uid, "passwd");

foreach($groups as $group) {
\OC_Group::createGroup($group);
}

foreach($membership as $group) {
\OC_Group::addToGroup($uid, $group);
}

$appConfig = \OC::$server->getAppConfig();
$appConfig->setValue('core', 'shareapi_exclude_groups_list', implode(',', $excludedGroups));
$appConfig->setValue('core', 'shareapi_exclude_groups', 'yes');

$result = \OCP\Util::isSharingDisabledForUser();

$this->assertSame($expected, $result);

// cleanup
\OC_User::deleteUser($uid);
\OC_User::setUserId('');

foreach($groups as $group) {
\OC_Group::deleteGroup($group);
}

$appConfig->setValue('core', 'shareapi_exclude_groups_list', '');
$appConfig->setValue('core', 'shareapi_exclude_groups', 'no');

}

public function dataProviderForTestIsSharingDisabledForUser() {
return array(
// existing groups, groups the user belong to, groups excluded from sharing, expected result
array(array('g1', 'g2', 'g3'), array(), array('g1'), false),
array(array('g1', 'g2', 'g3'), array(), array(), false),
array(array('g1', 'g2', 'g3'), array('g2'), array('g1'), false),
array(array('g1', 'g2', 'g3'), array('g2'), array(), false),
array(array('g1', 'g2', 'g3'), array('g1', 'g2'), array('g1'), false),
array(array('g1', 'g2', 'g3'), array('g1', 'g2'), array('g1', 'g2'), true),
array(array('g1', 'g2', 'g3'), array('g1', 'g2'), array('g1', 'g2', 'g3'), true),
);
}
}

Завантаження…
Відмінити
Зберегти