chg: [alignments:acl] Reflected ACL logic from individuals to alignments
parent
367012af36
commit
8b4b47775c
|
@ -49,6 +49,9 @@ class AlignmentsController extends AppController
|
|||
throw new NotFoundException(__('Invalid alignment.'));
|
||||
}
|
||||
$alignment = $this->Alignments->get($id);
|
||||
if (!$this->canEditIndividual($alignment->individual_id) || !$this->canEditOrganisation($alignment->organisation_id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot delete this alignments.'));
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('delete')) {
|
||||
if ($this->Alignments->delete($alignment)) {
|
||||
$message = __('Alignments deleted.');
|
||||
|
@ -73,8 +76,21 @@ class AlignmentsController extends AppController
|
|||
if (empty($scope) || empty($source_id)) {
|
||||
throw new NotAcceptableException(__('Invalid input. scope and source_id expected as URL parameters in the format /alignments/add/[scope]/[source_id].'));
|
||||
}
|
||||
if (!in_array($scope, ['individuals', 'organisations'])) {
|
||||
throw new MethodNotAllowedException(__('Invalid scope. Should be `individuals` or `organisations`.'));
|
||||
}
|
||||
$this->loadModel('Individuals');
|
||||
$this->loadModel('Organisations');
|
||||
|
||||
$validIndividualIDs = $this->Individuals->getValidIndividualsToEdit($this->ACL->getUser());
|
||||
$validOrgs = $this->Organisations->getEditableOrganisationsForUser($this->ACL->getUser());
|
||||
|
||||
if ($scope == 'individuals' && !$this->canEditIndividual($source_id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that individual.'));
|
||||
} else if ($scope == 'organisations' && !$this->canEditOrganisation($source_id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that organisation.'));
|
||||
}
|
||||
|
||||
$alignment = $this->Alignments->newEmptyEntity();
|
||||
if ($this->request->is('post')) {
|
||||
$this->Alignments->patchEntity($alignment, $this->request->getData());
|
||||
|
@ -83,6 +99,11 @@ class AlignmentsController extends AppController
|
|||
} else {
|
||||
$alignment['organisation_id'] = $source_id;
|
||||
}
|
||||
if ($scope == 'individuals' && !$this->canEditOrganisation($alignment['organisation_id'])) {
|
||||
throw new MethodNotAllowedException(__('You cannot use that organisation.'));
|
||||
} else if ($scope == 'organisations' && !$this->canEditIndividual($alignment['individual_id'])) {
|
||||
throw new MethodNotAllowedException(__('You cannot assign that individual.'));
|
||||
}
|
||||
$alignment = $this->Alignments->save($alignment);
|
||||
if ($alignment) {
|
||||
$message = __('Alignment added.');
|
||||
|
@ -105,7 +126,7 @@ class AlignmentsController extends AppController
|
|||
}
|
||||
}
|
||||
if ($scope === 'organisations') {
|
||||
$individuals = $this->Individuals->find('list', ['valueField' => 'email'])->toArray();
|
||||
$individuals = $this->Individuals->find('list', ['valueField' => 'email'])->where(['id IN' => $validIndividualIDs])->toArray();
|
||||
$this->set('individuals', $individuals);
|
||||
$organisation = $this->Organisations->find()->where(['id' => $source_id])->first();
|
||||
if (empty($organisation)) {
|
||||
|
@ -113,7 +134,7 @@ class AlignmentsController extends AppController
|
|||
}
|
||||
$this->set(compact('organisation'));
|
||||
} else {
|
||||
$organisations = $this->Organisations->find('list', ['valueField' => 'name'])->toArray();
|
||||
$organisations = Hash::combine($validOrgs, '{n}.id', '{n}.name');
|
||||
$this->set('organisations', $organisations);
|
||||
$individual = $this->Individuals->find()->where(['id' => $source_id])->first();
|
||||
if (empty($individual)) {
|
||||
|
@ -124,6 +145,31 @@ class AlignmentsController extends AppController
|
|||
$this->set(compact('alignment'));
|
||||
$this->set('scope', $scope);
|
||||
$this->set('source_id', $source_id);
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
}
|
||||
|
||||
private function canEditIndividual($indId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
$this->loadModel('Individuals');
|
||||
$validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser);
|
||||
if (in_array($indId, $validIndividuals)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canEditOrganisation($orgId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ class ACLComponent extends Component
|
|||
'queryACL' => ['perm_admin']
|
||||
],
|
||||
'Alignments' => [
|
||||
'add' => ['perm_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'add' => ['perm_admin', 'perm_org_admin'],
|
||||
'delete' => ['perm_admin', 'perm_org_admin'],
|
||||
'index' => ['*'],
|
||||
'view' => ['*']
|
||||
],
|
||||
|
|
|
@ -80,4 +80,17 @@ class OrganisationsTable extends AppTable
|
|||
$this->saveMetaFields($id, $org);
|
||||
}
|
||||
}
|
||||
|
||||
public function getEditableOrganisationsForUser($user): array
|
||||
{
|
||||
$query = $this->find();
|
||||
if (empty($user['role']['perm_admin'])) {
|
||||
if (!empty($user['role']['perm_org_admin'])) {
|
||||
$query->where(['Organisations.id' => $user['organisation']['id']]);
|
||||
} else {
|
||||
return []; // User not an org_admin. Cannot edit orgs
|
||||
}
|
||||
}
|
||||
return $query->all()->toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,43 +4,56 @@ $alignments = '';
|
|||
$canRemove = $this->request->getParam('prefix') !== 'Open';
|
||||
if ($field['scope'] === 'individuals') {
|
||||
foreach ($raw_alignments as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div><span class="fw-bold">%s</span> @ %s <a href="#" class="fas fa-trash .text-reset .text-decoration-none" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
$canEdit = in_array($alignment->individual_id, $editableIds);
|
||||
$alignmentEntryHtml = $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type']));
|
||||
$alignmentEntryHtml .= ' @ ' . $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['organisation']['id']),
|
||||
h($alignment['organisation']['name'])
|
||||
),
|
||||
!$canRemove ? '' : sprintf(
|
||||
"UI.submissionModalForIndex(%s);",
|
||||
),);
|
||||
if ($canRemove && !empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'
|
||||
],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
h($alignment['id'])
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
} else if ($field['scope'] === 'organisations') {
|
||||
foreach ($raw_alignments as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div>[<span class="fw-bold">%s</span>] %s <a href="#" class="fas fa-trash .text-reset .text-decoration-none" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
'<a href="%s/individuals/view/%s">%s</a>',
|
||||
$canEdit = in_array($alignment->organisation_id, $editableIds);
|
||||
$alignmentEntryHtml = '[' . $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type'])) . ']';
|
||||
$alignmentEntryHtml .= $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['individual']['id']),
|
||||
h($alignment['individual']['email'])
|
||||
),
|
||||
!$canRemove ? '' : sprintf(
|
||||
"UI.submissionModalForIndex(%s);",
|
||||
),);
|
||||
if ($canRemove && !empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
h($alignment['id'])
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
}
|
||||
echo $alignments;
|
||||
|
|
|
@ -10,48 +10,59 @@ if (!empty($field['path'])) {
|
|||
}
|
||||
if ($field['scope'] === 'individuals') {
|
||||
foreach ($extracted['alignments'] as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div><span class="fw-bold">%s</span> @ %s <a href="#" class="fas fa-trash" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
$alignmentEntryHtml = $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type']));
|
||||
$alignmentEntryHtml .= ' @ ' . $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['organisation']['id']),
|
||||
h($alignment['organisation']['name'])
|
||||
),
|
||||
sprintf(
|
||||
),);
|
||||
if (!empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
} else if ($field['scope'] === 'organisations') {
|
||||
foreach ($extracted['alignments'] as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div>[<span class="fw-bold">%s</span>] %s <a href="#" class="fas fa-trash" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
'<a href="%s/individuals/view/%s">%s</a>',
|
||||
$alignmentEntryHtml = '[' . $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type'])) . ']';
|
||||
$alignmentEntryHtml .= $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['individual']['id']),
|
||||
h($alignment['individual']['email'])
|
||||
),
|
||||
sprintf(
|
||||
),);
|
||||
if (!empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
}
|
||||
echo sprintf('<div class="alignments-list">%s</div>', $alignments);
|
||||
if (!empty($canEdit)) {
|
||||
echo sprintf(
|
||||
'<div class="alignments-list">%s</div><div class="alignments-add-container"><button class="alignments-add-button btn btn-primary btn-sm" onclick="%s">%s</button></div>',
|
||||
$alignments,
|
||||
'<div class="alignments-add-container"><button class="alignments-add-button btn btn-primary btn-sm" onclick="%s">%s</button></div>',
|
||||
sprintf(
|
||||
"UI.submissionModalForSinglePage('/alignments/add/%s/%s');",
|
||||
h($field['scope']),
|
||||
|
@ -59,3 +70,4 @@ echo sprintf(
|
|||
),
|
||||
$field['scope'] === 'individuals' ? __('Add organisation') : __('Add individual')
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue