64
Practical Applications of Zend_Acl Rowan Merewood

Practical Applications of Zend_Acl

Embed Size (px)

DESCRIPTION

Access Control Lists are a tool that allows us to map permissions to objects - within Zend_Acl this maps to a hierarchical arrangement of roles and resources.This talk will follow through the basic use of Zend_Acl and steadily build a series of practical examples to illustrate the different methods of creating and enforcing an ACL for an application. This will include how to implement some of the more complicated hierarchical relationships and advanced conditions through the use of assertions. We will also cover the design considerations of where to attach the ACL, with the differences between applying it to controllers or models. With a functioning ACL in place, we will examine some of the methods for persisting the list and whether that list should be static or dynamic.Alongside the straight functionality of our code, we will also examine how to effectively unit test it, improving its performance and analysing the level of security that has been created.

Citation preview

Page 1: Practical Applications of Zend_Acl

Practical Applications of

Zend_Acl

Rowan Merewood

Page 2: Practical Applications of Zend_Acl

2

Who is this?

@rowan_m

Software Engineer

Team Lead

http://merewood.org

Page 3: Practical Applications of Zend_Acl

3

Why do this?

So you don't have to.

Problems encountered, solutions discovered,

lessons learned.

Page 4: Practical Applications of Zend_Acl

4

What do you want?

More concept?

- or -

More code?

Page 5: Practical Applications of Zend_Acl

5

What does this solve?

The “Gold Standard” for security.

Page 6: Practical Applications of Zend_Acl

6

Gold79

196.97

Gold79

196.97

Gold79

196.97

Gold Standard

Au thentication

Au thorisation

Au diting

B.W. Lampson. Computer Security in the Real World. Computer, 37(6):37–46, 2004http://research.microsoft.com/en-us/um/people/blampson/69-SecurityRealIEEE/69-SecurityRealIEEE.htm

Page 7: Practical Applications of Zend_Acl

7

Gold79

196.97

Gold79

196.97

Gold79

196.97

Gold Standard

Au thentication

Au thorisation

Au diting

B.W. Lampson. Computer Security in the Real World. Computer, 37(6):37–46, 2004http://research.microsoft.com/en-us/um/people/blampson/69-SecurityRealIEEE/69-SecurityRealIEEE.htm

You are here.

Page 8: Practical Applications of Zend_Acl

8

Role-based Access Control

Roles

Resources

Privileges

AssertionsOh my!

Page 9: Practical Applications of Zend_Acl

9

Warning!

There is no right answer

Page 10: Practical Applications of Zend_Acl

10

Roles

• A named group of privileges for a resource.

• A role may inherit from many parent roles

• Build the tree from leaf to root

Page 11: Practical Applications of Zend_Acl

11

Roles

• A user is a leaf node

Developer

Lead Developer

Rowan

Client Contact

Employee

Page 12: Practical Applications of Zend_Acl

12

Roles

• A user is a leaf node

Developer

Lead Developer

Rowan

Client Contact

Employee

Sales

Evil

Rowan

Page 13: Practical Applications of Zend_Acl

13

Roles

• Use inheritance sparingly

• Avoid circular dependencies

• Over-complicated relationships

• Difficult to configure

Page 14: Practical Applications of Zend_Acl

14

Resources

• Objects with which users can interact

• A resource may have one parent

• Build the tree from root to leaf

Page 15: Practical Applications of Zend_Acl

15

Resources

Ship

Galaxy class

Klingon

NCC-1701-D

Federation

NCC-1701-E

Page 16: Practical Applications of Zend_Acl

16

Resources

Allow “bridge crew” rolethe “activate cloak” privilege

Ship

Galaxy class

Klingon

NCC-1701-D

Federation

NCC-1701-E

Page 17: Practical Applications of Zend_Acl

17

Resources

Allow “*” rolethe “self destruct” privilege

asserting “is captain” Ship

Galaxy class

Klingon

NCC-1701-D

Federation

NCC-1701-E

Page 18: Practical Applications of Zend_Acl

18

Privileges

• Simple – just strings

• Qualifies the operation a role may perform against a resource

• Shared vocabulary: CRUD

Page 19: Practical Applications of Zend_Acl

