Im trying to test authorization in one of my controllers to make sure that only certains kind of user can access some actions. But the isAuthorized() method on AppController is never called when running tests. This is how the method looks like:
public function isAuthorized($user = null){
if(!isset($this->request->params['admin'])){
return true;
}
return in_array($user['role'], array('admin', 'root'));
}
My test function:
public function testArticlesIndex() {
$this->generate('Articles', array(
'components' => array('Auth')
));
$this->testAction('/admin/articles', array('return' => 'view'));
$this->assertEmpty($this->view);
}
I tried a lot of stuff mocking AuthComponent and not mocking it. I couldnt get a way to reproduce this situation, which would require isAuthorized(), where a user with a role other than admin or root tries to access an action on admin and fails.
When you mock the Auth component without defining what methods to mock, by default, all methods are mocked. The solution would be to mock the particular AuthComponent method you wish to mock:
$this->generate('Articles', array(
'components' => array(
'Auth' => array(
'redirect' // only mocks AuthComponent::redirect()
)
)
));
From the book:
You can choose to stub an entire class by not passing methods to it,
like Session in the example above.
See: http://book.cakephp.org/2.0/en/development/testing.html#using-mocks-with-testaction
Related
I'm attempting to make a cron shell, and the ability to use requestAction for this is crucial.
For testing purposes, I've reduced my Shell down to just this:
class CronShell extends AppShell {
public $uses = array('Cron');
public function main() {
$this->requestAction(['controller' => 'events', 'action' => 'play', 38, 0, true, true]);
echo "This will never be printed to the console, it dies before this.";
}
}
And I still cannot get this to run. It simply returns apparently successful, but actually is not. (no error) Another SO question suggested beforeFilter (and other lifecycle methods) could be the cause, but I've ensured that is not the case. See update.
Other controller/actions aren't working.
Removing the action's return doesn't help.
Using a string-based call doesn't help.
CakePHP v2.10.22
Update: It looks like the Auth component may be causing a redirect to users/login in AppController. Yet I see nowhere that I tell it to redirect here, and I don't even have a users/login action. When I remove 'Auth' from my public $components = [], the shell runs on CLI.
But I'm having trouble disabling Auth! I've tried allow(*), setting the redirects to false, even trying $this->components = ['']. I am always redirected to users/login.
Well, it turns out it wasn't the beforeFilter or any part of the lifecycle, but I did have the Auth component on my AppController like such:
public $components = [
'Session',
'Auth' => [
'loginRedirect' => ['controller' => 'worlds', 'action' => 'route']
]
];
Which, no matter what I did to allow or simulate Auth for cli, just constantly redirected me. So I reduced the above to just Session and wrote under,
if (php_sapi_name() !== 'cli')
This:
$this->components = [
'Session',
'Auth' => [
'loginRedirect' => ['controller' => 'worlds', 'action' => 'route']
]
];
And that removed Auth from the equation. I would have expected CakePHP 2's console to give me any indication it was being redirected, but I'll bet future versions handle it better.
I am moving from CakePHP 1.3 to CakePHP 2.2.2 and want to use Basic Http authentication for a simple admin area. I am just not able to make it work and I am thinking that I understood something wrong in the documentation.
From the documentation I understood I have to do something like
public $components = array(
'Auth' => array(
'authenticate' => array(
'Basic'
),
'authError' => 'You may not access this area.',
'authorize' => array('Controller')
)
);
I understand that further I need to extend the BaseAuthenticate Component to return valid user date but even with the above configuration I would expect that the browser's Http Access Dialog would open up in a popup window. But nothing like this happens, instead I am redirected to /users/login which does not exist. Why do I need a login view for Http Access? I am confused.
Add the Auth component to your controller (or to the AppController)
class ThingsController extends AppController {
var $components = array('Auth');
}
CakePHP requires a login action, so even if you use Basic authentication, where the HTTP agent is responsible for the UI to collect authentication details, you need to designate an action in some controller which will handle the login (in the Basic case, it will send the WWW-Authenticate: Basic header if the user is not authenticated yet).
You can set the AuthCompoment's $loginAction, but this defaults (and is advisable not to break conventions) to the login method in the UsersController. So, first create an empty template at View/Users/login.ctp, then add the following to your UsersController
class UsersController extends AppController {
public $components = array(
'Session',
'Auth' => array(
'authenticate' => array('Basic')
)
);
public function login() {
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Not able to login');
}
}
public function logout() {
$this->redirect($this->Auth->logout());
}
}
I know this must be something really stupid, but I am having issues with logging out. I can still see the full user Auth variable after calling Auth->logout(). In my users controller I have the standard:
function login()
{
}
function logout()
{
$this->redirect($this->Auth->logout());
}
But when I call logout, in my view I can still print the User by doing this:
$auth = $this->Session->read('Auth.User');
print "<pre>";
print_r($auth);
print "</pre>";
Am I missing something basic here? Thanks!
mine:
function logout() {
$this->Session->destroy();
$this->redirect($this->Auth->logout());
}
you have not allowed the use of the logout function, and the user is redirected instead of logged out.
in your controller containing logout the function, add this in your before filter:
$this->Auth->allow('logout');
In your app controller you must define a loginAction, in case of a not authorized entry, the view is redirected to that URL
'Auth' => array(
'loginRedirect' => array('controller' => 'products', 'action' => 'all'),
'logoutRedirect' => array('controller' => 'products', 'action' => 'index'),
'loginAction' => array('controller'=>'admins', 'action'=>'login'),
)
You'll find that if you just create a beforeFilter() function in UserController with that one line, you'll break the authorization on the Users model. That is, any user will be able to do users/add, users/edit, etc. To fix this, make sure you call AppController's beforeFilter. The complete beforeFilter() function looks like this:
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('logout');
}
If CakePHP is using PHP sessions and not rolling their own, you could just clear out the session on logout via session_destroy();. Sorry I have no CakePHP experience, so I'm just going off of an assumption.
what cake version do you have? I think you have to manually clear session in Cake 1.2. In newer Cake, if the logout function is called, it would clear out Auth.User; I'm sure on that.
I can't see a reason why this shoudn't work as I use exactly the same code...
did you confirm that the method is actually called? a simple "die('xyz')" etc before the Auth logout part can confirm that your action code is triggered.
I have an issue with cake's auth that I simply can't seem to get past (i've been debugging and trying different tutorials for the last two days). As far as I can see it should be very simple, the problem is whenever i try to login, it just refreshes the login page. I cannot for the life of me figure out why! My only conclusion is that there must be something (basic) which tutorials take for granted that I have missed.
Here are a couple of snippets:
users_controller.php
class UsersController extends AppController {
var $name = 'Users';
function beforeFiler() {
parent::beforeFilter();
}
function login() {
}
function logout() {
$this->Session->setFlash('You have successfully logged out.');
$this->redirect($this->Auth->logout());
}
}
app_controller.php
class AppController extends Controller {
var $helpers = array('Html','Form','Javascript');
var $components = array('Auth');
function beforeFilter() {
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'contents', 'action' => 'index');
$this->Auth->logoutRedirect = array('controller' => 'contents', 'action' => 'view');
$this->Auth->loginError = 'Something went wrong';
$this->Auth->allow('register', 'view');
$this->Auth->authorize = 'controller';
$this->set('loggedIn', $this->Auth->user('id'));
}
function isAuthorized() {
return true;
}
}
login.ctp
<div class="midCol short">
<h3>Login</h3>
<div class="loginBox">
<?php e($form->create('User', array('controller'=>'users','action'=>'login')));?>
<?php
echo $this->Form->input('username');
echo $this->Form->input('password');
e($this->Form->end(array('label'=>'Login', 'class'=>'loginButton button png')));?>
</div>
</div>
Any help would be greatly appreciated, this has me tearing my hair out!
Just for documentation as I had difficulties finding an answer for CakePHP 2.x on the web. This stuff needs to be "correct" in order to use Form authentication:
The config needs to be right, e.g. in your UsersController (the fields config is really only required when names differ in the DB):
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array(
'username' => 'username',
'password' => 'password'
),
)
)
)
);
You have to use the Form Helper: Form->create adds a hidden input field ("post"), and the names of the input fields generated by Form->input() follow a convention that the Auth component expects.
User->login must not pass custom data to Auth->login(). The Auth component will take the auth data from the form (= request).
Thanks for the advice, but I ended up scrapping it and building again from scratch. Not exactly sure why it was originally breaking, probably not calling inbuilt functions with American English!
The Auth component will redirect to the page before you logged in. If that page was the login page that's where it'll redirect to.
When you're testing, it's likely that you're refreshing the login page, so on successful login that's where you're redirected to. You can check this by trying to perform an Auth protected action after logging in.
This gives me a lot of headaches as well - I think the current functionality of the component is a little clumsy in that respect.
I had the exact same problem and found that I had to restart mySQL service. Once it was restarted I stopped getting the login page being redirected. Hope that helps.
Gonna throw something in here. I was having an almost unresolveable problem with cakephp authentication. Ended up doing some debugging around it and found that during my database prep I had created a field for the password which was perfectly able to store normal size passwords... but.... when you start applying password hashing you need a lot more. My code was fine, but I had to add a bunch more space into the VARCHAR field for the password before I could log in. If you're having a problem with authentication - make sure your password field is adequately sized and not getting truncated like mine was. Took me a whole day to find that. DOH!
Correct me if i am wrong but must there not be code for redirection or something inside the function of login
function login() {
}
should it not be something like
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
Checking my application, I saw that every user can access to all the actions in it.
I'm using cakePhp build-in ACL Component...
Checking permissions through terminal displays correctly is the user is allowed or not to call a certain action. But once I'm checking the application on the browser all users have access to every action. Any clue what could be doing this?
You can have CakePHP automatically handle things for you if you're using the built-in Auth and ACL components. To start, you can make sure you have an app_controller.php file in the App folder. Mine looks something like this:
<?php
class AppController extends Controller {
var $helpers = array('Form', 'Html', 'Javascript', 'Time');
var $components = array( 'Acl', 'Auth', 'Session', 'Cookie');
function beforeFilter() {
$this->Auth->authorize = 'actions';
$this->Auth->actionPath = 'controllers/';
$this->Auth->authError = ' Access Denied!';
$this->Auth->loginRedirect = '/registrations';
$this->__checkAuth();
}
private function __checkAuth() {
$currentUser = $this->Auth->user();
$currentUser = $currentUser['User'];
$this->set(compact('currentUser'));
}
}
?>
If you're authorizing 'actions' then try including that code in your app_controller.php file, or create one if you don't already have one. Then start browsing to see if it has made any changes.
If you have custom code in each controller's beforeFilter, you'll also need to add a single line of code to each controller.
function beforeFilter(){
parent::beforeFilter();
}
Any beforeFilter (even a blank one) placed in a controller will override the beforeFilter of the AppController unless you specifically call the AppController's beforeFilter using the code above.
You can also find some of the best tutorials on using CakePHP's ACL here: http://aranworld.com/article/161/cakephp-acl-tutorial-what-is-it