sharing ACL: need explicit new ACL or edit rights, to share into an addressbook

This commit is contained in:
Ralf Becker 2020-10-15 13:03:14 +02:00
parent 71d185a019
commit 9d776189b0
6 changed files with 132 additions and 2 deletions

View File

@ -484,6 +484,7 @@ class addressbook_hooks
Acl::EDIT => 'edit', Acl::EDIT => 'edit',
Acl::ADD => 'add', Acl::ADD => 'add',
Acl::DELETE => 'delete', Acl::DELETE => 'delete',
Acl::CUSTOM1 => 'shared with', // allows to share into given AB
); );
} }

View File

@ -2093,6 +2093,8 @@ class addressbook_ui extends addressbook_bo
]; ];
} }
unset($content['shared_values']); unset($content['shared_values']);
// remove invalid shared-with entries (should not happen, as we validate already on client-side)
$this->check_shared_with($content['shared']);
$button = @key($content['button']); $button = @key($content['button']);
unset($content['button']); unset($content['button']);
@ -2440,6 +2442,8 @@ class addressbook_ui extends addressbook_bo
]; ];
} }
$content['shared_values'] = array_keys($content['shared_options']); $content['shared_values'] = array_keys($content['shared_options']);
// disable shared with UI for non-SQL backends
$content['shared_disabled'] = !is_a($this->get_backend($content['id'], $content['owner']), Api\Contacts\Sql::class);
if ($content['id']) if ($content['id'])
{ {
@ -2643,6 +2647,46 @@ class addressbook_ui extends addressbook_bo
return $this->tmpl->exec('addressbook.addressbook_ui.edit', $content, $sel_options, $readonlys, $preserve, 2); return $this->tmpl->exec('addressbook.addressbook_ui.edit', $content, $sel_options, $readonlys, $preserve, 2);
} }
/**
* Check if user has right to share with / into given AB
*
* @param array $_data values for keys "shared_writable" and "shared_values"
* @return array of entries removed from $shared_with because current user is not allowed to share into
*/
public function ajax_check_shared(array $_data)
{
$response = Api\Json\Response::get();
try {
$shared = [];
foreach($_data['shared_values'] as $value)
{
if (is_numeric($value))
{
$shared[$value] = [
'shared_with' => $value,
'shared_by' => $this->user,
'shared_writable' => (int)$_data['shared_writable'],
];
}
else
{
$shared[$value] = array_combine(['shared_id', 'shared_with', 'shared_by', 'shared_writable'], explode(':', $value));
}
}
if (($failed = $this->check_shared_with($shared)))
{
$response->data(array_keys($failed));
$response->message(lang('You are not allowed to share into the addressbook of %1',
implode(', ', array_map(function ($data) {
return Api\Accounts::username($data['shared_with']);
}, $failed))), 'error');
}
}
catch (\Exception $e) {
$response->message($e->getMessage(), 'error');
}
}
/** /**
* Set the readonlys for non-admins editing their own account * Set the readonlys for non-admins editing their own account
* *

View File

@ -1224,6 +1224,27 @@ var AddressbookApp = /** @class */ (function (_super) {
app.status.makeCall(data); app.status.makeCall(data);
} }
}; };
/**
* Check if new shared_with value is allowed / user has rights to share into that AB
*
* Remove the entry again, if user is not allowed
*/
AddressbookApp.prototype.shared_changed = function () {
var _a;
var shared = this.et2.getInputWidgetById('shared_values');
var value = (_a = shared) === null || _a === void 0 ? void 0 : _a.get_value();
if (value) {
this.egw.json('addressbook.addressbook_ui.ajax_check_shared', [{
shared_values: value,
shared_writable: this.et2.getInputWidgetById('shared_writable').get_value()
}], function (_data) {
if (Array.isArray(_data) && _data.length) {
// remove not allowed entries
shared.set_value(value.filter(function (val) { return _data.indexOf(val) === -1; }));
}
}).sendRequest();
}
};
return AddressbookApp; return AddressbookApp;
}(egw_app_1.EgwApp)); }(egw_app_1.EgwApp));
app.classes.addressbook = AddressbookApp; app.classes.addressbook = AddressbookApp;

View File

@ -1479,6 +1479,30 @@ class AddressbookApp extends EgwApp
} }
} }
/**
* Check if new shared_with value is allowed / user has rights to share into that AB
*
* Remove the entry again, if user is not allowed
*/
public shared_changed()
{
let shared = this.et2.getInputWidgetById('shared_values');
let value = <Array<string>>shared?.get_value();
if (value)
{
this.egw.json('addressbook.addressbook_ui.ajax_check_shared', [{
shared_values: value,
shared_writable: this.et2.getInputWidgetById('shared_writable').get_value()
}], _data => {
if (Array.isArray(_data) && _data.length)
{
// remove not allowed entries
shared.set_value(value.filter(val => _data.indexOf(val) === -1));
}
}).sendRequest();
}
}
} }
app.classes.addressbook = AddressbookApp; app.classes.addressbook = AddressbookApp;

View File

@ -143,9 +143,10 @@
</hbox> </hbox>
<description/> <description/>
</row> </row>
<row> <row disabled="@shared_disabled">
<description for="shared" value="Shared with"/> <description for="shared" value="Shared with"/>
<taglist-account id="shared_values" multiple="true" select_options="@shared_options" span="4"/> <taglist-account account_type="both" id="shared_values" multiple="true"
onchange="app.addressbook.shared_changed" select_options="@shared_options" span="4"/>
<checkbox id="shared_writable" label="writable" statustext="Create new shares writable"/> <checkbox id="shared_writable" label="writable" statustext="Create new shares writable"/>
</row> </row>
</rows> </rows>

View File

@ -30,6 +30,17 @@ class Contacts extends Contacts\Storage
*/ */
const BIRTHDAY_CACHE_TIME = 864000; /* 10 days*/ const BIRTHDAY_CACHE_TIME = 864000; /* 10 days*/
/**
* Custom ACL allowing to share into the AB / setting shared_with
*/
const ACL_SHARED = Acl::CUSTOM1;
/**
* Mask to allow to share into the AB, at least one of the following need to be set:
* - custom ACL_SHARED
* - ACL::EDIT
*/
const CHECK_ACL_SHARED = Acl::EDIT|self::ACL_SHARED;
/** /**
* @var int $now_su actual user (!) time * @var int $now_su actual user (!) time
*/ */
@ -1250,6 +1261,34 @@ class Contacts extends Contacts\Storage
return $access; return $access;
} }
/**
* Check if user has right to share with / into given AB
*
* @param array[]& $shared_with array of arrays with values for keys "shared_with", "shared_by", ...
* @return array of entries removed from $shared_with because current user is not allowed to share into (key is preserved)
*/
function check_shared_with(array &$shared_with)
{
$removed = [];
foreach($shared_with as $key => $shared)
{
if (!empty($shared['shared_by']) && $shared['shared_by'] != $this->user)
{
$grants = $this->get_grants($user);
}
else
{
$grants = $this->grants;
}
if (!($grants[$shared['shared_with']] & self::CHECK_ACL_SHARED))
{
$removed[$key] = $shared;
unset($shared_with[$key]);
}
}
return $removed;
}
/** /**
* Check access to the file store * Check access to the file store
* *