Skip to content

act_as_feature #18

Merged
merged 21 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Config/Schema/schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,29 @@
<unique />
</index>
</table>

<table name="act_as_people">
<field name="id" type="I">
<key />
<autoincrement />
</field>
<field name="co_grouper_lite_widget_id" type="I">
<constraint>REFERENCES cm_co_grouper_lite_widgets(id)</constraint>
</field>
<field name="act_as_co_person_id" type="I">
<constraint>REFERENCES cm_co_people(id)</constraint>
</field>
<field name="co_person_id" type="I">
<constraint>REFERENCES cm_co_people(id)</constraint>
</field>
<field name="created" type="T" />
<field name="modified" type="T" />

<index name="grouper_lite_act_as_people_i1">
<col>co_grouper_lite_widget_id</col>
</index>
<index name="grouper_lite_act_as_people_i2">
<col>co_person_id</col>
</index>
</table>
</schema>
292 changes: 292 additions & 0 deletions Controller/ActAsPeopleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
<?php

App::uses('Validator', 'Vendor/cakephp/Validation');
App::uses('CoGrouperLite', 'GrouperLiteWidget.Model/');
App::uses('ActAsPerson', 'GrouperLiteWidget.Model/');
App::uses('GrouperGroup', 'GrouperLiteWidget.Model/');


App::uses('Identifier', 'Model');

class ActAsPeopleController extends GrouperLiteWidgetAppController
{
public $helpers = ['Html', 'Form', 'Flash'];

// Dynamic properties are deprecated, so we will define the property here
private $userId;

public $requires_person = true;

public $uses = [
'GrouperLiteWidget.ActAsPerson',
'GrouperLiteWidget.CoGrouperLiteWidget',
'GrouperLiteWidget.GrouperGroup',
'Identifier',
'CoPerson'
];

public $components = [
'Flash',
'RequestHandler',
'Security' => [
'validatePost' => false,
'csrfUseOnce' => false
]
];


public $name = 'ActAsPeople';

/**
* Overrides parent beforeFilter to verify that Session contains the correct API settings.
*
* @return void
*/
public function beforeFilter()
{
parent::beforeFilter();

if(empty($this->request->params['named']['glid'])) {
throw new InvalidArgumentException(_txt('er.grouperlite.glid'),
HttpStatusCodesEnum::HTTP_BAD_REQUEST);
}
$this->response->disableCache();
$this->RequestHandler->addInputType('json', ['json_decode', true]);

$this->Security->unlockedActions = [
'delete',
'upsert',
];

// Get the config
$args = array();
$args['conditions']['CoGrouperLiteWidget.id'] = $this->request->params['named']['glid'];
$args['contain'] = false;
$cfg = $this->CoGrouperLiteWidget->find('first', $args);
// Set the config so that everybody can access it
$this->CoGrouperLiteWidget->setConfig($cfg);
}

/**
* Update or Insert an ActAs user
*
* @since COmanage Registry v4.4.0
*/

public function upsert(): void
{
$this->request->allowMethod('ajax');
$this->layout = 'ajax';

if(!$this->request->is('restful')
&& !$this->request->is('ajax')) {
throw new RuntimeException('HTTP Method Not Allowed', HttpStatusCodesEnum::HTTP_METHOD_NOT_ALLOWED);
}

$data = $this->request->data;

$fullName = $data['fullname'];
unset($data['fullname']);

$args = [];
$args['conditions']['ActAsPerson.co_person_id'] = $data['co_person_id'];
if(!empty($this->request->named['actrecordId'])) {
$args['conditions']['ActAsPerson.id'] = $this->request->named['actrecordId'];
}
$args['conditions']['ActAsPerson.act_as_co_person_id'] = $data['act_as_co_person_id'];
$args['contain'] = false;
if(!empty($this->ActAsPerson->find('first', $args))) {
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_CONFLICT, 'Already Exists');
$error = ErrorsEnum::Conflict;
$this->set(compact('error'));
$this->set('_serialize', 'error');
return;
}

// We will update
if(!empty($this->request->named['actrecordId'])) {
$this->ActAsPerson->id = $this->request->named['actrecordId'];
}

try {
$ret = $this->ActAsPerson->save($data);
}
catch(Exception $e) {
$err = filter_var($e->getMessage(),FILTER_SANITIZE_SPECIAL_CHARS);
$this->Flash->set($err, ['key' => 'error']);
}

if($ret) {
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_CREATED, 'Added');
// I am registering a flash message. It will render after the frontend triggers a reload
$this->Flash->set("Act As {$fullName} Enabled" , ['key' => 'success']);

$args = [];
$args['conditions']['ActAsPerson.id'] = $this->ActAsPerson->id;
$args['contain'] = false;
$data = $this->ActAsPerson->find('first', $args);

$resp = [
'ActAsPerson' => $data['ActAsPerson']
];
$this->set(compact('resp'));
$this->set('_serialize', 'resp');
} else {
$fs = $this->ActAsPerson->invalidFields();

if(!empty($fs)) {
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_BAD_REQUEST, 'Invalid Fields');
$this->Flash->set('Invalid Fields', ['key' => 'error']);
$this->set(compact('fs'));
$this->set('_serialize', 'fs');
} elseif ($e
&& isset(_txt('en.http.status.codes')[$e->getCode()]) ) {
$this->Api->restResultHeader($e->getCode(), _txt('en.http.status.codes')[$e->getCode()]);
if(!empty($e->getMessage())) {
$vv_error = $e->getMessage();
$this->set(compact('vv_error'));
$this->set('_serialize', 'vv_error');
$this->Flash->set($e->getMessage(), ['key' => 'error']);
}
} else {
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_INTERNAL_SERVER_ERROR, 'Other Error');
$error = ErrorsEnum::Error;
$this->set(compact('error'));
$this->set('_serialize', 'error');
}
}
}

