Account Suspended
Account Suspended
This Account has been suspended.
Contact your hosting provider for more information.
 All Data Structures Functions Variables Pages
ReadPermissionsOptimizationUtil.php
1 <?php
2  /*********************************************************************************
3  * Zurmo is a customer relationship management program developed by
4  * Zurmo, Inc. Copyright (C) 2017 Zurmo Inc.
5  *
6  * Zurmo is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  *
13  * Zurmo is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  *
23  * You can contact Zurmo, Inc. with a mailing address at 27 North Wacker Drive
24  * Suite 370 Chicago, IL 60606. or at email address contact@zurmo.com.
25  *
26  * The interactive user interfaces in original and modified versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  *
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the Zurmo
32  * logo and Zurmo copyright notice. If the display of the logo is not reasonably
33  * feasible for technical reasons, the Appropriate Legal Notices must display the words
34  * "Copyright Zurmo Inc. 2017. All rights reserved".
35  ********************************************************************************/
36 
38  {
46  public static function rebuild($overwriteExistingTables = true, $forcePhp = false, $messageStreamer = null)
47  {
48  //Forcing php way until we can fix failing tests here: AccountReadPermissionsOptimizationScenariosTest
49  $forcePhp = true;
50  assert('is_bool($overwriteExistingTables)');
51  assert('is_bool($forcePhp)');
52  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
53  {
54  $mungeTableName = self::getMungeTableName($modelClassName);
55  $readTableExists = ZurmoRedBean::$writer->doesTableExist($mungeTableName);
56  if (!$overwriteExistingTables && $readTableExists)
57  {
58  if (isset($messageStreamer))
59  {
60  $messageStreamer->add(Zurmo::t('ZurmoModule', "Skipping {{tableName}}",
61  array('{{tableName}}' => $mungeTableName)));
62  }
63  // skip if we don't want to overwrite existing tables and table already exists
64  continue;
65  }
66  if (isset($messageStreamer))
67  {
68  $messageStreamer->add(Zurmo::t('ZurmoModule', "Building {{tableName}}",
69  array('{{tableName}}' => $mungeTableName)));
70  }
71 
72  if (!SECURITY_OPTIMIZED || $forcePhp)
73  {
74  self::rebuildViaSlowWay($modelClassName);
75  }
76  else
77  {
78  //models that extend activity are special and can only be done with the PHP process. They cannot
79  //be done using the stored procedure because it does not support the extra joins needed to determine
80  //which securable items to look at.
81  if (is_subclass_of($modelClassName, 'Activity'))
82  {
83  self::rebuildViaSlowWay($modelClassName);
84  }
85  else
86  {
87  $modelTableName = $modelClassName::getTableName();
88  if (!is_subclass_of($modelClassName, 'OwnedSecurableItem'))
89  {
90  throw new NotImplementedException();
91  }
92  if (is_subclass_of($modelClassName, 'Person'))
93  {
94  if ($modelClassName != 'Contact')
95  {
96  throw new NotSupportedException();
97  }
98  else
99  {
100  $modelTableName = Person::getTableName();
101  }
102  }
104  callProcedureWithoutOuts("rebuild('$modelTableName', '$mungeTableName')");
105  }
106  }
107  }
108  }
109 
110  protected static function rebuildViaSlowWay($modelClassName)
111  {
112  // The slow way will remain here as documentation
113  // for what the optimized way is doing.
114  $mungeTableName = self::getMungeTableName($modelClassName);
115  self::recreateTable($mungeTableName);
116  //Specifically call RedBeanModel to avoid the use of the security in OwnedSecurableItem since for
117  //rebuild it needs to look at all models regardless of permissions of the current user.
118  $modelCount = RedBeanModel::getCount(null, null, $modelClassName);
119  $subset = intval($modelCount / 20);
120  if ($subset < 100)
121  {
122  $subset = 100;
123  }
124  elseif ($subset > 1000)
125  {
126  $subset = 1000;
127  }
128  $users = User::getAll();
129  $groups = Group::getAll();
130  $roles = Role::getAll();
131  for ($i = 0; $i < $modelCount; $i += $subset)
132  {
133  //Specifically call RedBeanModel to avoid the use of the security in OwnedSecurableItem since for
134  //rebuild it needs to look at all models regardless of permissions of the current user.
135  $models = RedBeanModel::getSubset(null, $i, $subset, null, null, $modelClassName);
136  foreach ($models as $model)
137  {
138  assert('$model instanceof SecurableItem');
139  $securableItemId = $model->getClassId('SecurableItem');
140  foreach ($users as $user)
141  {
142  list($allowPermissions, $denyPermissions) = $model->getExplicitActualPermissions($user);
143  $effectiveExplicitPermissions = $allowPermissions & ~$denyPermissions;
144  if (($effectiveExplicitPermissions & Permission::READ) == Permission::READ)
145  {
146  self::incrementCount($mungeTableName, $securableItemId, $user);
147  }
148  }
149 
150  foreach ($groups as $group)
151  {
152  list($allowPermissions, $denyPermissions) = $model->getExplicitActualPermissions($group);
153  $effectiveExplicitPermissions = $allowPermissions & ~$denyPermissions;
154  if (($effectiveExplicitPermissions & Permission::READ) == Permission::READ)
155  {
156  self::incrementCount($mungeTableName, $securableItemId, $group);
157  foreach ($group->users as $user)
158  {
159  if ($user->role->id > 0)
160  {
161  self::incrementParentRolesCounts($mungeTableName, $securableItemId, $user->role);
162  }
163  }
164  foreach ($group->groups as $subGroup)
165  {
166  self::processNestedGroupWhereParentHasReadPermissionOnSecurableItem(
167  $mungeTableName, $securableItemId, $subGroup);
168  }
169  }
170  }
171  foreach ($roles as $role)
172  {
173  $count = self::getRoleMungeCount($model, $role);
174  assert('$count >= 0');
175  if ($count > 0)
176  {
177  self::setCount($mungeTableName, $securableItemId, $role, $count);
178  }
179  }
180  }
181  }
182  }
183 
184  protected static function processNestedGroupWhereParentHasReadPermissionOnSecurableItem(
185  $mungeTableName, $securableItemId, Group $group)
186  {
187  assert('is_string($mungeTableName) && $mungeTableName != ""');
188  assert('is_int($securableItemId) && $securableItemId > 0');
189  self::incrementCount($mungeTableName, $securableItemId, $group);
190  foreach ($group->users as $user)
191  {
192  if ($user->role->id > 0)
193  {
194  self::incrementParentRolesCounts($mungeTableName, $securableItemId, $user->role);
195  }
196  }
197  foreach ($group->groups as $subGroup)
198  {
199  self::processNestedGroupWhereParentHasReadPermissionOnSecurableItem(
200  $mungeTableName, $securableItemId, $subGroup);
201  }
202  }
203 
204  protected static function getRoleMungeCount(SecurableItem $securableItem, Role $role)
205  {
206  $count = 0;
207  foreach ($role->roles as $subRole)
208  {
209  $count += self::getSubRoleMungeCount($securableItem, $subRole);
210  }
211  return $count;
212  }
213 
214  protected static function getSubRoleMungeCount(SecurableItem $securableItem, Role $role)
215  {
216  $count = self::getImmediateRoleMungeCount($securableItem, $role);
217  foreach ($role->roles as $subRole)
218  {
219  $count += self::getSubRoleMungeCount($securableItem, $subRole);
220  }
221  return $count;
222  }
223 
224  protected static function getImmediateRoleMungeCount(SecurableItem $securableItem, Role $role)
225  {
226  $count = 0;
227  foreach ($role->users as $user)
228  {
229  if ($securableItem->owner->isSame($user))
230  {
231  $count++;
232  }
233  list($allowPermissions, $denyPermissions) = $securableItem->getExplicitActualPermissions($user);
234  $effectiveExplicitPermissions = $allowPermissions & ~$denyPermissions;
235  if (($effectiveExplicitPermissions & Permission::READ) == Permission::READ)
236  {
237  $count++;
238  }
239  foreach ($user->groups as $group)
240  {
241  $count += self::getGroupMungeCount($securableItem, $group);
242  }
243  }
244  return $count;
245  }
246 
247  protected static function getGroupMungeCount(SecurableItem $securableItem, Group $group)
248  {
249  $count = 0;
250  list($allowPermissions, $denyPermissions) = $securableItem->getExplicitActualPermissions($group);
251  $effectiveExplicitPermissions = $allowPermissions & ~$denyPermissions;
252  if (($effectiveExplicitPermissions & Permission::READ) == Permission::READ)
253  {
254  $count++;
255  }
256  if ($group->group->id > 0 && !$group->group->isSame($group)) // Prevent cycles in database auto build.
257  {
258  $count += self::getGroupMungeCount($securableItem, $group->group);
259  }
260  return $count;
261  }
262 
263  // SecurableItem create, assigned, or deleted.
264 
265  // Past tense implies the method must be called immediately after the associated operation.
266  public static function ownedSecurableItemCreated(OwnedSecurableItem $ownedSecurableItem)
267  {
268  self::ownedSecurableItemOwnerChanged($ownedSecurableItem);
269  }
270 
275  public static function ownedSecurableItemOwnerChanged(OwnedSecurableItem $ownedSecurableItem, User $oldUser = null)
276  {
277  $modelClassName = get_class($ownedSecurableItem);
278  assert('$modelClassName != "OwnedSecurableItem"');
279  $mungeTableName = self::getMungeTableName($modelClassName);
280  if ($oldUser !== null && $oldUser->role->id > 0)
281  {
282  self::decrementParentRolesCounts($mungeTableName, $ownedSecurableItem->getClassId('SecurableItem'), $oldUser->role);
283  self::garbageCollect($mungeTableName);
284  }
285  if ($ownedSecurableItem->owner->role->id > 0)
286  {
287  self::incrementParentRolesCounts($mungeTableName, $ownedSecurableItem->getClassId('SecurableItem'), $ownedSecurableItem->owner->role);
288  }
289  }
290 
291  // Being implies the the method must be called just before the associated operation.
292  // The object is needed before the delete occurs and the delete cannot fail.
293  public static function securableItemBeingDeleted(SecurableItem $securableItem) // Call being methods before the destructive operation.
294  {
295  $modelClassName = get_class($securableItem);
296  assert('$modelClassName != "OwnedSecurableItem"');
297  $mungeTableName = self::getMungeTableName($modelClassName);
298  $securableItemId = $securableItem->getClassId('SecurableItem');
299  ZurmoRedBean::exec("delete from $mungeTableName
300  where securableitem_id = $securableItemId");
301  }
302 
303  // Permissions added or removed.
304 
309  public static function securableItemGivenPermissionsForUser(SecurableItem $securableItem, User $user)
310  {
311  $modelClassName = get_class($securableItem);
312  assert('$modelClassName != "OwnedSecurableItem"');
313  $mungeTableName = self::getMungeTableName($modelClassName);
314  $securableItemId = $securableItem->getClassId('SecurableItem');
315  self::incrementCount($mungeTableName, $securableItemId, $user);
316  if ($user->role->id > 0)
317  {
318  self::incrementParentRolesCounts($mungeTableName, $securableItemId, $user->role);
319  }
320  }
321 
326  public static function securableItemGivenPermissionsForGroup(SecurableItem $securableItem, Group $group)
327  {
328  // need this to fix some failures in AccountReadPermissionsOptimizationScenariosTest
329  // find a better way to deal with this
330  Role::forgetRoleIdToRoleCache();
331 
332  $modelClassName = get_class($securableItem);
333  assert('$modelClassName != "OwnedSecurableItem"');
334  $mungeTableName = self::getMungeTableName($modelClassName);
335  $securableItemId = $securableItem->getClassId('SecurableItem');
336  self::incrementCount($mungeTableName, $securableItemId, $group);
337  $roleIds = Role::getIdsByUsersMemberOfGroup($group->id);
338  foreach ($roleIds as $roleId)
339  {
340  $role = Role::getFromCacheOrDatabase($roleId);
341  self::incrementParentRolesCounts($mungeTableName, $securableItemId, $role);
342  }
343  foreach ($group->groups as $subGroup)
344  {
345  self::securableItemGivenPermissionsForGroup($securableItem, $subGroup);
346  }
347  }
348 
353  public static function securableItemLostPermissionsForUser(SecurableItem $securableItem, User $user)
354  {
355  $modelClassName = get_class($securableItem);
356  assert('$modelClassName != "OwnedSecurableItem"');
357  $mungeTableName = self::getMungeTableName($modelClassName);
358  $securableItemId = $securableItem->getClassId('SecurableItem');
359  self::decrementCount($mungeTableName, $securableItemId, $user);
360  if ($user->role->id > 0)
361  {
362  self::decrementParentRolesCounts($mungeTableName, $securableItemId, $user->role);
363  }
364  self::garbageCollect($mungeTableName);
365  }
366 
371  public static function securableItemLostPermissionsForGroup(SecurableItem $securableItem, Group $group)
372  {
373  $modelClassName = get_class($securableItem);
374  assert('$modelClassName != "OwnedSecurableItem"');
375  $mungeTableName = self::getMungeTableName($modelClassName);
376  $securableItemId = $securableItem->getClassId('SecurableItem');
377  self::decrementCount($mungeTableName, $securableItemId, $group);
378  foreach ($group->users as $user)
379  {
380  self::securableItemLostPermissionsForUser($securableItem, $user);
381  }
382  foreach ($group->groups as $subGroup)
383  {
384  self::securableItemLostPermissionsForGroup($securableItem, $subGroup);
385  }
386  self::garbageCollect($mungeTableName);
387  }
388 
389  // User operations.
390 
394  public static function userBeingDeleted($user) // Call being methods before the destructive operation.
395  {
396  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
397  {
398  $mungeTableName = self::getMungeTableName($modelClassName);
399  if ($user->role->id > 0)
400  {
401  self::decrementParentRolesCountsForAllSecurableItems($mungeTableName, $user->role);
402  self::garbageCollect($mungeTableName);
403  }
404  $userId = $user->id;
405  ZurmoRedBean::exec("delete from $mungeTableName
406  where munge_id = 'U$userId'");
407  }
408  }
409 
410  // Group operations.
411 
416  public static function userAddedToGroup(Group $group, User $user)
417  {
418  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
419  {
420  $mungeTableName = self::getMungeTableName($modelClassName);
421  $groupId = $group->id;
422  $sql = "select securableitem_id
423  from $mungeTableName
424  where munge_id = concat('G', $groupId)";
425  $securableItemIds = ZurmoRedBean::getCol($sql);
426  self::bulkIncrementParentRolesCounts($mungeTableName, $securableItemIds, $user->role);
427  /*
428  * This extra step is not needed. See slide 21. This is similar to userBeingRemovedFromRole in that
429  * the above query already is trapping the information needed.
430  Follow the same process for any upstream groups that the group is a member of.
431  */
432  }
433  }
434 
439  public static function userRemovedFromGroup(Group $group, User $user)
440  {
441  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
442  {
443  $mungeTableName = self::getMungeTableName($modelClassName);
444  $groupId = $group->id;
445  $sql = "select securableitem_id
446  from $mungeTableName
447  where munge_id = concat('G', $groupId)";
448  $securableItemIds = ZurmoRedBean::getCol($sql);
449  self::bulkDecrementParentRolesCounts($mungeTableName, $securableItemIds, $user->role);
450  /*
451  * This extra step is not needed. See slide 22. This is similar to userBeingRemovedFromRole or
452  * userAddedToGroup in that the above query is already trapping the information needed.
453  Follow the same process for any upstream groups that the group is a member of.
454  */
455  self::garbageCollect($mungeTableName);
456  }
457  }
458 
462  public static function groupAddedToGroup(Group $group)
463  {
464  self::groupAddedOrRemovedFromGroup(true, $group);
465  }
466 
470  public static function groupBeingRemovedFromGroup(Group $group) // Call being methods before the destructive operation.
471  {
472  self::groupAddedOrRemovedFromGroup(false, $group);
473  }
474 
478  public static function groupBeingDeleted($group) // Call being methods before the destructive operation.
479  {
480  if ($group->group->id > 0 && !$group->group->isSame($group)) // Prevent cycles in database auto build.
481  {
482  self::groupBeingRemovedFromGroup($group);
483  }
484  foreach ($group->groups as $childGroup)
485  {
486  if ($group->isSame($childGroup)) // Prevent cycles in database auto build.
487  {
488  continue;
489  }
490  self::groupBeingRemovedFromGroup($childGroup);
491  }
492  foreach ($group->users as $user)
493  {
494  self::userRemovedFromGroup($group, $user);
495  }
496  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
497  {
498  $groupId = $group->id;
499  $mungeTableName = self::getMungeTableName($modelClassName);
500  ZurmoRedBean::exec("delete from $mungeTableName
501  where munge_id = 'G$groupId'");
502  }
503  }
504 
505  protected static function groupAddedOrRemovedFromGroup($isAdd, Group $group)
506  {
507  assert('is_bool($isAdd)');
508  if ($group->group->isSame($group)) // Prevent cycles in database auto build.
509  {
510  return;
511  }
512 
513  $countMethod1 = $isAdd ? 'bulkIncrementCount' : 'bulkDecrementCount';
514  $countMethod2 = $isAdd ? 'bulkIncrementParentRolesCounts' : 'bulkDecrementParentRolesCounts';
515 
516  $parentGroups = self::getAllParentGroups($group);
517  $users = self::getAllUsersInGroupAndChildGroupsRecursively($group);
518 
519  // Handle groups that $parentGroup is in. In/decrement for the containing groups' containing
520  // groups the models they have explicit permissions on.
521  // And handle user's role's parents. In/decrement for all users that have permission because
522  // they are now in the containing group.
523  if (count($parentGroups) > 0)
524  {
525  $parentGroupPermitableIds = array();
526  foreach ($parentGroups as $parentGroup)
527  {
528  $parentGroupPermitableIds[] = $parentGroup->getClassId('Permitable');
529  }
530  $sql = 'select securableitem_id
531  from permission
532  where permitable_id in (' . join(', ', $parentGroupPermitableIds) . ')';
533  $securableItemIds = ZurmoRedBean::getCol($sql);
534  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
535  {
536  $mungeTableName = self::getMungeTableName($modelClassName);
537  self::$countMethod1($mungeTableName, $securableItemIds, $group);
538  foreach ($users as $user)
539  {
540  if ($user->role->id > 0)
541  {
542  self::$countMethod2($mungeTableName, $securableItemIds, $user->role);
543  }
544  }
545  }
546  }
547  if (!$isAdd)
548  {
549  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
550  {
551  $mungeTableName = self::getMungeTableName($modelClassName);
552  self::garbageCollect($mungeTableName);
553  }
554  }
555  }
556 
557  protected static function getAllUsersInGroupAndChildGroupsRecursively(Group $group)
558  {
559  $users = array();
560  foreach ($group->users as $user)
561  {
562  $users[] = $user;
563  }
564  foreach ($group->groups as $childGroup)
565  {
566  if ($group->isSame($childGroup)) // Prevent cycles in database auto build.
567  {
568  continue;
569  }
570  $users = array_merge($users, self::getAllUsersInGroupAndChildGroupsRecursively($childGroup));
571  }
572  return $users;
573  }
574 
575  protected static function getAllParentGroups(Group $group)
576  {
577  $parentGroups = array();
578  $parentGroup = $group->group;
579  while ($parentGroup->id > 0 && !$parentGroup->isSame($parentGroup->group)) // Prevent cycles in database auto build.
580  {
581  $parentGroups[] = $parentGroup;
582  $parentGroup = $parentGroup->group;
583  }
584  return $parentGroups;
585  }
586 
587  // Role operations.
588 
592  public static function roleParentSet(Role $role)
593  {
594  assert('$role->role->id > 0');
595  self::roleParentSetOrRemoved(true, $role);
596  }
597 
601  public static function roleParentBeingRemoved(Role $role) // Call being methods before the destructive operation.
602  {
603  assert('$role->role->id > 0');
604  self::roleParentSetOrRemoved(false, $role);
605  }
606 
610  public static function roleBeingDeleted(Role $role) // Call being methods before the destructive operation.
611  {
612  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
613  {
614  if ($role->role->id > 0)
615  {
616  self::roleParentBeingRemoved($role);
617  }
618  foreach ($role->roles as $childRole)
619  {
620  if ($childRole->role->id > 0)
621  {
622  self::roleParentBeingRemoved($childRole);
623  }
624  }
625  $mungeTableName = self::getMungeTableName($modelClassName);
626  $roleId = $role->id;
627  $sql = "delete from $mungeTableName
628  where munge_id = 'R$roleId'";
629  ZurmoRedBean::exec($sql);
630  }
631  }
632 
633  protected static function roleParentSetOrRemoved($isSet, Role $role)
634  {
635  assert('is_bool($isSet)');
636  if ($role->role->isSame($role)) // Prevent cycles in database auto build.
637  {
638  return;
639  }
640 
641  $countMethod = $isSet ? 'bulkIncrementParentRolesCounts' : 'bulkDecrementParentRolesCounts';
642 
643  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
644  {
645  $mungeTableName = self::getMungeTableName($modelClassName);
646  $usersInRolesChildren = self::getAllUsersInRolesChildRolesRecursively($role);
647 
648  // Handle users in $role. In/decrement for the parent's parent
649  // roles the models they either own or have explicit permissions on.
650 
651  if (count($role->users) > 0)
652  {
653  $userIds = array();
654  $permitableIds = array();
655  foreach ($role->users as $user)
656  {
657  $userIds[] = $user->id;
658  $permitableIds[] = $user->getClassId('Permitable');
659  }
660  $sql = 'select securableitem_id
661  from ownedsecurableitem
662  where owner__user_id in (' . join(', ', $userIds) . ')
663  union all
664  select securableitem_id
665  from permission
666  where permitable_id in (' . join(', ', $permitableIds) . ')';
667  $securableItemIds = ZurmoRedBean::getCol($sql);
668  self::$countMethod($mungeTableName, $securableItemIds, $role);
669  }
670 
671  // Handle users in the child roles of $role. Increment for the parent's parent
672  // roles the models they either own or have explicit permissions on.
673 
674  if (count($usersInRolesChildren))
675  {
676  $userIds = array();
677  $permitableIds = array();
678  foreach ($usersInRolesChildren as $user)
679  {
680  $userIds[] = $user->id;
681  $permitableIds[] = $user->getClassId('Permitable');
682  }
683  $sql = 'select securableitem_id
684  from ownedsecurableitem
685  where owner__user_id in (' . join(', ', $userIds) . ')
686  union all
687  select securableitem_id
688  from permission
689  where permitable_id in (' . join(', ', $permitableIds) . ')';
690  $securableItemIds = ZurmoRedBean::getCol($sql);
691  self::$countMethod($mungeTableName, $securableItemIds, $role);
692  }
693 
694  // Handle groups for the users in $role. Increment for the parent's parent
695  // roles the models they have explicit permissions on.
696  if (count($role->users) > 0)
697  {
698  $permitableIds = array();
699  foreach ($role->users as $user)
700  {
701  foreach ($user->groups as $group)
702  {
703  $permitableIds[] = $group->getClassId('Permitable');
704  }
705  }
706  $permitableIds = array_unique($permitableIds);
707  if (count($permitableIds) > 0)
708  {
709  $sql = 'select securableitem_id
710  from permission
711  where permitable_id in (' . join(', ', $permitableIds) . ')';
712  $securableItemIds = ZurmoRedBean::getCol($sql);
713  }
714  else
715  {
716  $securableItemIds = array();
717  }
718  self::$countMethod($mungeTableName, $securableItemIds, $role->role);
719  }
720 
721  // Handle groups for the users $role's child roles. Increment for the role's parent
722  // roles the models they have explicit permissions on.
723 
724  if (count($usersInRolesChildren))
725  {
726  $permitableIds = array();
727  foreach ($usersInRolesChildren as $user)
728  {
729  foreach ($user->groups as $group)
730  {
731  $permitableIds[] = $group->getClassId('Permitable');
732  }
733  }
734  $permitableIds = array_unique($permitableIds);
735  if (count($permitableIds) > 0)
736  {
737  $sql = 'select securableitem_id
738  from permission
739  where permitable_id in (' . join(', ', $permitableIds) . ')';
740  $securableItemIds = ZurmoRedBean::getCol($sql);
741  }
742  else
743  {
744  $securableItemIds = array();
745  }
746  self::$countMethod($mungeTableName, $securableItemIds, $role);
747  }
748  if (!$isSet)
749  {
750  self::garbageCollect($mungeTableName);
751  }
752  }
753  }
754 
755  protected static function getAllUsersInRolesChildRolesRecursively(Role $role)
756  {
757  $users = array();
758  foreach ($role->roles as $childRole)
759  {
760  if ($role->isSame($childRole)) // Prevent cycles in database auto build.
761  {
762  continue;
763  }
764  foreach ($childRole->users as $user)
765  {
766  $users[] = $user;
767  }
768  $users = array_merge($users, self::getAllUsersInRolesChildRolesRecursively($childRole));
769  }
770  return $users;
771  }
772 
776  public static function userAddedToRole(User $user)
777  {
778  assert('$user->role->id > 0');
779  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
780  {
781  $mungeTableName = self::getMungeTableName($modelClassName);
782  $userId = $user->id;
783  $sql = "select securableitem_id
784  from ownedsecurableitem
785  where owner__user_id = $userId";
786  $securableItemIds = ZurmoRedBean::getCol($sql);
787  //Increment the parent roles for securableItems that the user is the owner on.
788  self::bulkIncrementParentRolesCounts($mungeTableName, $securableItemIds, $user->role);
789 
790  //Get all downstream groups the user is in including any groups that are in those groups recursively.
791  //Then for each group found, add weight for the user's upstream roles.
792  $groupMungeIds = array();
793  foreach ($user->groups as $group)
794  {
795  $groupMungeIds[] = 'G' . $group->id;
796  self::getAllUpstreamGroupsRecursively($group, $groupMungeIds);
797  }
798  if (count($groupMungeIds) > 0)
799  {
800  $inSqlPart = SQLOperatorUtil::resolveOperatorAndValueForOneOf('oneOf', $groupMungeIds, true);
801  $sql = "select distinct $mungeTableName.securableitem_id
802  from $mungeTableName
803  where $mungeTableName.munge_id $inSqlPart";
804  $securableItemIds = ZurmoRedBean::getCol($sql);
805  self::bulkIncrementParentRolesCounts($mungeTableName, $securableItemIds, $user->role);
806  }
807  }
808  }
809 
814  public static function userBeingRemovedFromRole(User $user, Role $role)
815  {
816  foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName)
817  {
818  $mungeTableName = self::getMungeTableName($modelClassName);
819  $userId = $user->id;
820  $sql = "select securableitem_id
821  from ownedsecurableitem
822  where owner__user_id = $userId";
823  $securableItemIds = ZurmoRedBean::getCol($sql);
824  self::bulkDecrementParentRolesCounts($mungeTableName, $securableItemIds, $role);
825 
826  $sql = "select $mungeTableName.securableitem_id
827  from $mungeTableName, _group__user
828  where $mungeTableName.munge_id = concat('G', _group__user._group_id) and
829  _group__user._user_id = $userId";
830  $securableItemIds = ZurmoRedBean::getCol($sql);
831  self::bulkDecrementParentRolesCounts($mungeTableName, $securableItemIds, $role);
832  /*
833  * This additional step I don't think is needed because the sql query above actually traps
834  * the upstream explicit securableItems because the lower level groups will already have a point for
835  * each of them.
836  What groups are the user part of and what groups are those groups children of recursively?
837  For any models that have that group explicity for read, subtract 1 point for the user's
838  upstream roles from the disconnected role.
839  */
840  self::garbageCollect($mungeTableName);
841  }
842  }
843 
845 
850  public static function getAllUpstreamGroupsRecursively(Group $group, & $groupMungeIds)
851  {
852  assert('is_array($groupMungeIds)');
853  if ($group->group->id > 0 )
854  {
855  $groupMungeIds[] = 'G' . $group->group->id;
856  if ($group->isSame($group->group))
857  {
858  //Do Nothing. Prevent cycles in database auto build.
859  }
860  else
861  {
862  self::getAllUpstreamGroupsRecursively($group->group, $groupMungeIds);
863  }
864  }
865  }
866 
870  public static function recreateTable($mungeTableName)
871  {
872  assert('is_string($mungeTableName) && $mungeTableName != ""');
873  ZurmoRedBean::$writer->dropTableByTableName($mungeTableName);
874  $schema = static::getMungeTableSchemaByName($mungeTableName);
875  $messageLogger = new MessageLogger();
877  $schema, $messageLogger);
878  }
879 
880  protected static function getMungeTableSchemaByName($tableName)
881  {
882  return array($tableName => array('columns' => array(
883  array(
884  'name' => 'securableitem_id',
885  'type' => 'INT(11)',
886  'unsigned' => 'UNSIGNED',
887  'notNull' => 'NOT NULL', // Not Coding Standard
888  'collation' => null,
889  'default' => null,
890  ),
891  array(
892  'name' => 'munge_id',
893  'type' => 'VARCHAR(12)',
894  'unsigned' => null,
895  'notNull' => 'NOT NULL', // Not Coding Standard
896  'collation' => 'COLLATE utf8_unicode_ci',
897  'default' => null,
898  ),
899  array(
900  'name' => 'count',
901  'type' => 'INT(8)',
902  'unsigned' => 'UNSIGNED',
903  'notNull' => 'NOT NULL', // Not Coding Standard
904  'collation' => null,
905  'default' => null,
906  ),
907  ),
908  'indexes' => array('securableitem_id_munge_id' => array(
909  'columns' => array('securableitem_id', 'munge_id'),
910  'unique' => true,
911  ),
912  $tableName . '_securableitem_id' => array(
913  'columns' => array('securableitem_id'),
914  'unique' => false,
915  ),
916  ),
917  )
918  );
919  }
920 
921  protected static function incrementCount($mungeTableName, $securableItemId, $item)
922  {
923  assert('is_string($mungeTableName) && $mungeTableName != ""');
924  assert('is_int($securableItemId) && $securableItemId > 0');
925  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
926  $itemId = $item->id;
927  $type = self::getMungeType($item);
928  $mungeId = "$type$itemId";
929  ZurmoRedBean::exec("insert into $mungeTableName
930  (securableitem_id, munge_id, count)
931  values ($securableItemId, '$mungeId', 1)
932  on duplicate key
933  update count = count + 1");
934  }
935 
936  protected static function setCount($mungeTableName, $securableItemId, $item, $count)
937  {
938  assert('is_string($mungeTableName) && $mungeTableName != ""');
939  assert('is_int($securableItemId) && $securableItemId > 0');
940  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
941  $itemId = $item->id;
942  $type = self::getMungeType($item);
943  $mungeId = "$type$itemId";
944  ZurmoRedBean::exec("insert into $mungeTableName
945  (securableitem_id, munge_id, count)
946  values ($securableItemId, '$mungeId', $count)
947  on duplicate key
948  update count = $count");
949  }
950 
951  protected static function decrementCount($mungeTableName, $securableItemId, $item)
952  {
953  assert('is_string($mungeTableName) && $mungeTableName != ""');
954  assert('is_int($securableItemId) && $securableItemId > 0');
955  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
956  $itemId = $item->id;
957  $type = self::getMungeType($item);
958  $mungeId = "$type$itemId";
959  ZurmoRedBean::exec("update $mungeTableName
960  set count = count - 1
961  where securableitem_id = $securableItemId and
962  munge_id = '$mungeId'");
963  }
964 
965  protected static function decrementCountForAllSecurableItems($mungeTableName, $item)
966  {
967  assert('is_string($mungeTableName) && $mungeTableName != ""');
968  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
969  $itemId = $item->id;
970  $type = self::getMungeType($item);
971  $mungeId = "$type$itemId";
972  ZurmoRedBean::exec("update $mungeTableName
973  set count = count - 1
974  where munge_id = '$mungeId'");
975  }
976 
977  protected static function bulkIncrementCount($mungeTableName, $securableItemIds, $item)
978  {
979  assert('is_string($mungeTableName) && $mungeTableName != ""');
980  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
981  foreach ($securableItemIds as $securableItemId)
982  {
983  self::incrementCount($mungeTableName, intval($securableItemId), $item);
984  }
985  }
986 
987  protected static function bulkDecrementCount($mungeTableName, $securableItemIds, $item)
988  {
989  assert('is_string($mungeTableName) && $mungeTableName != ""');
990  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
991  foreach ($securableItemIds as $securableItemId)
992  {
993  self::decrementCount($mungeTableName, intval($securableItemId), $item);
994  }
995  }
996 
997  protected static function incrementParentRolesCounts($mungeTableName, $securableItemId, Role $role)
998  {
999  assert('is_string($mungeTableName) && $mungeTableName != ""');
1000  assert('is_int($securableItemId) && $securableItemId > 0');
1001  if ($role->role->isSame($role)) // Prevent cycles in database auto build.
1002  {
1003  return;
1004  }
1005  if ($role->role->id > 0)
1006  {
1007  self::incrementCount ($mungeTableName, $securableItemId, $role->role);
1008  self::incrementParentRolesCounts($mungeTableName, $securableItemId, $role->role);
1009  }
1010  }
1011 
1012  protected static function decrementParentRolesCounts($mungeTableName, $securableItemId, Role $role)
1013  {
1014  assert('is_string($mungeTableName) && $mungeTableName != ""');
1015  assert('is_int($securableItemId) && $securableItemId > 0');
1016  if ($role->role->isSame($role)) // Prevent cycles in database auto build.
1017  {
1018  return;
1019  }
1020  if ($role->role->id > 0)
1021  {
1022  self::decrementCount ($mungeTableName, $securableItemId, $role->role);
1023  self::decrementParentRolesCounts($mungeTableName, $securableItemId, $role->role);
1024  }
1025  }
1026 
1027  protected static function decrementParentRolesCountsForAllSecurableItems($mungeTableName, Role $role)
1028  {
1029  assert('is_string($mungeTableName) && $mungeTableName != ""');
1030  if ($role->role->isSame($role)) // Prevent cycles in database auto build.
1031  {
1032  return;
1033  }
1034  if ($role->role->id > 0)
1035  {
1036  self::decrementCountForAllSecurableItems ($mungeTableName, $role->role);
1037  self::decrementParentRolesCountsForAllSecurableItems($mungeTableName, $role->role);
1038  }
1039  }
1040 
1041  protected static function bulkIncrementParentRolesCounts($mungeTableName, $securableItemIds, Role $role)
1042  {
1043  foreach ($securableItemIds as $securableItemId)
1044  {
1045  self::incrementParentRolesCounts($mungeTableName, intval($securableItemId), $role);
1046  }
1047  }
1048 
1049  protected static function bulkDecrementParentRolesCounts($mungeTableName, $securableItemIds, Role $role)
1050  {
1051  foreach ($securableItemIds as $securableItemId)
1052  {
1053  self::decrementParentRolesCounts($mungeTableName, intval($securableItemId), $role);
1054  }
1055  }
1056 
1057  // This must be called ny any public method which decrements
1058  // counts after it has done all its count decrementing.
1059  // It is not done in decrementCount to avoid doing it more
1060  // than is necessary.
1061  protected static function garbageCollect($mungeTableName)
1062  {
1063  assert("(int)ZurmoRedBean::getCell('select count(*)
1064  from $mungeTableName
1065  where count < 0') == 0");
1066  ZurmoRedBean::exec("delete from $mungeTableName
1067  where count = 0");
1068  assert("(int)ZurmoRedBean::getCell('select count(*)
1069  from $mungeTableName
1070  where count < 1') == 0");
1071  }
1072 
1073  protected static function getMungeType($item)
1074  {
1075  assert('$item instanceof User || $item instanceof Group || $item instanceof Role');
1076  return substr(get_class($item), 0, 1);
1077  }
1078 
1079  protected static function getMainTableName($modelClassName)
1080  {
1081  assert('is_string($modelClassName) && $modelClassName != ""');
1082  return $modelClassName::getTableName();
1083  }
1084 
1089  public static function getMungeTableName($modelClassName)
1090  {
1091  assert('is_string($modelClassName) && $modelClassName != ""');
1092  return self::getMainTableName($modelClassName) . '_read';
1093  }
1094  }
1095 ?>
Definition: Role.php:37
static userAddedToGroup(Group $group, User $user)
Definition: User.php:37
getClassId($modelClassName)
static securableItemGivenPermissionsForUser(SecurableItem $securableItem, User $user)
static userBeingRemovedFromRole(User $user, Role $role)
static ownedSecurableItemOwnerChanged(OwnedSecurableItem $ownedSecurableItem, User $oldUser=null)
static securableItemGivenPermissionsForGroup(SecurableItem $securableItem, Group $group)
static userRemovedFromGroup(Group $group, User $user)
static generateOrUpdateTableBySchemaDefinition(array $schemaDefinition, &$messageLogger, $validate=true)
static rebuild($overwriteExistingTables=true, $forcePhp=false, $messageStreamer=null)
static getSubset(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter=null, $offset=null, $count=null, $where=null, $orderBy=null, $modelClassName=null, $selectDistinct=false)
Definition: Group.php:37
static getAllUpstreamGroupsRecursively(Group $group, &$groupMungeIds)
static getCount(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter=null, $where=null, $modelClassName=null, $selectDistinct=false)
static getAll($orderBy=null, $sortDescending=false, $modelClassName=null)
static getTableName()
isSame(RedBeanModel $model)
static securableItemLostPermissionsForGroup(SecurableItem $securableItem, Group $group)
static securableItemLostPermissionsForUser(SecurableItem $securableItem, User $user)
Generated on Sat Dec 5 2020 07:10:47
Account Suspended
Account Suspended
This Account has been suspended.
Contact your hosting provider for more information.