Download - Practical Applications of Zend_Acl

Transcript
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/