Sunday, July 4, 2010

Integrating CKEditor and CakePHP : Part 6

In Part 5 of the series, I covered how to check the CakePHP session from CKFinder to see if there was an authenticated user. There were a couple of shortcuts taken, so in this article we look at using the Auth component to check for authentication. As yet, this still does not cover ACLs in CKFinder.

Now, the previous method had CKFinder take advantage of inside information, that the authenticated user was stored in the session, and that the authentication model was the User model. We were also assuming that just because you had a session, it also meant you were allowed to upload files. While this might be try for some controllers, it might not be true for all.

While I was trying to work out how CakePHP authentication could be integrated into CKFinder, I got the chance to take another closer look at how CakePHP authentication works.

In my example code, there's very little setup for authentication, while means that as long as I have an authenticated session, I am considered authenticated for all controllers. However, this might change if I use the controller method of authentication, where each controller overrides the isAuthorized() function to determine is a user is allowed access to any actions on that controller. Or I might use the actions method of authentication, where ACLs are used to determine if a user is allowed to access a particular action (this has been my preferred method of authentication and access control, to date). There are other methods such as CRUD (ACL on actions mapped to create, read, update and delete classes), object (isAuthorized() function on any object) and model (like object, but just for models).

Suggested read for the brief overview is the CakePHP Book section on Authentication, and for a more detailed view, go straight to the AuthComponent, but look at the source, as the summary of methods might be a bit too vague.

Anyway, to integrate with CakePHP Authentication, we have to make use of the Authentication Component. Components usually expect a controller, and in this case, a controller and an action are needed. For our simple authentication, we going to say "if you're authenticated to access the contents/edit page, then you're allowed to use CKFinder". To do this, we need to communicate to CKFinder the controller and action we'll be working with, which is done via the ckeditor element. Update it with these new session variables after setting up the other session variables.

$_SESSION['controller_name'] = $this->name;
$_SESSION['controller_action'] = $this->action;


And then we update the vendors/ckfinder/config.inc.php to pick out the controller and action, start up the controller and use the Auth component to determine if the user in the session is authenticated to access the controller and the action.

<?php 
/**
 * This file is included by the CKFinder config.php, and will
 * set up the basePath and permissions, as is specific for the project
 */
 
define('EXTERNAL_APP', true);
// starts from app/webroot/ckfinder/core/connector/php/connector.php
include_once '../../../../../index.php'; // targetting app/webroot/index.php
 
App::import('Core', 'CakeSession');
 
$Session = new CakeSession();
$Session->start();
 
// What resource type are we playing with, Image or File
if (isset($_GET['type']) && $_GET['type'] == 'Images') {
 $baseUrl = $Session->read('path_to_dest_image');
 $baseDir = $Session->read('path_to_destsvr_image'); 
} else  /* if ($_GET['type'] == 'File') */ {
 // File is the default
 $baseUrl = $Session->read('path_to_dest_file');
 $baseDir = $Session->read('path_to_destsvr_file');
} 
 
function CheckAuthentication() {
 $Session = new CakeSession();
 $Session->start();
   
 $controllerName = $Session->read('controller_name');
 $controllerClass = Inflector::camelize($controllerName).'Controller';
 App::import('Controller',Inflector::camelize($controllerName));
 
 $controllerAction = $Session->read('controller_action');
  
 $params = array('action' => $controllerAction);
 $controller = new $controllerClass();
 
 $controller->params = $params;
 $controller->action = $params['action'];
  
 $controller->constructClasses();
 $controller->startupProcess(); 
  
 return !is_null($controller->Auth->user());
} 


You might notice a couple of improvements to the code. Because we're properly bootstrapping CakePHP, we now have access to the App::import() function to include CakePHP classes. There's also a chance that CKFinder will not pass the "type" on the URL, as we make sure it is set before doing anything.

Now, it this point, I wonder if there's any need to try and make use of CKFinder ACLs. Now that we have CakePHP Authentication integrated, we also have CakePHP ACLs integrated by proxy. There is one case where you might want to make use of CKFinder ACLs, and that is where you want to differentiate CRUD actions on files from CRUD actions on folders. This might be harder to set up and have feeding from CakePHP in a non-gimmicky way, since CakePHP ACLs deal with one access point for a single check, whereas CKFinder ACLs are dealing with two, files and folders.

At this point, I've just got vague suggestions for that integration. If you have set up ACLs in CakePHP, and are using groups or roles, then set the role of the user in the ckfinder element, then extract this in the vendors/ckfinder/config.inc.php and populate the $config['RoleSessionVar']. Though I'm not too sure how accurate it would be to call it a role, since a user may have multiple roles, and from a look at the ckfinder/config.php, you only get to choose one.

For the moment, that concludes my series on integrating CakePHP and CKFinder via CKEditor. I hope you found it educational. It was certainly an interesting journey for me, especially being able to bootstrap CakePHP into legacy code.

Series Index : Part 1, Part 2, Part 3, Part 4, Part 5, Part 6

No comments:

Post a Comment