19

Assertions

• An arbitrary condition attached to the ACL returning true or false

• Has access to the role, resource, privilege and ACL

• Power and flexibility open to abuse

Page 20: Practical Applications of Zend_Acl

20

Assertions

• "user" can "view" a "group photo" if "user is a member of the group"

• "user" can "create" an "comment" if "the user has submitted less than 5 comments in the last hour"

• "job scheduler" may "schedule" a "task" if "no instances of the task are running"

Page 21: Practical Applications of Zend_Acl

21

Assertions

• "user" can "view" a "group photo" if "user is a member of the group"

• "user" can "create" an "comment" if "the user has submitted less than 5 comments in the last hour"

• "job scheduler" may "schedule" a "task" if "no instances of the task are running"

Direct relationship between the role and the resource.All dependencies are passed in.

“Visibility” is a good concept to keep in the ACL

Page 22: Practical Applications of Zend_Acl

22

Assertions

• "user" can "view" a "group photo" if "user is a member of the group"

• "user" can "create" an "comment" if "the user has submitted less than 5 comments in the last hour"

• "job scheduler" may "schedule" a "task" if "no instances of the task are running"

Border-line – most dependencies containedDoes the time-based system state count as

“authorisation”?

Page 23: Practical Applications of Zend_Acl

23

Assertions

• "user" can "view" a "group photo" if "user is a member of the group"

• "user" can "create" an "comment" if "the user has submitted less than 5 comments in the last hour"

• "job scheduler" may "schedule" a "task" if "no instances of the task are running"

Advanced dependencies definitely outside the scopeThis is a “pre-add” check for the model

Page 24: Practical Applications of Zend_Acl

24

Let's see some code

Zend_Acl

Zend_Acl_Role_Interface

Zend_Acl_Resource_Interface

Zend_Acl_Assert_Interface

Page 25: Practical Applications of Zend_Acl

25

Simple, Static ACL

$acl = new Zend_Acl();

$eng = new Zend_Acl_Role('engineering');

$scotty = new Zend_Acl_Role('scotty');

$kirk = new Zend_Acl_Role('kirk');

$dilCrys = new Zend_Acl_Resource('dilithium crystals');

$acl->addRole($eng);

$acl->addRole($scotty, $eng);

$acl->addRole($kirk);

$acl->addResource($dilCrys);

$acl->allow($eng, $dilCrys);

Page 26: Practical Applications of Zend_Acl

26

Simple, Static ACL

echo "Can Scotty replace the dilithium crystals?\n";

echo ($acl->isAllowed('scotty', 'dilithium crystals', 'replace')) ? "Can do\n" : "Cannae do\n";

echo "Can Kirk seduce the dilithium crystals?\n";

echo ($acl->isAllowed('kirk', 'dilithium crystals', 'seduce')) ? "Really can\n" : "Obviously not\n";

rowan@swordbean:~$ php test01.php

Can Scotty replace the dilithium crystals?

Can do

Can Kirk seduce the dilithium crystals?

Obviously not

Page 27: Practical Applications of Zend_Acl

27

Implementing Resource

Any entity in your system:• Controllers• Models• Users• Files• Processes

Page 28: Practical Applications of Zend_Acl

28

Implementing Resource class Ship implements Zend_Acl_Resource_Interface

{

public $captain;

public $registry;

public function getResourceId()

{

return $this->registry;

}

}

$acl = new Zend_Acl();

$kirk = new Zend_Acl_Role('kirk');

$acl->addRole($kirk);

$ship = new Ship();

$ship->captain = 'kirk';

$ship->registry = 'ncc-1701';

$acl->addResource($ship);

Page 29: Practical Applications of Zend_Acl

29

Adding an Assertionclass IsCaptainOf implements Zend_Acl_Assert_Interface

{

public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $role = null,

Zend_Acl_Resource_Interface $resource = null, $privilege = null) {

if ( !($resource instanceof Ship) ) {

throw new Zend_Acl_Exception(

'IsCaptainOf assertion only valid on Ships' );

}

return ($role->getRoleId() == $resource->captain);

}

}

$assert = new IsCaptainOf();

