![]() |
|
Snippets |
|
Let's say your site manages users who can create posts. You want to restrict the edition of a post to the administrator or the owner of the post. You would therefore like to have a security.yml file that looks like:
edit: is_secure:on credentials: [[administrator owner]]
but the problem is that the credentials are given to the user statically upon login.
The solution is therefore to give or take back credentials dynamically. As of now, the only way to achieve that is to “hijack” the getCredential action of the sfAction class.
Write the following in the action file of your post module:
// to put in the actions.class.php file function getCredential() { $this->post = $this->_retrievePost(); // retrieving the object based on the request parameters if ($this->getUser()->isOwnerOf($this->post)) $this->getUser()->addCredential('owner'); else $this->getUser()->removeCredential('owner'); // the hijack is over, let the normal flow continue: return parent::getCredential(); }
Of course you will have to write the sfUser::isOwnerOf function, which depends on your data structure. You will also have to write the _retrievePost function that returns null if no post have been found. Watch out for the isOwnerOf function: it has to return true if the post is new, otherwise users will never be able to create new posts.
Now the owner of a post can edit his post but not the others ones, except if he has administrator credentials. Everything is set up in the security.yml file. For example it is now trivial to add the same security check for the delete method.
Comments on this snippet
This snippet will open your application up to a very serious security hole because it would have you use a request parameter before it is validated. An attacker could exploit this and easily deliver an SQL injection into variables assumed to have been validated. To illustrate this, consider the following:
You have an action that is supposed to be secure and requires the owner credential and overrides getCredential like this:
If you try sending this module a request with a URI that you KNOW will trigger a validation error and you'll see the log entries still show up--which means no validation error was raised and you still used the request parameters as is.
I forgot to mention that you CAN force the parameters to be validated by calling sfActions::validate() before doing ANYTHING with the parameters.
for instance:
I don't think that a SQL Injection is possible, cause in my case a prepared statement is used. Am i right?
You're right tobias, Propel / Creole does a great job preventing injection attacks... at least better than a strip slashes or basic quote escaping. More here: http://creole.phpdb.org/trac/wiki/Documentation/FAQ
Why not implement a validation method for the edit/delete actions of said module? You would simply pull the requested id from the request, retrieve your record, and return isOwnerOf($post).
1st @Stephen Riesenberg: No, that'd interfere with the existing symfony form validation mechanism. I.e., if you do that, all of you existing validation rules won't work any more. I find it the hard way.
2nd, we can use PHP's conditional statement short-circuiting feature, as:
{{{ // to put in the actions.class.php file function getCredential() { // retrieving the object based on the request parameters if ($this->post = $this->_retrievePost() AND $this->post AND $this->getUser()->isOwnerOf($this->post)) $this->getUser()->addCredential('owner'); else $this->getUser()->removeCredential('owner');
} }}}
So that the _retrievePost function returns null if no post have been found, and the isOwnerOf function does not have to return true if the post is new.
I don't think that getCredential() is the function you want to overload. getCredential() is the function used by the security filters to see what credentials are required by a particular action, not to assign credentials to a user. I believe that preExecute() is to proper place to put that code. There're more details here: http://techstumbler.blogspot.com/2008/04/dynamic-credentials-in-symfony.html
I am not sure whether you can override the preExecute() to get the desired effect. For me it is not working. My guess is the credentials are checked before the preExecute() is invoked. Hence adding credentials dynamically within the preExecute() may not work. Did this work for anyone else? Or am I doing something stupid?
Hari Gangadharan