Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Ioannis/comanage-grouper-widget
base: master
Choose a base ref
...
head repository: internet2/comanage-grouper-widget
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.
Showing with 9,582 additions and 13,190 deletions.
  1. 0 Config/Schema/empty
  2. +37 −6 Config/Schema/schema.xml
  3. +292 −0 Controller/ActAsPeopleController.php
  4. +30 −7 Controller/{CoGrouperLitesController.php → CoGrouperLiteWidgetsController.php}
  5. +586 −383 Controller/GrouperGroupsController.php
  6. +0 −7 Controller/GrouperLiteAppController.php
  7. +7 −0 Controller/GrouperLiteWidgetAppController.php
  8. 0 GrouperWidget/Config/Schema/empty
  9. 0 GrouperWidget/Console/Command/Task/empty
  10. 0 GrouperWidget/Console/Templates/empty
  11. +0 −71 GrouperWidget/Controller/CoGrouperWidgetController.php
  12. 0 GrouperWidget/Controller/Component/empty
  13. +0 −7 GrouperWidget/Controller/GrouperWidgetAppController.php
  14. 0 GrouperWidget/Lib/empty
  15. 0 GrouperWidget/Locale/eng/LC_MESSAGES/empty
  16. 0 GrouperWidget/Model/Behavior/empty
  17. +0 −9 GrouperWidget/Model/CoGrouperWidget.php
  18. 0 GrouperWidget/Model/Datasource/empty
  19. +0 −11 GrouperWidget/Model/GrouperWidget.php
  20. +0 −7 GrouperWidget/Model/GrouperWidgetAppModel.php
  21. 0 GrouperWidget/Test/Case/Controller/Component/empty
  22. 0 GrouperWidget/Test/Case/Lib/empty
  23. 0 GrouperWidget/Test/Case/Model/Behavior/empty
  24. 0 GrouperWidget/Test/Case/Model/Datasource/empty
  25. 0 GrouperWidget/Test/Case/View/Helper/empty
  26. 0 GrouperWidget/Test/Fixture/empty
  27. 0 GrouperWidget/View/Elements/empty
  28. 0 GrouperWidget/View/Helper/empty
  29. 0 GrouperWidget/View/Layouts/empty
  30. 0 GrouperWidget/webroot/css/empty
  31. 0 GrouperWidget/webroot/img/empty
  32. 0 GrouperWidget/webroot/js/empty
  33. +494 −411 Lib/GrouperApiAccess.php
  34. +134 −49 Lib/GrouperHTTPWrapper.php
  35. +2 −2 Lib/{GrouperLiteException.php → GrouperLiteWidgetException.php}
  36. 0 Lib/empty
  37. +66 −0 Lib/enum.php
  38. +68 −16 Lib/lang.php
  39. +44 −42 Model/{CoGrouperLite.php → ActAsPerson.php}
  40. +159 −0 Model/CoGrouperLiteWidget.php
  41. +1 −1 Model/CoManagePerson.php
  42. +6 −6 Model/GrouperAttribute.php
  43. +650 −397 Model/GrouperGroup.php
  44. +12 −19 Model/{GrouperLite.php → GrouperLiteWidget.php}
  45. +1 −1 Model/{GrouperLiteAppModel.php → GrouperLiteWidgetAppModel.php}
  46. +10 −12 README.md
  47. +16 −0 Test/HttpRequests/grouper.http
  48. +6 −0 Test/HttpRequests/http-client.env.json
  49. +136 −0 View/CoGrouperLiteWidgets/display.ctp
  50. 0 View/{CoGrouperLites → CoGrouperLiteWidgets}/edit.ctp
  51. +359 −0 View/CoGrouperLiteWidgets/fields.inc
  52. +118 −0 View/CoGrouperLiteWidgets/view.ctp
  53. +0 −134 View/CoGrouperLites/display.ctp
  54. +0 −209 View/CoGrouperLites/fields.inc
  55. +183 −0 View/Elements/ActAsPeopleAutocomplete.ctp
  56. +186 −0 View/Elements/ActionItem.ctp
  57. +42 −0 View/Elements/ActionSideBar.ctp
  58. +0 −27 View/Elements/Components/groupattributes.ctp
  59. +0 −42 View/Elements/Components/groupproperties.ctp
  60. +0 −68 View/Elements/Components/navigation-emaillists.ctp
  61. +0 −68 View/Elements/Components/navigation-groups.ctp
  62. +0 −10 View/Elements/Components/optAction.ctp
  63. +0 −49 View/Elements/Components/search.ctp
  64. +0 −11 View/Elements/Components/subscriberList.ctp
  65. +0 −57 View/Elements/base-styles.ctp
  66. 0 View/Elements/empty
  67. +0 −141 View/Elements/pagination.ctp
  68. +35 −13 View/GrouperGroups/base.ctp
  69. +0 −70 View/GrouperGroups/emaillistinfo.ctp
  70. +0 −48 View/GrouperGroups/emaillistsmanage.ctp
  71. +0 −38 View/GrouperGroups/emaillistsmember.ctp
  72. +0 −37 View/GrouperGroups/emaillistsoptin.ctp
  73. +0 −35 View/GrouperGroups/groupcreate.ctp
  74. +0 −30 View/GrouperGroups/groupcreatetemplate.ctp
  75. +0 −210 View/GrouperGroups/groupfields.inc
  76. +0 −71 View/GrouperGroups/groupinfo.ctp
  77. +0 −55 View/GrouperGroups/groupmember.ctp
  78. +0 −55 View/GrouperGroups/groupoptin.ctp
  79. +0 −62 View/GrouperGroups/groupowner.ctp
  80. +177 −0 View/GrouperGroups/index.ctp
  81. +0 −2 View/GrouperGroups/joingroup.ctp
  82. +0 −2 View/GrouperGroups/leavegroup.ctp
  83. +0 −249 View/GrouperGroups/templatefields.inc
  84. +0 −344 View/GrouperGroups/users.json
  85. +0 −27 View/Layouts/Emails/html/default.ctp
  86. +0 −19 View/Layouts/Emails/text/default.ctp
  87. 0 View/Layouts/empty
  88. +3 −3 View/Layouts/error.ctp
  89. +0 −13 View/Layouts/rss/default.ctp
  90. +0 −1 View/Layouts/xml/default.ctp
  91. +144 −9 webroot/css/co-grouper-base.css
  92. +100 −33 webroot/css/co-grouper-plugin.css
  93. 0 webroot/css/empty
  94. +207 −0 webroot/files/groupmember.json
  95. +14 −0 webroot/files/groupoptin.json
  96. +26 −0 webroot/files/groupowner.json
  97. +21 −0 webroot/js/autocomplete.grouperplugin.js
  98. +139 −0 webroot/js/autocomplete.js
  99. +0 −7,031 webroot/js/bootstrap.bundle.js
  100. +26 −0 webroot/js/collapse.js
  101. +15 −0 webroot/js/dashboard.js
  102. 0 webroot/js/empty
  103. +75 −0 webroot/js/grouper-groups-view.js
  104. +162 −0 webroot/js/groups-table.js
  105. +130 −0 webroot/js/groups.js
  106. +0 −2 webroot/js/jquery-3.5.1.min.js
  107. +22 −0 webroot/js/loader.js
  108. +210 −0 webroot/js/members.js
  109. +73 −0 webroot/js/nested-table.js
  110. +78 −0 webroot/js/page/GroupMember.js
  111. +30 −0 webroot/js/page/GroupOptin.js
  112. +38 −0 webroot/js/page/GroupOwner.js
  113. +111 −0 webroot/js/page/UserManager.js
  114. +33 −0 webroot/js/pagecount.js
  115. +126 −0 webroot/js/pagination.js
  116. +24 −0 webroot/js/params.js
  117. +9 −0 webroot/js/popover.js
  118. +39 −0 webroot/js/table.js
  119. +18 −0 webroot/js/tabs/tab.js
  120. +46 −0 webroot/js/tabs/tabs.js
  121. +0 −2,451 webroot/js/typeahead.bundle.js
  122. +3,806 −0 webroot/js/vue-router.js
