mirror of https://github.com/MISP/MISP
chg: [analyst-data:ACL] Enforced ACL and reflected the change in the UI
parent
b9f1a0ad89
commit
b2f3602265
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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'];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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> \
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue