CakePHP: using a different users table - cakephp

After setting up the simple Cakephp login concept I would like to let CakePHP use a different table to check the users and the login. I can't figure out how to change the table name within the Auth-component.
Below my basic Controller. How can I let Cakephp know it has to look into a different database table?
class AppController extends Controller {
public $components = array(
'Session',
'Auth'=>array(
'loginRedirect'=>array('controller'=>'users', 'action'=>'index'),
'logoutRedirect'=>array('controller'=>'users', 'action'=>'index'),
'authError'=>"You can't access that page",
'authorize'=>array('Controller')
)
);
public function isAuthorized($user) {
return true;
}
public function beforeFilter() {
//$this->Auth->allow('index', 'view');
$this->set('siteCategory', 'home');
$this->set('logged_in', $this->Auth->loggedIn());
$this->set('current_user', $this->Auth->user());
}
}

Best Solution:
This is something that's done in the User model with the useTable property.
I.e. in app/Model/User.php you should have something like this:
class User extends AppModel {
public $useTable = 'table_name';
//... rest of Model stuff here
}
Alternative:
Alternatively you can specify a different model to be used for the user, although I don't think that's what you're asking for. If I'm wrong there though, just set the userModel value like this:
public $components = array(
'Auth'=>array(
'authenticate'=>array(
'Form' => array('userModel' => 'ADifferentUserModel')
)));

Related

In CakePHP 2, how do you tell the Paginator component which model to use?

Given the following working example,
// ProductsController.php
<?php
App::uses('AppController', 'Controller');
class ProductsController extends AppController {
public $helpers = array('Html', 'Form');
public $components = array('Session', 'Paginator');
public $paginate = array(
'limit' => 5
);
public function index() {
$this->Product->recursive = -1;
$this->set('products', $this->paginate());
}
}
?>
What tells Paginator which model to use when it sets the variable? Currently this seems to be automatically using the Products model, but I don't really understand why. Is it just part CakePHP's magic that it selects the model that has the same name as the current controller? And if so, how would I tell Paginator to use some other model? Like if I wanted to also paginate the User model on the same page, how would I implement that?
According to Cakephp2,
$this->paginate() by default works on the current model.
if you want to use other model on the same page you can do like this:
$this->paginate('User');
You can all pass other parameters like:
$this->Paginator->settings = array(
'fields' => array('User.*'),
'order' => array('User.username' => 'asc'),
'limit' => 10,
);
$this->set('users', $this->paginate('User'));
Reference: Pagination
So, after some trial and error, it seems like by default Paginator will just use whatever model is associated with the controller via the naming conventions (the 'User' model is associated with the 'UsersController' controller, and so on).
The first argument of $this->paginate() will accept a different model if you want to use one, for example $this->paginate('Dinglehopper'), but the specified model needs to be available to the controller/action in order for it to work. In order to do that you need $this->loadModel('Dinglehopper'); inside the action where $this->paginate('Dinglehopper') is called.
So in the hypothetical situation where you want to use and paginate the model 'Dinglehopper' inside your 'Products' controller you would do,
// ProductsController.php
<?php
App::uses('AppController', 'Controller');
class ProductsController extends AppController {
public $helpers = array('Html', 'Form');
public $components = array('Session', 'Paginator');
public $paginate = array(
'limit' => 5
);
public function index() {
$this->loadModel('Dinglehopper');
$this->Product->recursive = -1;
$this->set('dinglehoppers', $this->paginate('Dinglehopper'));
}
}
?>
$this->loadModel('Dinglehopper'); makes the model 'Dinglehopper' available to the action, and then $this->paginate('Dinglehopper') returns the paginated Dinglehopper model.

CakePHP mapping HTTP request to the correct action

I have been trying to set up RESTful functionality in my app and I face a problem of whatever action I call it is router to that particular controllers index action. I have been trying to call add and view actions, but they are just not being routed properly. Here is the resposne I get when trying to call the view action:
{"code":404,"url":"\/application\/rest_customers\/54.json","name":"Action RestCustomersController::54() could not be found."}
And here is the way it is all set up in. RestCustomersController:
class RestCustomersController extends AppController {
public $uses = array('Customer');
public $helpers = array('Html', 'Form');
public $components = array('RequestHandler');
public function index() {
}
public function view($id=null){
$customer = $this->Customer->find('first', array(
'conditions'=>array('Customer.id'=> $id)));
$this->set(array(
'customer' => $customer,
'_serialize' => array('customer')
)); }}
Here are the routes:
Router::mapResources('customers');
Router::parseExtensions('json', 'xml', 'csv', 'pdf');
And here os the AppControllers beforeFitler function:
if(in_array($this->params['controller'], array('rest_customers'))){
$this->Auth->allow();
$this->Security->unlockedActions = array('add','index', 'view');
}else{
$this->Security->unlockedActions = array('add', 'edit');
$this->Auth->allow('index', 'view');
$this->set('logged_in', $this->Auth->loggedIn());
$this->set('current_user', $this->Auth->user());
}}
Any help is very much appreciated.
You are creating REST routes for a controller named Customers, but you are actually accessing a controller named RestCustomers, so what you are experiencing is the expected behavior as there simply are no REST routes connected for the RestCustomers controller.
You should either rename your controller to CustomersController, or change your mapping to use the correct name so that it will connect the routes to RestCustomersController
Router::mapResources('rest_customers');

