Section 3: Restrictors & The Permission Context

Restrictors

Now we come down to the key difference between most security managers and this one, we add the concept of restrictors which are applied to a permission in a GRANT statement. The restrictor idea came about because I found that often you would want to grant a user a permission like for example a UserPermission allowing them to manage users but they should not be allowed to manage users outside their group, by applying a restrictor we can solve this problem. You could solve this problem in another way with other security managers by having alot more permissions but it doesnt make for clean scalable code, there is more on this and the overall problem here.

Lets take a look at the Restrictor interface:

UML class definition of the Restrictor interface

As you can see it's very simple and because it is an interface it's easy to make anything into a Restrictor. When you grant a permission with restrictors the AccessDescriptor when performing a permission check will first ask each granted permission if it 'implies' (by implies we sort of mean 'does it equal the permission and therefore pass the check?') the permission being checked, and then for each implied permission it asks any restrictors whether they restrict the permission being checked. If the permission is restricted then isRestricted returns true and if no permissions and restrictors within the AccessDescriptor pass the checked permission then a SecurityException is thrown, the sequence diagram below explains this whole process visually.

Sequence diagram showing a check permission call

Sequence diagram showing a check permission call (click image to see original size)

The Permission Context

So the restrictors have enough information to carry out their role a permission object can have a context set by calling setContext, the context can be anything at all but I tend to set it with the object that is the subject of some operation. For example consider the class below.

UML class diagram of the imaginary User class

If I instantiated this class into $u and was about to call save() before I did that I would do a permission check for the UserPermission and set the context as $u, this is illustrated below.

  1. <?php
  2. $u = new User('Rob Graham', 5);
  3. $p = Permission::getInstance('user.UserPermission','',array('add'));
  4. $p->setContext($u);
  5. AccessController::checkPermission($p);
  6. $u->save();
  7. ?>

If any restrictors are associated with the UserPermission, like a restrictor that only allows the management of a User object if it belongs to the same group ID as the authenticated user, they can inspect the permission context to carry out their role.

Creating Your Own Restrictor

Lets create an example restrictor that restricts any User operations to within the same group ID as the current authenticated user, all restrictors must reside within security/restrictor and follow the same naming convention as permissions, that is they must start with 'class.' then have the classname and end with '.php'. We'll call our restrictor class UserOwnGroup and therefore it will be created in the file 'security/restrictor/class.UserOwnGroup.php', which looks like this:

  1. <?php
  2. require_once('security/restrictor/interface.Restrictor.php');
  3. /**
  4. * @package security
  5. * @subpackage restrictor
  6. */
  7. class UserOwnGroup implements Restrictor
  8. {
  9. public function isRestricted(Permission $p)
  10. {
  11. $context = $p->getContext();
  12. if($context instanceof User)
  13. {
  14. $currentUser = User::getAuthenticatedUser();
  15. if($currentUser->getGroupId()!=$context->getGroupId())
  16. {
  17. return true; // restricted - group id's are different
  18. }
  19. else
  20. {
  21. return false;
  22. }
  23. }
  24. else
  25. {
  26. return true;
  27. }
  28. }
  29. }
  30. ?>

On line 13 you can see we check the type of the context, we have to do this because the permission context can be set to anything so for robust code we should check and by default if the type is not one we deal with we should restrict as we do on line 27, this makes for more secure code if someone were to try and apply the wrong type of restrictor to a permission.

The GRANT Statement Revisited

Lets go back and look at the GRANT statement from section 2 now we know what restrictors are, you'll remember the syntax was:

GRANT permission{name}[/action 1[, action 2 [,...]]]  [-restrictor 1[, restrictor 2 [,...]]]

So to tie up our example so far and grant the UserPermission with the UserOwnGroup restrictor we do this:

GRANT user.UserPermission{*}/add,update,delete -UserOwnGroup

Conclusion

As you can probably tell from the documentation as a fully fledged security manager this code is some way off, however if this project generates enough interest and I find that other people do have the same problems when using current security managers and libraries then I will hopefully continue development and perhaps get a SourceForge project going.

Thankyou for reading,

Rob Graham.