chg: [analyst-data:ACL] Enforced ACL and reflected the change in the UI

notes
Sami Mokaddem 2024-01-30 15:15:26 +01:00
parent b9f1a0ad89
commit b2f3602265
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
12 changed files with 171 additions and 46 deletions

View File

@ -35,6 +35,7 @@ class AnalystDataController extends AppController
$dropdownData['valid_targets'] = array_combine($this->AnalystData->valid_targets, $this->AnalystData->valid_targets);
$this->set(compact('dropdownData'));
$this->set('modelSelection', $this->modelSelection);
$this->set('distributionLevels', $this->Event->distributionLevels);
}
public function add($type = 'Note', $object_uuid = null, $object_type = null)
@ -48,7 +49,6 @@ class AnalystDataController extends AppController
}
if (empty($this->request->data[$this->modelSelection]['object_type']) && !empty($this->request->data[$this->modelSelection]['object_uuid'])) {
// Target uuid set, but no type provided, time to figure it out...
$this->request->data[$this->modelSelection]['object_type'] = $this->AnalystData->deduceType($object_uuid);
}
$params = [];
@ -68,7 +68,15 @@ class AnalystDataController extends AppController
{
$this->__typeSelector($type);
$this->set('id', $id);
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
$params = [
'conditions' => $conditions,
'afterFind' => function(array $analystData) {
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
if (!$canEdit) {
throw new MethodNotAllowedException(__('You are not authorised to do that.'));
}
}
];
$this->CRUD->edit($id, $params);
if ($this->IndexFilter->isRest()) {
@ -85,7 +93,15 @@ class AnalystDataController extends AppController
public function delete($type = 'Note', $id)
{
$this->__typeSelector($type);
$this->CRUD->delete($id);
$params = [
'afterFind' => function(array $analystData) {
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
if (!$canEdit) {
throw new MethodNotAllowedException(__('You are not authorised to do that.'));
}
}
];
$this->CRUD->delete($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
@ -95,8 +111,18 @@ class AnalystDataController extends AppController
public function view($type = 'Note', $id)
{
$this->__typeSelector($type);
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'view'));
$this->CRUD->view($id);
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
$this->CRUD->view($id, [
'conditions' => $conditions,
'afterFind' => function(array $analystData) {
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
if (!$this->IndexFilter->isRest()) {
$analystData[$this->modelSelection]['_canEdit'] = $canEdit;
}
return $analystData;
}
]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
@ -105,22 +131,35 @@ class AnalystDataController extends AppController
$this->_setViewElements();
$this->set('distributionLevels', $this->Event->distributionLevels);
$this->set('shortDist', $this->Event->shortDist);
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'view'));
$this->render('view');
}
public function index($type = 'Note')
{
$this->__typeSelector($type);
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'index'));
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
$params = [
'filters' => ['uuid', 'target_object', 'uuid'],
'quickFilters' => ['name']
'quickFilters' => ['name'],
'conditions' => $conditions,
'afterFind' => function(array $data) {
foreach ($data as $i => $analystData) {
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
if (!$this->IndexFilter->isRest()) {
$data[$i][$this->modelSelection]['_canEdit'] = $canEdit;
}
}
return $data;
}
];
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->_setViewElements();
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'index'));
}
public function getRelatedElement($type, $uuid)

View File

