This blog is about the PHP solutions I have to think of when I'm developing systems almost every single day...

Sunday, March 30, 2008

Using proper Auth Component in Cakephp

A while back (when I had very little experience with cakephp), I wrote about how to go about setting user authentication in your cakephp. Basically you have to login to do anything at all in my whole web app. So we manually set the user id and stuff in the sessions and check it every now and then. Now I'm more experienced and have finally been able to google my way to a better understanding of how it can be done using the auth component that comes with cakephp.

First is that it is a component. And you will want it everywhere in your application. So where else do you write it but in your app/app_controller.php file.


var $components = array('Acl','Auth');

Make sure you add Acl before Auth because Auth uses some functions in Acl which is not initialized unless it is already loaded. So load Acl first.

Okay, then you have to set up some settings for your Auth component. It is set in the beforeFilter function of your app_controller.

function beforeFilter() {
$this->Auth->loginAction=array('controller'=>'users','action'=>'login');
$this->Auth->loginRedirect=array('controller'=>'users','action'=>'alert');
$this->Auth->logoutRedirect=array('controller'=>'users','action'=>'login');
$this->Auth->loginError=__('Invalid username or password',true);
$this->Auth->authorize='controller';
}

Okay. Some explanation. loginAction is which page to display when your user will login. Of course it has to be a page with a form with the fields username and password. Then loginRedirect is the default page to go to if the users come directly to the login page. Let me explain. You see, when parts of your application is protected with this component, and the user has not logged in, the user will be redirected automatically to the login page to allow the user to login. If it is a successful login s/he will be directed back to the page s/he requested for before. Unless the user went directly to the login page. Because then the component don't know where to send the user. So we set it using loginRedirect. logoutRedirect is the page to send the user to once they have successfully logged out. In my case I'd send them straight back to the login page. loginError is of course the error message that will be flashed if the user didn't login successfully. And finally authorize will determine who will authorize the user. It can be set to 'controller', 'actions', 'crud', array('model'=>'name') or 'object'. To learn more see here.

Then if you want your views to be able to know who is logged in you've got to send the data over. I do it in the beforeRender function like this.

function beforeRender(){
if($this->Auth->user()){
$this->set('auth_user',$this->Auth->user());
}
}

Pretty straight forward. Previously I've set the controller to authorize the user access, so we will need the isAuthorized function defined in our controller. Define it in our app_controller like this.

function isAuthorized(){
return true;
}

Then overwrite in your model controllers for more sophisticated authentication. Basically I've just set default is to allow access to the controller.

Okay. That's it for the app_controller. Now for the user controller.

function login(){
}

function logout(){
$this->Session->SetFlash(__('Successfully logged out',true));
$this->redirect($this->Auth->logout());
}

Make sure the login view is created. But no other code is necessary for the login. And very litte required for the logout function. And basically that is it. Very cool. Of and one more thing. The auth component will actually hash the password with some other measures of security. So you have to make sure at least the first user uses the same password generated by $this->Auth->password('password') so that you can login. Or some other way to get the password in. I've read that when you save the user data the auth component will do it for you, haven't tested that yet. But sure to try soon.

Wednesday, March 26, 2008

The use of recursive property

After much experimenting, I've finally found the reason for the recursive property. If you set it to 0 then it will not query into all the rest of the models it is bound with (you know has, belongsto and stuff like that). And so your query will be less.

Tuesday, March 25, 2008

Fixing habtm in cakephp

Okay... One of the cool things about cakephp is it's habtm (Has and belongs to many) relationship. It is great and works more the less out of the box if all your settings are correct (Hint: use cake bake to get it right). But I found some undefined behavior if you are editing a form with more than 1 habtm field. If you select the second item in the first list, it will also save the second item in your second list even if you did not select it. After much bashing my head over it, I finally tracked it all the way to the file cake/libs/model/models.php. It looks like the $newValues used at line 1301 is not unset for the second loop. So just add:


unset($newValues);


Right after the insertMulti line. And it will be working alright.

But there is another problem. If you use the 'multiple'=>'checkbox' option in your form, you will get an error if you uncheck all the options. That is because it will not set the $value for line 1284. Add the whole block in an:

if(is_array($value)){
....
}


And all will be okay. Hope that helps

Optimize cakephp with cache

Wow.. Seems it is quite easy to do caching with cakephp. Just check out:
http://bakery.cakephp.org/articles/view/optimizing-your-cakephp-elements-and-views-with-caching

Hope to be able to use it soon.

Monday, March 24, 2008

Joomla: Hacking the ACL

I needed to allow managers and others to change their own password but only the back-end is accessible. I didn't want to enable login at the front end. But anything below administrator, not even the user manager menu comes out, so there is nothing to edit. Finally after much googling I found: http://demo.joomlaworks.gr/content/view/23/32/ which basically tells me that I can change the includes/gacl.class.php to hand hack the acl. By adding:


$this->_mos_add_acl( 'administration', 'manage', 'users', 'manager', 'components', 'com_users' );


I was able to show the user menu for managers and they can see the list of users. But they cannot edit anything. Cannot add anything. Cannot delete anything. So I finally checked directly into the file controlling the user data administrator/components/com_users/admin.users.php

In the function checkUserPermission I changed it to:

if ( !$allowActionToMyself && $id == $my->id ){
$msg .= 'You cannot '. $actionName .' Yourself!';
} else if (($id != $my->id && $obj->gid == $my->gid && !in_array($my->gid, array(24, 25))) || ($obj->gid && !in_array($obj->gid,getGIDSChildren($my->gid)))) {
$msg .= 'You cannot '. $actionName .' a `'. $this_group .'`. Only higher-level users have this power. ';
}


I added the $id!=$my->id part in the elseif. That would allow the action if the action is allowed to self (ie the $allowActionToMyself is set). Finally I was able to edit. But when I tried to save it didn't work. Finally I checked into the saveUser function and changed it to:

$msg = checkUserPermissions( array($userIdPosted), 'save', true );


Meaning that I allow user to save themselves. So now my managers can edit their own profile and only their profile. Turned out better than I thought.

Wednesday, March 5, 2008

Using checkboxes instead of selects in habtm

CakePHP is seriously cool. How easy is it to use check boxes rather than the default select box in a habtm field? Just this:


echo $form->input('FieldModel',array('multiple'=>'checkbox'));


And that's it!!! Sweet... :)