Cakephp 2 ACL ERR_TOO_MANY_REDIRECTS - php

I am trying to implement the ACL tutorial found here:
http://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html
I followed all the instructions, however when I try to go to [my_site]/users/add ERR_TOO_MANY_REDIRECTS error.
I found this on the Cakephp website:
This happens when for example we have an element that receives data
from a method of a controller, and the implementation of this method
requires conducting the login would create an infinite loop that
eventually will cause the browser to decline redirects
And they suggest this as the fix:
function beforeFilter () {
$ this -> Auth -> allow ( 'CONTROLLER_NAME' );
}
Which doesn't seem to work.
If I change the AppController from this:
public function beforeFilter() {
$this->Auth->allow('index', 'view', 'login', 'add');
}
to:
public function beforeFilter() {
$this->Auth->allow('*');
}
I dont get the error anymore but get redirected to [my_site]/users/login
Any suggestions as to what I am doing wrong that I can't view the User-Add page?
TIA!
UserController:
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('add');
}
Login function (UsersController):
Public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash(__('Invalid username or password, try again'));
}
}
}
Auth Component Loader:
public $components = array(
'Session',
'RequestHandler',
'Auth' => array(
'loginRedirect' => array('controller' => 'projects', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'pages', 'action' => 'display', 'home')
)
);

The error you are getting has nothing to do with ACL, but with the Auth component denying access to your UsersController add and login functions, to which it is trying to redirect the user. Make sure the add and login functions are public, by adding this line in your UsersController (rather than your AppController):
public function beforeFilter() {
$this->Auth->allow(array('add', 'login'));
}
The loop you are now encountering is because the add and login functions are not public and therefor the loop looks like: add -> Unauthorized -> login -> Unauthorized -> login ... and so on.

I finally managed to solve the problem with help Brian:
Do you have an requestAction() code? If so, try adding this in
AppController::beforeFilter()
if (isset($this->params['requested'])) $this->Auth->allow($this->action);

Please change your beforeFilter()'s Auth->allow('_CONTROLLER_NAME') to:
public function beforeFilter(){
$this->Auth->allow();
}
Hope that works for you!

Related

Authentication with Doctrine in Zend Framework 2 Good Practices

Lets say that my page has 10 sections, in 6 of them I have to check if the user is logged in, and if not, redirect him to the "login/register" page.
I found myself repeating this code in the controller of those 6 pages:
public function actionthatneedsauthAction()
{
$sl = $this->getServiceLocator();
$authService = $sl->get('doctrine.authenticationservice.orm_default');
$user = $authService->getStorage()->read(); //is the user logged in?
if ($user) { //auth successful
//-------------/*CODE FOR THIS SPECIFIC CONTROLLER GOES HERE*/--------
return new ViewModel(array(
'user' => $user,
'somethingelse' => $somethingelse
));
} else { //auth denied
return $this->redirect()->toRoute(user, array('action' => 'login'));
}
}
I tried to encapsulate that into a service, called islogged (this is a model, not a controller), but I couldn't make it work because I couldn't find a way to redirect to a controller from inside a model, I only know how to redirect to a controller via another controller.
So in my usermanager.php I had a function like this one:
public function islogged()
{
$sl = $this->getServiceLocator();
$authService = $sl->get('doctrine.authenticationservice.orm_default');
$user = $authService->getStorage()->read(); //is the user logged in?
if ($user) { //auth successful
return $user;
} else {
/*
redirect to the login screen, dont know how to do it,
this code doesnt work here:
return $this->redirect()->toRoute(NULL, array(
'controller' => 'user',
'action' => 'login'
));
*/
}
}
so the idea was that in my controllers I only had to write:
$user = islogged();
and all the code repetition I mentioned won't be necessary anymore.
Is it a good practice what I tried to do with the usermanager.php islogged function?
If it is a good practice, how am I supposed to redirect to a controller from inside a model?
If is not a good practice, which will be the way to avoid all that code repetition that I'm having in my controllers?
I know that I can put the authentication step into the onboostrap() but in that case the auth will be triggered for all of my pages, and I just want it in some of them.
I would advise you to implement Doctrine Authentication with official DoctrineModule Authentication described in the docs folder of the repo.
Read this - Link to DoctrineModule Authentication
Then you can handle your Authentication check via the zf2 own Controller and View Helpers identity. See example in the docs here.
I use this ACL Module on my apps: https://github.com/ZF-Commons/zfc-rbac
My Customer overview controller then looks as so:
<?php
namespace RoleBasedCustomer\Controller;
use RoleBasedUser\Service\AuthenticationService;
use RoleBasedUser\Service\UserService;
use RoleBasedUser\Controller\AbstractMultiModelController;
class OverviewController extends AbstractMultiModelController
{
public function __construct(
AuthenticationService $authService,
UserService $userService
) {
$this->authService = $authService;
$this->userService = $userService;
}
public function indexAction()
{
if ( ! $this->authService->hasIdentity() ) {
return $this->redirect()->toRoute('customer/login');
}
}
}
The only thing i had to do is replace these two lines:
$authService = $this->getServiceLocator()
->get('doctrine.authenticationservice.orm_default');
$user = $authService->getStorage()->read(); //is the user logged in?
with this one:
$user = $this->identity();