Empty file removed Config/Schema/empty
Empty file.
43 changes: 37 additions & 6 deletions Config/Schema/schema.xml
@@ -25,24 +25,55 @@
must be specified in raw SQL, which needs the prefixed table name.
-->
<schema version="0.3">
<table name="co_grouper_lites">
<table name="co_grouper_lite_widgets">
<field name="id" type="I">
<key />
<autoincrement />
</field>
<field name="co_dashboard_widget_id" type="I">
<constraint>REFERENCES cm_co_dashboard_widgets(id)</constraint>
</field>
<field name="connUrl" type="C" size="256" />
<field name="connVer" type="C" size="256" />
<field name="connUser" type="C" size="64" />
<field name="connPass" type="C" size="64" />
<field name="conn_url" type="C" size="256" />
<field name="identifier_type" type="C" size="32" />
<field name="conn_ver" type="C" size="256" />
<field name="grouper_url" type="C" size="256" />
<field name="act_as_grp_name" type="C" size="512" />
<field name="conn_user" type="C" size="64" />
<field name="conn_pass" type="C" size="64" />
<field name="adhoc_heading" type="C" size="64" />
<field name="wg_heading" type="C" size="64" />
<field name="default_collapse" type="C" size="64" />
<field name="created" type="T" />
<field name="modified" type="T" />

<index name="co_grouper_lites_i1">
<index name="co_grouper_lite_widgets_i1">
<col>co_dashboard_widget_id</col>
<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
@@ -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;
}
}