cant understand how this function works $this->Auth->login() in cakephp 2.x

i am new in cakephp. i am making a logging system in cakephp 2.x .. i am stuck here
UsersController.php extending AppController
public function login()
{
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Your email/password combination was incorrect');
}
}
}
the problem is that it is not checking that whether the email and password typed by the user is correct or not..and is logging the user in without checking .. i have never used the auth component before ... so i am feeling hard to grasp that how this function is checking the email and password from the database as on the internet and the cakephp website they are using this function to check whether the user has logged in successfully or not./i always used sql queries but i dont know how this component is working .. please correct this function and explain me where it is checking the email and password from the database
here is my
AppController
class AppController extends Controller {
public $components = array(
'Session',
'Auth'=>array(
'loginRedirect'=>array('controller'=>'users', 'action'=>'admin'),
'logoutRedirect'=>array('controller'=>'users', 'action'=>'admin'),
'authError'=>"You can't access that page",
'authorize'=>array('Controller')
)
);
public function isAuthorized($user) {
}
public function beforeFilter() {
$this->Auth->allow('index');
}
}
class AppController extends Controller {
// added the debug toolkit
// sessions support
// authorization for login and logut redirect
public $components = array(
'Session',
'Cookie',
'Auth' => array(
'authenticate' => array('Form' => array('fields' => array('username' => 'email', 'password' => 'password'),)),
'authorize' => array('Controller'))
);
public function isAuthorized($user) {
return true;
}
}
please replace the co in app controller.

Change CakePHP settings for tests

The Code
AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Auth' => array(
'authorize' => array('Controller')
),
'Session'
);
}
PostsController.php
<?php
App::uses('AppController', 'Controller');
class PostsController extends AppController {
public function isAuthorized() {
return $this->Auth->user('role') == 'admin';
}
public function add() {
$this->set('some_var', true);
}
}
PostsControllerTest.php
<?php
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
CakeSession::write('Auth.User', array(
'id' => 2,
'username' => 'joe_bloggs',
'role' => 'user',
'created' => '2013-05-17 10:00:00',
'modified' => '2013-05-17 10:00:00'
));
}
public function testAddWhileLoggedInAsNonAdminFails() {
$this->testAction('/posts/add/', array('method' => 'get'));
$this->assertTrue($this->vars['some_var']);
}
public function tearDown() {
parent::tearDown();
CakeSession::destroy();
}
}
The Problem
Right now, the "testAddWhileLoggedInAsNonAdminFails" test passes. It should fail. The issue is that redirects do not exit/halt the simulated request.
Partial Solution
I can fix the problem by modifying "AppController.php" and "PostsControllerTest.php" like so:
Modified AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Auth' => array(
'authorize' => array('Controller'),
// ***** THE FOLLOWING LINE IS NEW *****
'unauthorizedRedirect' => false
),
'Session'
);
}
Modified PostsControllerTest.php
<?php
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
CakeSession::write('Auth.User', array(
'id' => 2,
'username' => 'joe_bloggs',
'role' => 'user',
'created' => '2013-05-17 10:00:00',
'modified' => '2013-05-17 10:00:00'
));
}
// ***** THE FOLLOWING 3 LINES ARE NEW *****
/**
* #expectedException ForbiddenException
*/
public function testAddWhileLoggedInAsNonAdminFails() {
$this->testAction('/posts/add/', array('method' => 'get'));
}
public function tearDown() {
parent::tearDown();
CakeSession::destroy();
}
}
The problem with this solution is it modifies the behavior of the real website too. I'm looking for a way to set the Auth component's unauthorizedRedirect property to false only when tests are being run. How can I do this?
Changing the behavior of your code to make tests work right is not really a good idea.
The correct answer to this question is that it's not a very good question, and what you really should do is test each function separately.
For the isAuthorized function, you should do:
<?php
class PostsControllerTest extends ControllerTestCase {
public function testIsAuthorized() {
$Posts = $this->generate('Posts');
$user = array('role' => 'admin');
$this->assertTrue($Posts->isAuthorized($user));
$anotherUser = array('role' => 'saboteur');
$this->assertFalse($Posts->isAuthorized($user));
}
public function testAdd() {
$this->testAction('/posts/add/', array('method' => 'get'));
$this->assertTrue($this->vars['some_var']);
}
}
The core concept behind unit testing is breaking down your app into the smallest pieces possible, and testing each in isolation. Once you have your unit tests sorted out, you can work on integration tests that cover more than one function, but many projects never reach that stage, and that's okay. The redirect issue can be interesting to work with, but you can mock out controller::redirect as described in this blog post. It's a bit old but still useful.
Did you check the book? http://book.cakephp.org/2.0/en/development/testing.html#testing-controllers
When testing actions that contain redirect() and other code following
the redirect it is generally a good idea to return when redirecting.
The reason for this, is that redirect() is mocked in testing, and does
not exit like normal. And instead of your code exiting, it will
continue to run code following the redirect.
It exactly describes your problem.
I haven't tested this but try it, the manual says the controller is already mocked when using ControllerTestCase so you should be able to expect it:
$this->controller->expects($this->at(0))
->method('redirect')
->with('/your-expected-input');
Taking a look at the ControllerTestCase class might reveal how the controller is exactly mocked and set up. Alternatively you could just fall back to the regular CakeTestCase and set the controller mocks up by yourself.
Another alternative would be to extend your controller you want to test and override the redirect() method, not calling the parent but setting the first arg to a property like Controller::$redirectUrl. After your action call you can then assertEqual the properties value. But this still requires you to return after the redirect call in your controller. Also this won't work either when using ControllerTestCase because it would mock your overriden method.