CakePHP 2.1.1 > Redirection error with Basic Authentification (users/login)

I am trying to make a basic user login authentification in CakePHP(2.1.1) ,and it seems to work because it redirects well to users/login (in the url field) BUT when i'm on this page, Firefox says that:
The page isn't redirecting properly. Firefox has detected that the server is redirecting the request for this address in a way that will never complete. This problem can sometimes be caused by disabling or refusing to accept cookies."
I encounter the same kind of error with Google Chrome, so I think it's really a problem that comes from the coding of CakePHP side, especially a redirection loop.
In app\Controller\AppController.php I have put :
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
var $components = array('Auth');
}
?>
In app\Controller\UsersController.php:
<?php
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());
}
}
?>
in app\View\Users\login.ctp :
<?php
echo $this->Form->create('User', array('action' => 'login'));
echo $this->Form->input('login', array('label' => 'Login : '));
echo $this->Form->input('pass', array('type' => 'password', 'label' => 'Password : '));
echo $this->Form->end('Connexion');
?>
I really don't know where to put new code or modification to stop this redirection loop... :s
Thks in advance!
I see two places in your code where redirects can happen at UserController->login() and UserController->logout(). Make sure you have Firebug installed along with Firephp. Include the Firephp core library:
require_once('FirePHPCore/FirePHP.class.php');
$firephp = FirePHP::getInstance(true);
then add these lines in login() and logout():
$firephp->log('Login() has been fired.');
$firephp->log('Logout() has been fired.');
Turn on the net panel in Firebug, run your code again and if "Login() has been fired" shows up in the Firebug console output you know that it is the login() function that is doing the redirecting. Try having firephp log wherever you want to know if a script is going. You can also have it log variables to the console so you can see if things are getting set properly.
Sorry I couldn't diagnose the root of your problem, but this advice should help you troubleshoot.
In the AppController, you should set these three values like below, it might be because of the login action redirect to itself (login page), you should set loginRedirect to other page than login page
public function beforeFilter() {
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'home');
}

CakePHP form authentication for normal requests with basic authentication for JSON

I'm attempting to to build a web application that can be view by a user in a browser but also has an API for developers to interface with my application. My question is how do I change the authentication based on what type of request it is in CakePHP?
I would like my application to prompt users using the site with form authentication but when a request comes in with a '.json' to use basic authentication.
I've tried this in my AppController:
class AppController extends Controller {
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array(
'controller' => 'journeys',
'action' => 'index'
),
'logoutRedirect' => array(
'controller' => 'pages',
'action' => 'display', 'home'
)
),
'RequestHandler'
);
public function beforeFilter() {
if($this->params['ext'] == 'json') {
$this->Auth->authenticate = array('Basic');
} else {
$this->Auth->authenticate = array('Form');
}
$this->Auth->allow('display');
}
}
I have checked that the clause in the beforeFilter if works and it does but I seem to get redirected to my form authentication no matter what URL I try and access in my application
The login function in my UsersController file looks like:
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash(__('Username or password is incorrect'), 'default', array(), 'auth');
}
I have read the docs on the CakePHP website but I can't seem to find an example that will help me. Any help would be appreciated.
EDITED FOR CORRECTION TO CODE AND MORE INFORMATION
I have carried on looking at this problem and Ive noticed that if I log the value of:
$this->Auth->authenticate
in the beforeFilter it says that it is basic but it's still sending me to the form log in.
Excerp from the docs (see http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html):
Because basic and digest authentication don’t require an initial POST
or a form so if using only basic / digest authenticators you don’t
require a login action in your controller. Also you can set
AuthComponent::$sessionKey to false to ensure AuthComponent doesn’t
try to read user info from session.
So you do not require a login action. You could check for the authentication method in your users controller and skip the login action if the authentication method is "Basic".