@ -1100,6 +1100,27 @@ class ACLComponent extends Component
return $cluster['GalaxyCluster']['orgc_id'] == $user['org_id'];
}
/**
* Checks if user can modify given analyst data
*
* @param array $user
* @param array $analystData
* @return bool
*/
public function canEditAnalystData(array $user, array $analystData, $modelType): bool
{
if (!isset($analystData[$modelType])) {
throw new InvalidArgumentException('Passed object does not contain a(n) ' . $modelType);
}
if ($user['Role']['perm_site_admin']) {
return true;
}
if ($analystData[$modelType]['orgc_uuid'] == $user['Organisation']['uuid']) {
return true;
}
return false;
}
/**
* Checks if user can publish given galaxy cluster
*

View File

@ -13,6 +13,7 @@ class AnalystData extends AppModel
public $valid_targets = [
'Attribute',
'Event',
'EventReport',
'GalaxyCluster',
'Galaxy',
'Object',
@ -85,6 +86,8 @@ class AnalystData extends AppModel
$results[$i] = $this->rearrangeOrganisation($results[$i], $this->current_user);
$results[$i] = $this->rearrangeSharingGroup($results[$i], $this->current_user);
$results[$i][$this->alias]['_canEdit'] = $this->canEditAnalystData($this->current_user, $v, $this->alias);
if (!empty($results[$i][$this->alias]['uuid'])) {
$results[$i][$this->alias] = $this->fetchChildNotesAndOpinions($results[$i][$this->alias]);
}
@ -108,6 +111,52 @@ class AnalystData extends AppModel
return true;
}
/**
* Checks if user can modify given analyst data
*
* @param array $user
* @param array $analystData
* @return bool
*/
public function canEditAnalystData(array $user, array $analystData, $modelType): bool
{
if (!isset($analystData[$modelType])) {
throw new InvalidArgumentException('Passed object does not contain a(n) ' . $modelType);
}
if ($user['Role']['perm_site_admin']) {
return true;
}
if ($analystData[$modelType]['orgc_uuid'] == $user['Organisation']['uuid']) {
return true;
}
return false;
}
public function buildConditions(array $user): array
{
$conditions = [];
if (!$user['Role']['perm_site_admin']) {
$sgids = $this->SharingGroup->authorizedIds($user);
$alias = $this->alias;
$conditions['AND']['OR'] = [
"{$alias}.org_uuid" => $user['Organisation']['uuid'],
[
'AND' => [
"{$alias}.distribution >" => 0,
"{$alias}.distribution <" => 4
],
],
[
'AND' => [
"{$alias}.sharing_group_id" => $sgids,
"{$alias}.distribution" => 4
]
]
];
}
return $conditions;
}
protected function setUser()
{
if (empty($this->current_user)) {
@ -124,7 +173,7 @@ class AnalystData extends AppModel
if (!empty($analystData[$this->alias]['orgc_uuid'])) {
if (!isset($analystData['Organisation'])) {
$this->Organisation = ClassRegistry::init('Organisation');
$analystData[$this->alias]['Organisation'] = $this->Organisation->find('first', ['condition' => ['uuid' => $analystData[$this->alias]['orgc_uuid']]])['Organisation'];
$analystData[$this->alias]['Organisation'] = $this->Organisation->find('first', ['conditions' => ['uuid' => $analystData[$this->alias]['orgc_uuid']]])['Organisation'];
} else {
$analystData[$this->alias]['Organisation'] = $analystData['Organisation'];
}

View File

@ -20,7 +20,7 @@ class AnalystDataBehavior extends ModelBehavior
$conditions = [
'object_uuid' => $uuid
];
$type = $this->__current_type;
$type = $Model->current_type;
if (empty($user['Role']['perm_site_admin'])) {
// $this->SharingGroup = ClassRegistry::init('SharingGroup');
// $validSharingGroups = $this->SharingGroup->authorizedIds($user, true);

View File

@ -65,10 +65,6 @@ if ($modelSelection === 'Note') {
} else if ($modelSelection === 'Relationship') {
$fields = array_merge($fields,
[
[
'field' => 'relationship_type',
'class' => 'span4',
],
[
'field' => 'related_object_type',
'class' => 'span4',
@ -163,10 +159,10 @@ if (!$ajax) {
}
$(document).ready(function() {
$('#NoteDistribution').change(function() {
checkSharingGroup('Note');
$('#<?= h($modelSelection) ?>Distribution').change(function() {
checkSharingGroup('<?= h($modelSelection) ?>');
});
checkSharingGroup('Note');
checkSharingGroup('<?= h($modelSelection) ?>');
$('#RelationshipRelatedObjectType').change(function(e) {
if ($('#RelationshipRelatedObjectUuid').val().length == 36) {

View File

@ -5,6 +5,11 @@
'sort' => $modelSelection . '.id',
'data_path' => $modelSelection . '.id'
],
[
'name' => __('Org'),
'element' => 'org',
'data_path' => $modelSelection . '.Organisation'
],
[
'name' => __('UUID'),
'data_path' => $modelSelection . '.uuid'
@ -35,8 +40,11 @@
],
[
'name' => __('Distribution'),
'element' => 'distribution_levels',
'sort' => $modelSelection . '.distribution',
'data_path' => $modelSelection . '.distribution'
'class' => 'short',
'data_path' => $modelSelection . '.distribution',
'sg_path' => $modelSelection . '.SharingGroup',
]
];
@ -150,7 +158,10 @@
),
'onclick_params_data_path' => $modelSelection . '.id',
'title' => __('Edit %s', $modelSelection),
'icon' => 'edit'
'icon' => 'edit',
'complex_requirement' => function($item) use ($modelSelection) {
return !empty($item[$modelSelection]['_canEdit']);
}
],
[
'onclick' => sprintf(
@ -158,7 +169,10 @@
$baseurl
),
'onclick_params_data_path' => $modelSelection . '.id',
'icon' => 'trash'
'icon' => 'trash',
'complex_requirement' => function($item) use ($modelSelection) {
return !empty($item[$modelSelection]['_canEdit']);
}
]
]
]

View File

@ -5,8 +5,14 @@ $fields = [
'path' => $modelSelection . '.id'
],
[
'key' => __('UUID'),
'path' => $modelSelection . '.uuid'
'key' => 'UUID',
'path' => $modelSelection . '.uuid',
'class' => '',
'type' => 'uuid',
'object_type' => $modelSelection,
'notes_path' => $modelSelection . '.Note',
'opinions_path' => $modelSelection . '.Opinion',
'relationships_path' => $modelSelection . '.Relationship',
],
[
'key' => __('Note Type'),
@ -54,7 +60,7 @@ $fields = [
'path' => $modelSelection . '.distribution',
'event_id_path' => $modelSelection . '.id',
'disable_distribution_graph' => true,
'sg_path' => $modelSelection . '.sharing_group_id',
'sg_path' => $modelSelection . '.SharingGroup',
'type' => 'distribution'
],
];
@ -78,7 +84,6 @@ if ($modelSelection === 'Note') {
'path' => $modelSelection . '.opinion',
'type' => 'opinion_scale',
];
} else if ($modelSelection === 'Relationship') {
$fields[] = [
'key' => __('Related Object'),
@ -122,20 +127,15 @@ $options = [
'object_type' => $modelSelection,
'object_uuid' => $object_uuid,
'shortDist' => $shortDist,
'notes' => $data[$modelSelection]['Note'] ?? [],
'opinions' => $data[$modelSelection]['Opinion'] ?? [],
'relationships' => $data[$modelSelection]['Relationship'] ?? [],
];
if ($modelSelection == 'Note') {
$options['notes'] = [$data[$modelSelection]];
} else if ($modelSelection == 'Opinion') {
$options['opinions'] = [$data[$modelSelection]];
} else if ($modelSelection == 'Relationship') {
$options['relationships'] = [$data[$modelSelection]];
}
echo $this->element('genericElements/Analyst_data/thread', $options);
?>
<?php if ($modelSelection == 'Relationship'): ?>
<?php if ($modelSelection == 'Relationship') : ?>
<script>
$(document).ready(function() {
$('#analyst_data_thread').find('li > a[href^="#relationship"]').tab('show')

View File

@ -50,11 +50,7 @@ function renderNote(note, relationship_related_object) {
note.distribution_text = note.distribution != 4 ? shortDist[note.distribution] : note.SharingGroup.name
note.distribution_color = note.distribution == 0 ? '#ff0000' : (note.distribution == 4 ? '#0088cc' : '#000')
note.authors = Array.isArray(note.authors) ? note.authors.join(', ') : note.authors;
note._permissions = {
can_edit: true,
can_delete: true,
can_add: true,
}
if (note.note_type == 0) { // analyst note
note.content = analystTemplate(note)
} else if (note.note_type == 1) { // opinion
@ -185,16 +181,16 @@ var baseNoteTemplate = doT.template('\
{{?}} \
</span> \
<span class="action-button-container" style="margin-left: auto; display: flex; gap: 0.2rem;"> \
{{? it._permissions.can_add }} \
{{? 1 == <?= $me['Role']['perm_modify'] ? 1 : 0 ?> }} \
<span role="button" onclick="addOpinion(this, \'{{=it.uuid}}\', \'{{=it.note_type_name}}\')" title="<?= __('Add an opinion to this note') ?>"><i class="<?= $this->FontAwesome->getClass('gavel') ?> useCursorPointer"></i></span> \
{{?}} \
{{? it._permissions.can_add }} \
{{? 1 == <?= $me['Role']['perm_modify'] ? 1 : 0 ?> }} \
<span role="button" onclick="addNote(this, \'{{=it.uuid}}\', \'{{=it.note_type_name}}\')" title="<?= __('Add a note to this note') ?>"><i class="<?= $this->FontAwesome->getClass('comment-alt') ?> useCursorPointer"></i></span> \
{{?}} \
{{? it._permissions.can_edit }} \
{{? it._canEdit }} \
<span role="button" onclick="editNote(this, {{=it.id}}, \'{{=it.note_type_name}}\')" title="<?= __('Edit this note') ?>"><i class="<?= $this->FontAwesome->getClass('edit') ?> useCursorPointer"></i></span> \
{{?}} \
{{? it._permissions.can_delete }} \
{{? it._canEdit }} \
<span role="button" onclick="deleteNote(this, {{=it.id}})" title="<?= __('Delete this note') ?>" href="<?= $baseurl . $URL_DELETE ?>{{=it.note_type_name}}/{{=it.id}}"><i class="<?= $this->FontAwesome->getClass('trash') ?> useCursorPointer"></i></span> \
{{?}} \
</span> \

View File

@ -7,6 +7,9 @@ if ($quickedit) {
}
$distributionLevel = Hash::extract($row, $field['data_path'])[0];
if ($distributionLevel == 4) {
$sg = empty($field['sg_path']) ? $row['SharingGroup'] : Hash::extract($row, $field['sg_path']);
}
echo sprintf('<div%s>', $quickedit ? sprintf(
" onmouseenter=\"quickEditHover(this, '%s', %s, 'distribution');\"",
@ -25,8 +28,8 @@ echo sprintf(
sprintf(
'<a href="%s/sharing_groups/view/%s">%s</a>',
$baseurl,
h($row['SharingGroup']['id']),
h($row['SharingGroup']['name'])
h($sg['id']),
h($sg['name'])
)
);
if ($quickedit) {

View File

@ -1808,7 +1808,7 @@ $divider = '<li class="divider"></li>';
'url' => '/analystData/index',
'text' => __('List Analyst Data')
));
if ($this->Acl->canAccess('analyst_notes', 'add')) {
if ($this->Acl->canAccess('analyst_data', 'add')) {
echo $divider;
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'add_note',

View File

@ -4,7 +4,7 @@
'<span class="quickSelect">%s</span>',
h($uuid)
);
if (!empty($field['object_type'])) {
$field['notes_path'] = !empty($field['notes_path']) ? $field['notes_path'] : 'Note';
$field['opinions_path'] = !empty($field['opinions_path']) ? $field['opinions_path'] : 'Opinion';
@ -17,6 +17,4 @@
'object_uuid' => $uuid,
'object_type' => $field['object_type']
]);
} else {
debug('Provide object type to access notes for that object');
}

View File

@ -115,4 +115,13 @@ class AclHelper extends Helper
{
return $this->ACL->canModifyGalaxyCluster($this->me, $cluster);
}
/**
* @param array $cluster
* @return bool
*/
public function canEditAnalystData(array $analystData, $modelType): bool
{
return $this->ACL->canEditAnalystData($this->me, $analystData, $modelType);
}
}