CakePHP 2.0.5 Auth and User Model Recursive issue

In Cake 2.0.5 when logging in using the Auth component, it would seem Cake is retrieving all related models; and with many associations, logging in takes a long time.
This problem was first identified here in this ticket but the "solution" given doesn't mean a lot, and I can't find anything else in the documentation.
Using the FormAuthenticate class in 2.0 you can subclass and add
whatever recursive level you feel is appropriate fairly easily.
Has anyone experienced this, and have a fix?
Below - sample code:
Standard login method:
public function login() {
$this->User->recursive = -1; // does nothing
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Invalid username or password.');
}
}
And the query cake is producing for my app:
SELECT `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`created`, `User`.`modified`, `Band`.`id`, `Band`.`name`, `Band`.`genre`, `Band`.`location`, `Band`.`influences`, `Band`.`founded`, `Band`.`bio`, `Band`.`created`, `Band`.`modified`, `Band`.`status`, `Band`.`website`, `Band`.`email`, `Band`.`contact_number`, `Band`.`user_id`, `Member`.`id`, `Member`.`user_id`, `Member`.`first_name`, `Member`.`last_name`, `Member`.`display_name`, `Member`.`dob`, `Member`.`gender`, `Member`.`bio`, `Member`.`influences`, `Member`.`band_id` FROM `users` AS `User` LEFT JOIN `bands` AS `Band` ON (`Band`.`user_id` = `User`.`id`) LEFT JOIN `members` AS `Member` ON (`Member`.`user_id` = `User`.`id`) WHERE `User`.`username` = 'admin' AND `User`.`password` = 'dcec839a9258631138974cbccd81219f1d5dfcfa' LIMIT 1
As you can see it's retrieving every field, and joining every model. My app only has 2 additional associations, but you can see how this might be an issue with very complex apps.
When really, it should just be the users table. Setting recursive appears to do absolutely nothing.
You can use recursive option for Auth component.
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array('recursive' => -1)
)
)
or in beforeFilter method:
$this->Auth->authenticate = array('Form' => array('recursive' => -1));
What Mark is suggesting is to extend the FormAuthenticate class, or essentially override it.
Create a new file app/Controller/Component/Auth/ExtendedFormAuthenticate.php
This is the basic structure of the code - I've left in the important bit where the recursive level is set in the _findUser method:
App::uses('FormAuthenticate', 'Controller/Component/Auth');
class ExtendedFormAuthenticate extends FormAuthenticate
{
public function authenicate(CakeRequest $request, CakeResponse $response) {
// foo
}
protected function _findUser($username, $password)
{
// bar
$result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions,
'recursive' => -1
));
// fooBar
}
}
I've created a Gist with the whole lot in: https://gist.github.com/1565672
Oh, almost forgot, you'll need to setup the AuthComponent to use the extended class.
public $components = array(
'Auth'=> array(
'authenticate' => array(
'ExtendedForm'
)
),
);
What about using Containable and override find at model?
Modifying Containable fields required in beforeFind callback?

Resources