/**
* Handle an Act As Person Delete request.
*
* @since COmanage Registry v4.4.0
*/

public function delete():void
{
$this->request->allowMethod('ajax');
$this->layout = 'ajax';

if(!$this->request->is('delete')
&& !$this->request->is('ajax')
) {
throw new RuntimeException('HTTP Method Not Allowed', HttpStatusCodesEnum::HTTP_METHOD_NOT_ALLOWED);
}

if(empty($this->request->named['copersonid'])
|| empty($this->request->named['act_as_copersonid'])) {
$this->log(__METHOD__ . '::message: Named Parameter missing', LOG_ERROR);
throw new BadRequestException('Named Parameter missing');
}

try {
$conditions = [
'ActAsPerson.co_person_id' => $this->request->named['copersonid'],
'ActAsPerson.act_as_co_person_id' => $this->request->named['act_as_copersonid']
];

$ret = $this->ActAsPerson->deleteAll($conditions, true, true);
} catch(Exception $e) {
$error = filter_var($e->getMessage(),FILTER_SANITIZE_SPECIAL_CHARS);
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_INTERNAL_SERVER_ERROR, 'Other Error');
$this->set(compact('error'));
$this->set('_serialize', 'error');
return;
}

// Set flash message
if(!$ret) {
$error = ErrorsEnum::NotDeleted;
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_BAD_REQUEST, 'Bad Request');
$this->set(compact('error'));
$this->set('_serialize', 'error');
return;
}

// We will register a Flash message here. The frontend will reload and it will be rendered
$this->Flash->set('Act As Disabled', ['key' => 'success']);

// We do not redirect but we pass data back to the frontend because we
// want to keep the view structure as simple as possible. If we redirect we will
// have to add more JSON view infrastructure. We will let the frontend
// handle the redirect
$this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_OK, 'Success');
$success = ['status' => 'Success', 'message' => 'Succeeded'];
$this->set(compact('success'));
$this->set('_serialize', 'success');
}


/**
* NOTE: All permissions will be done on the Grouper side. All Authenticated users will be able to
* use this plugin for self-admin of groups.
*
* Authorization for this Controller, called by Auth component
* - precondition: Session.Auth holds data used for authz decisions
* - postcondition: $permissions set with calculated permissions
*
* @return array|bool Permissions
* @since COmanage Registry v4.4.0
*/
public function isAuthorized(): array|bool
{
$roles = $this->Role->calculateCMRoles();
$this->set('roles', $roles);

$pids = $this->parsePersonID($this->request->data);

$cfg = $this->CoGrouperLiteWidget->getConfig();
// Find the identifier
$args = array();
$args['conditions']['Identifier.type'] = $cfg['CoGrouperLiteWidget']['identifier_type'];
$args['conditions']['Identifier.status'] = SuspendableStatusEnum::Active;
$args['conditions']['Identifier.co_person_id'] = !empty($roles['copersonid']) ? $roles['copersonid'] : $pids['copersonid'];
$args['contain'] = false;

$identifiers = $this->Identifier->find('first', $args);
if(!empty($identifiers)
&& is_array($identifiers)
&& isset($identifiers['Identifier']['identifier'])
) {
$this->setUserId($identifiers['Identifier']['identifier']);
}

// Find if the user belongs to Group
$eligibleGroup = $cfg['CoGrouperLiteWidget']['act_as_grp_name'];
$isActAsEligibilityGroupmember = false;

if(!empty($eligibleGroup)) {
$isActAsEligibilityGroupmember = $this->GrouperGroup
->isGroupMember($this->getUserId(), $eligibleGroup, $cfg);
}

// Determine what operations this user can perform
// Construct the permission set for this user, which will also be passed to the view.
$p = [];

$p['delete'] = $isActAsEligibilityGroupmember;
$p['upsert'] = $isActAsEligibilityGroupmember;

$this->set('permissions', $p);

return ($p[$this->action]);
}

/**
* @return null
*/
public function getUserId()
{
return $this->userId;
}


/**
* @param null $userId
*/
private function setUserId($userId): void
{
$this->userId = $userId;
}
}
2 changes: 1 addition & 1 deletion Controller/CoGrouperLiteWidgetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function display($id) {
$cfg = $this->CoGrouperLiteWidget->getConfig();

$this->set('pl_grouperlite_index_url', Router::url([
'plugin' => "grouper_lite",
'plugin' => 'grouper_lite',
'controller' => 'grouper_groups',
'action' => 'index',
'co' => $this->cur_co['Co']['id'],
Expand Down
Loading