$acl->allow('kirk', 'ncc-1701', 'destruct', $assert);

echo "Can Kirk order self-destruct?\n";

echo ($acl->isAllowed('kirk', 'ncc-1701', 'destruct')) ?

"Star Trek III: The Search for Spock\n" : "No\n";

Page 30: Practical Applications of Zend_Acl

30

class IsCaptainOf implements Zend_Acl_Assert_Interface

{

public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $role = null,

Zend_Acl_Resource_Interface $resource = null, $privilege = null) {

if ( !($resource instanceof Ship) ) {

throw new Zend_Acl_Exception(

'IsCaptainOf assertion only valid on Ships' );

}

return ($role->getRoleId() == $resource->captain);

}

}

$assert = new IsCaptainOf();

$acl->allow('kirk', 'ncc-1701', 'destruct', $assert);

echo "Can Kirk order self-destruct?\n";

echo ($acl->isAllowed('kirk', 'ncc-1701', 'destruct')) ?

"Star Trek III: The Search for Spock\n" : "No\n";

Adding an Assertion

Increasing complexityIntroducing extra points of failure

Page 31: Practical Applications of Zend_Acl

31

Dynamic ACL

Store config. in the DB

Build on the fly

One size does not fit all

Page 32: Practical Applications of Zend_Acl

32

Database structure

Page 33: Practical Applications of Zend_Acl

33

Database structure

Users not givenpermissions directly

Page 34: Practical Applications of Zend_Acl

34

Database structure

Role hierarchy restrictedto Group User→

Page 35: Practical Applications of Zend_Acl

35

Database structure

Permissions assignedto 1 Group

Page 36: Practical Applications of Zend_Acl

36

Database structure

Resource is free textDoes not need DB link

e.g. Controllers

Page 37: Practical Applications of Zend_Acl

37

Database structure

Same for privilege

Page 38: Practical Applications of Zend_Acl

38

Database structure

Class name fragment

Page 39: Practical Applications of Zend_Acl

39

DB Classesclass Users extends Zend_Db_Table_Abstract

{

protected $_name = 'users';

protected $_primary = 'user_id';

protected $_dependentTables = array('UserGroups');

protected $_rowClass = 'User';

}

class Groups extends Zend_Db_Table_Abstract

{

protected $_name = 'groups';

protected $_primary = 'group_id';

protected $_dependentTables = array('UserGroups');

protected $_rowClass = 'Group';

}

Page 40: Practical Applications of Zend_Acl

40

DB Classesclass UserGroups extends Zend_Db_Table_Abstract

{

protected $_name = 'user_groups';

protected $_primary = array('user_id', 'group_id');

protected $_referenceMap = array(

'User' => array(

'columns' => array('user_id'),

'refTableClass' => 'Users',

'refColumns' => array('user_id'),

),

'Group' => array(

'columns' => array('group_id'),

'refTableClass' => 'Groups',

'refColumns' => array('group_id'),

),

);

}

Page 41: Practical Applications of Zend_Acl

41

DB Classes

class Permissions extends Zend_Db_Table_Abstract

{

protected $_name = 'permissions';

protected $_primary = 'permission_id';

protected $_referenceMap = array(

'Group' => array(

'columns' => array('group_id'),

'refTableClass' => 'Groups',

'refColumns' => array('group_id'),

),

);

}

Page 42: Practical Applications of Zend_Acl

42

Implementing Roleinterface Role_Interface extends Zend_Acl_Role_Interface {

public function getType();

}

class User extends Zend_Db_Table_Row_Abstract implements Role_Interface {

public function getType() {

return 'User';

}

public function getRoleId() {

return $this->getType().':'.$this->user_id;

}

}

class Group extends Zend_Db_Table_Row_Abstract implements Role_Interface {

public function getType() {

return 'Group';

}

public function getRoleId() {

return $this->getType().':'.$this->group_id;

}

}

Page 43: Practical Applications of Zend_Acl

43

What do wewant to enforce?

Users in the “command” group may issue orders to users

subordinate to them

Page 44: Practical Applications of Zend_Acl

44

Implementing Resource

Page 45: Practical Applications of Zend_Acl

45

Implementing Resource