REST api with CakePHP

I'm trying to create a RESTful api with CakePHP that will add a user when a POST request is sent to /users.json. After the user is created, the client will be redirected to the page with the JSON representation of the user. The code I have for the controller is:
class UsersController extends AppController {
public $components = array('RequestHandler');
public function view($id) {
$user = $this->User->findById($id);
$this->set(array(
'user' => $user['User'],
'_serialize' => 'user'
));
}
public function add() {
if ($this->User->save($this->data)) {
$this->redirect(array('action' => 'view', 1)); //using 1 just to test
} else {
print_r($this->User->validationErrors);
$this->set(array(
'errors' => $this->User->validationErrors,
'_serialize' => array('errors')
));
}
}
}
I have added Router::mapResources('users') and Router::parseExtensions('json') to routes.php. However, when I send a post request using Chrome's REST console plugin, I get a response of "{errors:[]}" and no new user is created. When I use curl, a user is created but I don't get a json representation of the user after. Any idea what's going on?
If you have not already done, then retry after creating app/View/user/json/index.ctp
With following content:
<?php
return json_encode(compact());
?>

CakePHP 2.0 Basic Authentication always gives 302 redirect instead of 401 unauthorized

I have basic authentication set up in a simple CakePHP 2.0 application. I first set up the application to use regular form authentication, then I added the following line to the beforeFilter() of my AppController.php to enable basic http authentication:
$this->Auth->authenticate = array('Basic');
Here's the full AppController:
<?php
class AppController extends Controller {
public $components = array(
"Session",
"Auth" => array(
'loginRedirect' => array('controller'=>'users','action'=>'index'),
'logoutRedirect' => array('controller'=>'users','action'=>'index'),
'authError' => "You are not authorized to view this page.",
'authorize' => array('Controller'),
)
);
public function isAuthorized($user) {
return true;
}
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('index','view');
$this->set('logged_in', $this->Auth->loggedIn());
$this->set('current_user',$this->Auth->user());
$this->Auth->authenticate = array('Basic');
}
}
?>
Ideally I'd like one specific controller (a controller which will expose an API for use with a mobile device) out of the entire application to use only Basic HTTP authentication, and the rest of the controllers to behave like a normal web application.
Currently if I pass incorrect credentials to the controller I get an HTTP 302 response, when I'd really like a HTTP 401 to be passed back. How can I do this?
*edited for typo
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('index','view');
$this->set('logged_in', $this->Auth->loggedIn());
$this->set('current_user',$this->Auth->user());
if($this->name == 'Specific') {
// for the specific controller
$this->Auth->authenticate = array('Basic');
} else {
// everything else
}
}
checkout KVZ's rest plugin it may be of interest
https://github.com/kvz/cakephp-rest-plugin
I haven't tried 2.0 yet, but usually what Auth does is use setFlash to set the error message, then redirect you somewhere to show you that message. That's probably why you're getting the 302 redirect.
How about manually setting the header and then exiting?
e.g.
public function login() {
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirect());
} else {
header("HTTP/1.0 401 Unauthorized");
exit;
}
}
In cakePHP 2.x you can create a custom status code e.g.
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirect());
} else {
throw new MissingWidgetHelperException('You are not authorized to view this page.', 401);
}

Categories