Securing JsTable
by Naneau
After introducing JsTable there were concerns about it’s security. While the javascript itself of course can’t change your models, creating a matching controller that gives unlimited access to your database is probably a bad idea.
So I’m going to show you how to make it (more) secure. First, JsTable can be initialized like this:
1 2 3 | var mP = new JsTable('Post', { 'urlFind': 'jstable/index/find/post/' }); |
This means that the default ‘find’ URL will be overridden with a different one. There are also options for the other URLs, you can find them in JsTable.js, they should be pretty clear.
Now, how does this help securing your code? It doesn’t. By itself. But it does allow you to route certain requests to certain controllers and/or actions. This means that you don’t have to load your model dynamically for instance, restricting it to just one. You can also easily block users from controllers. How you can do that is explained in an excellent tutorial on Zend Developer Zone.
If you want to do this in a more consistent way, Zend Acl is probably the thing for you. For our sample application, with users who write posts, I have written a simple ACL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | class Jstable_Acl_Default extends Zend_Acl { /** * constructor * * @param Zend_Auth_Adapter_DbTable $auth * @param Zend_Db_Table $row */ public function __construct($auth = null, $row = null) { $this->addRole(new Zend_Acl_Role('guest')); $this->addRole(new Zend_Acl_Role('member'), 'guest'); //guests and members $this->add(new Zend_Acl_Resource('User')); $this->add(new Zend_Acl_Resource('Post')); //our models $this->allow('guest', 'Post', 'view'); $this->allow('guest', 'User', 'view'); //guests can view Zend_Loader::loadClass('Jstable_Acl_UserAssertion'); $this->allow('member', 'User', 'create'); $this->allow('member', 'User', 'edit', new Jstable_Acl_UserAssertion($auth, $row)); $this->allow('member', 'User', 'delete', new Jstable_Acl_UserAssertion($auth, $row)); Zend_Loader::loadClass('Jstable_Acl_PostAssertion'); $this->allow('member', 'Post', 'create'); $this->allow('member', 'Post', 'edit', new Jstable_Acl_PostAssertion($auth, $row)); $this->allow('member', 'Post', 'delete', new Jstable_Acl_PostAssertion($auth, $row)); //members can edit their posts, but there's an assertion, //to make sure they can only edit their own. } } |
As you can see there are assertions. These are to make sure that users can edit their OWN posts, but not those of others. And the same goes for editing their user-row. Such an assertion looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | class Jstable_Acl_PostAssertion implements Zend_Acl_Assert_Interface { /** * auth * * @var Zend_Auth_Adapter_DbTable */ private $_auth; /** * the row we're working with * * @var Zend_Db_Table_Row */ private $_row; /** * constructor * * @param Zend_Auth_Adapter_DbTable $auth * @param Zend_Db_Table_Row $row */ public function __construct($auth, $row) { $this->_auth = $auth; $this->_row = $row; } public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $role = null, Zend_Acl_Resource_Interface $resource = null, $privilege = null) { if (!$this->_auth->authenticate()) { return false; } if ($this->_row->user_id == $this->_auth->getResultRowObject()->id) { //user id matches return true; } return false; } } |
Finally, let me show you how to use this from your controller. First, you have to authenticate a user. Zend_Auth provides methods for that. I’m going to use Zend_Auth_Adapter_DbTable, because it’s easy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $db = Zend_Registry::get('db'); //let's assume we can get a db con this way. Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable'); $auth = new Zend_Auth_Adapter_DbTable($db, 'user', 'username', 'password'); //default dbtable auth adapter $auth->setCredentialTreatment('MD5(?)'); //we don't want plain text passwords, now do we? $auth->setIdentity('Maurice'); $auth->setCredential('sesame'); //this would of course come from a form $authResult = $auth->authenticate(); if ($authResult->isValid()) { $role = 'member'; } else { $role = 'guest'; } //role |
We now have an Auth object, as well as a determined role. This could of course be refined. You can save the role in the database for instance, allowing for more types. With a role, we can check some the action is allowed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Zend_Loader::loadClass('Post'); $mP = new Post(); $rowset = $mP->find(1); //what to find would be determined by the input from JsTable $row = $rowset->current(); Zend_Loader::loadClass('Jstable_Acl_Default'); $acl = new Jstable_Acl_Default($auth, $row); //see how we pass the row to the ACL, it is needed by the assertions if ($acl->isAllowed($role, 'Post', 'edit')) { echo 'yay!'; //do something } else { echo 'nay!'; //don't do something } |
I have left out the actual action, and response encoding in JSON. The sample controller included with JsTable shows you how to do that.
Please note that there are some things left out. Just 4 types of operations is a limitation. You may want to restrict access to certain fields of a row. You can of course do this as well, by refining the ACL. I leave this to you.
Comments
None…
None…