interface Resource_Interface extends Zend_Acl_Role_Interface {

public function getType();

}

class Order extends Zend_Db_Table_Row_Abstract implements Resource_Interface {

public function getType() {

return 'Order';

}

public function getResourceId() {

$id = $this->getType();

if ($this->order_id) {

$id .= ':'.$this->order_id;

}

return $id;

}

}

Page 46: Practical Applications of Zend_Acl

46

Populate the DB

mysql> select * from groups;

+----------+-------------+

| group_id | name |

+----------+-------------+

| 1 | command |

| 2 | bridge crew |

+----------+-------------+

mysql> select * from permissions;

+---------------+-------+----------+----------+-----------+------------+

| permission_id | type | group_id | resource | privilege | assert |

+---------------+-------+----------+----------+-----------+------------+

| 1 | allow | 1 | Order | read | NULL |

| 2 | allow | 1 | Order | create | IsSuperior |

| 3 | allow | NULL | Order | belay | IsIssuer |

| 4 | allow | 2 | Order | read | NULL |

+---------------+-------+----------+----------+-----------+------------+

Page 47: Practical Applications of Zend_Acl

47

Populate the DB

mysql> select u.name, g.name as `group`, r.name as `rank` from users u

inner join user_groups ug on u.user_id = ug.user_id

inner join groups g on ug.group_id = g.group_id

inner join user_ranks ur on u.user_id = ur.user_id

inner join ranks r on ur.rank_id = r.rank_id;

+------+-------------+---------+

| name | group | rank |

+------+-------------+---------+

| kirk | command | captain |

| rand | bridge crew | yeoman |

+------+-------------+---------+

Page 48: Practical Applications of Zend_Acl

48

Issuing an order

$issuer = Zend_Auth::getInstance()->getIdentity();

$u = new Users();

$subord = $u->find(2)->current();

$order = new Order();

$order->superior_user_id = $issuer->user_id;

$order->subordinate_user_id = $subord->user_id;

$order->detail = "Get your red shirt, it's time for an away mission.";

$acl = new AclWrapper();

if (!$acl->isAllowed($issuer, $order, 'create')) {

throw new Zend_Controller_Action_Exception(

'Not allowed to create order!' , 403);

}

$order->save();

Page 49: Practical Applications of Zend_Acl

49

Issuing an order

$issuer = Zend_Auth::getInstance()->getIdentity();

$u = new Users();

$subord = $u->find(2)->current();

$order = new Order();

$order->superior_user_id = $issuer->user_id;

$order->subordinate_user_id = $subord->user_id;

$order->detail = "Get your red shirt, it's time for an away mission.";

$acl = new AclWrapper();

if (!$acl->isAllowed($issuer, $order, 'create')) {

throw new Zend_Controller_Action_Exception(

'Not allowed to create order!' , 403);

}

$order->save();

You could move this checkonto the model

Page 50: Practical Applications of Zend_Acl

50

Building the ACLclass AclWrapper

{

public function isAllowed(User $role = null,

Resource_Interface $resource = null, $privilege = null) {

$acl = new Zend_Acl();

$groups = $user->findGroups();

foreach ($groups as $group) {

$acl->addRole($group);

}

$acl->addRole($user, $groups);

if (strpos($resource->getResourceId(), ':')) {

$parent = new Zend_Acl_Resource($resource->getType());

$acl->addResource($parent);

$acl->addResource($resource, $parent);

} else {

$acl->addResource($resource);

}

[...]

Page 51: Practical Applications of Zend_Acl

51

Building the ACLclass AclWrapper

{

public function isAllowed(User $role = null,

Resource_Interface $resource = null, $privilege = null) {

$acl = new Zend_Acl();

$groups = $user->findGroups();

foreach ($groups as $group) {

$acl->addRole($group);

}

$acl->addRole($user, $groups);

if (strpos($resource->getResourceId(), ':')) {

$parent = new Zend_Acl_Resource($resource->getType());

$acl->addResource($parent);

$acl->addResource($resource, $parent);

} else {

$acl->addResource($resource);

}

[...]

Add Group rolesAdd the User role

Page 52: Practical Applications of Zend_Acl

52

Building the ACLclass AclWrapper

{

public function isAllowed(User $role = null,

Resource_Interface $resource = null, $privilege = null) {

$acl = new Zend_Acl();

$groups = $user->findGroups();

foreach ($groups as $group) {

$acl->addRole($group);

}

$acl->addRole($user, $groups);

if (strpos($resource->getResourceId(), ':')) {

$parent = new Zend_Acl_Resource($resource->getType());

$acl->addResource($parent);

$acl->addResource($resource, $parent);

} else {

$acl->addResource($resource);

}

[...]

':' means adding an instanceand its parent

Page 53: Practical Applications of Zend_Acl

53

Building the ACLforeach ($groups as $group) {

foreach ($groups->findPermissions as $permission) {

$assert = null;

$classname = $permission->assert;

if (

$classname && class_exists($classname)

&& is_subclass_of($classname, 'Zend_Acl_Assert_Interface')

) {

$assert = new $classname();

}

$op = ($permission->type == 'allow') ? 'allow' : 'deny';

$acl->$op($group, $resource, $permission->privilege, $assert);

}

}

return $acl->isAllowed($role, $resource, $privilege);

}

}

Page 54: Practical Applications of Zend_Acl

54

Building the ACLforeach ($groups as $group) {

foreach ($groups->findPermissions as $permission) {

$assert = null;

$classname = $permission->assert;

if (

$classname && class_exists($classname)

&& is_subclass_of($classname, 'Zend_Acl_Assert_Interface')

) {

$assert = new $classname();

}

$op = ($permission->type == 'allow') ? 'allow' : 'deny';

$acl->$op($group, $resource, $permission->privilege, $assert);

}

}

return $acl->isAllowed($role, $resource, $privilege);

}

}

Validate as much as possible!

Page 55: Practical Applications of Zend_Acl

55

Asserting Superiorityclass IsSuperior implements Zend_Acl_Assert_Interface

{

public function assert(

Zend_Acl $acl, Zend_Acl_Role_Interface $role = null,

Zend_Acl_Resource_Interface $resource = null, $privilege = null)

{

if (!$role instanceof User) {

throw new Zend_Acl_Exception('Assertion only applies to Users');

}

if (!$resource instanceof Order) {

throw new Zend_Acl_Exception('Assertion only applies to Orders');

}

$supRank = $role->findRanks()->current();

$subRank = $resource->findUsersBySubordinate()->current();

return ($supRank->rank_id > $subRank->rank_id);

}

}

Page 56: Practical Applications of Zend_Acl

56

Issuing an order

$issuer = Zend_Auth::getInstance()->getIdentity();

$u = new Users();

$subord = $u->find(2)->current();

$order = new Order();

$order->superior_user_id = $issuer->user_id;

$order->subordinate_user_id = $subord->user_id;

$order->detail = "Get your red shirt, it's time for an away mission.";

$acl = new AclWrapper();

if (!$acl->isAllowed($issuer, $order, 'create')) {

throw new Zend_Controller_Action_Exception(

'Not allowed to create order!' , 403);

}

$order->save();

Page 57: Practical Applications of Zend_Acl

57

Conclusions

Is this the right way?

Page 58: Practical Applications of Zend_Acl

58

Attaching the ACL

• Controller and Action?

• Model and Method?

• Business object and Action?

• All of the above?

Page 59: Practical Applications of Zend_Acl

59

Unit testing

• Pass the Zend_Acl into your wrapper

• Use a factory

Page 60: Practical Applications of Zend_Acl

60

Caching the ACL

• Build everything for the User?

• Build everything for the Resource?

• Build everything for everything!

Page 61: Practical Applications of Zend_Acl

61

Advice

• Think about what you want to protect

• Test your solution with realistic data

• Assume that you are wrong

Page 62: Practical Applications of Zend_Acl

Questions?

62

Page 63: Practical Applications of Zend_Acl

63

Feedback

http://joind.in/2054

“...even a well presented talk by a charismatic speaker upset me.”

Page 64: Practical Applications of Zend_Acl

64

Credits

http://www.flickr.com/photos/innoxiuss/2824204305/

http://www.flickr.com/photos/carianoff/2849384997/