Ok this is the scenario:
Admin logs in
Admin logs out
Normal user logs in
Redirected to admin page!
If I delete cookies stored however before logging in as a normal user, login works as expected. Another strange thing I have noticed is that the admin redirect in my login function is not actually ran when the normal user logs so they are being redirected elsewhere.
Here is some code:
Users Controller:
<?php
public function login() {
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->Auth->login()) {
//if login successful update logged in User as login does not use recursive find
$this->User->id = $this->Auth->user('id');
$this->User->read();
$this->Auth->login($this->User->data);
if($this->Login->isRole($this->Auth->user(), 'Admin')) {
//redirect admins to admin page, not ran when error occurs!!
$this->redirect(array('controller' => 'users', 'action' => 'index', 'admin' => true));
} else {
//isAuthorised in AppController takes care of redirect to registration page if required
$this->redirect($this->Auth->redirect());
}
} else {
//if login unsuccessful
$this->Session->setFlash(
__('Invalid username or password, please try again.'),
'alert',
array(
'plugin' => 'TwitterBootstrap',
'class' => 'alert-error'
)
);
}
}
//set layout
$this->layout = 'not_logged_in';
//set title
$this->set('title_for_layout', 'Login');
}
public function logout() {
//logout
$this->redirect($this->Auth->logout());
}
public function isAuthorized($user) {
if(parent::isAuthorized($user)) {
//call parent method for redirect and admin permission
return true;
}
switch ($this->action) {
case 'add':
case 'resetPassword':
case 'login':
//logged in users cannot access registration, login or password reset pages
return false;
break;
case 'add_role':
//check user is finshing registration or has a role request accepted
return (!$this->Login->isRegistrationComplete($user) || $this->Login->isRoleRequestAccepted($user));
break;
default:
//all loogged in users can access the rest of User controller
return true;
break;
}
}
php?>
App Controller:
<?php
public $components = array(
'Session',
'Auth' => array(
'className' => 'UserAuth',
'loginRedirect' => array('controller' => 'users', 'action' => 'view'),
'logoutRedirect' => array('controller' => 'users', 'action' => 'login'),
'authorize' => array('Controller'),
'authenticate' => array('Blowfish'),
'authError' => 'Please login.',
'loginError' => 'Invalid Username or Password entered, please try again.',
),
'Login'
);
public function isAuthorized($user) {
//set values needed for all logged in pages
$this->set('loggedIn', $user);
$this->set('role', $user['User']['role']);
if($this->Login->isAccessing($this, 'users', 'logout')) {
//never prevent user from logging out
return true;
}
if($this->Login->isRole($user, 'Admin')) {
//admin can access every action
return true;
} elseif ($this->request->prefix === 'admin') {
//only admins allowed on admin pages
throw new Exception('You do not have permission to access this page.', 1);
}
//get user role and requested role
$roles = $this->Login->getRolesCurrentAndNew($user);
if($this->Login->isRoleRequestAccepted($user)) {
//user has an accepted role request
$controller = 'users';
$action = 'add_role';
if($this->Login->isRedirectRequired($this, $controller, $action)) {
//if user is already accessing registration this check prevents redirect loops
if ($this->Login->isRegistrationComplete($user)) {
//display flash based on registration status
$this->Session->setFlash(
__('Your request for the role of '. strtolower($roles['new']) . ' has been accepted, please enter additional details.'),
'alert',
array(
'plugin' => 'TwitterBootstrap',
'class' => 'alert-success'
)
);
} else {
$this->Session->setFlash(
__('Please complete your registration.'),
'alert',
array(
'plugin' => 'TwitterBootstrap',
'class' => 'alert-success'
)
);
}
$this->redirect(array('controller' => $controller, 'action' => $action));
}
} elseif (!$this->Login->isRegistrationComplete($user)) {
//user has not registered yet and role request is not accepted
if(!$this->Login->isRegistrationComplete($user)) {
//user has not completed registration yet, awaiting approval
throw new Exception('Your registration request is awaiting approval from a member of the admin team.', 1);
}
}
return false;
}
?>
Routes config:
Router::connect('/login', array('controller' => 'users', 'action' => 'login'));
Router::connect('/logout', array('controller' => 'users', 'action' => 'logout'));
Router::connect('/register', array('controller' => 'users', 'action' => 'add'));
Router::connect('/register/role', array('controller' => 'users', 'action' => 'add_role'));
Router::connect('/', array('controller' => 'users', 'action' => 'view'));
Router::connect('/admin', array('controller' => 'users', 'action' => 'index', 'admin' => true));
Router::connect('/users/manageRoleRequest', array('controller' => 'roleRequests', 'action' => 'add'));
I eventually realised that CakePHP automatically takes you to the last page you were viewing when you log out and in on the same machine, regardless of whether or not you're logging in as a different user or not.
From the docs:
property AuthComponent::$logoutRedirect
The default action to
redirect to after the user is logged out. While AuthComponent does
not handle post-logout redirection, a redirect URL will be returned
from AuthComponent::logout().
Defaults to AuthComponent::$loginAction.
Meaning:
$this->Auth->logout()
will return a string url. Nothing more.
You wrap it with a redirect to redirect to this url:
public function logout() {
$this->redirect($this->Auth->logout());
}
So, no, Cake will only redirect you to the specific action you specified via "loginRedirect" setting.
In your case your login view.
I tried the following and it seemed to work for me
public function logout() {
$logout_redirect = $this->Auth->logout();
$this->Auth->redirectUrl($this->Auth->redirect());
return $this->redirect($logout_redirect);
}
Related
I am trying to deny some actions for non-admin users in my controllers. So in the controllers I am using this code:
public $components = array('Auth');
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
if($this->Auth->user('role_id')==1 or $this->Auth->user('role_id')==2){ //role: 1 admin, 2 project manager
$this->set('is_admin', true);
}
else
{
$this->Auth->deny(['index','delete']);
$this->set('is_admin', false);
}
$this->set('my_id', $this->Auth->user('id'));
}
So now anytime a user that is not admin or project manager tries to perform index or delete actions, is redirected to the "Method Not Allowed" error page. But I would like to return to the previous page with a message: "You are not authorized to perform this action".
I tried to set 'unauthorizedRedirect' => $this->referer() in the AppController:
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'unauthorizedRedirect' => $this->referer()
]);
But didn't work. The only way I managed to achieve that is by using this code in the beforeFilter function of the controller:
if(!($this->Auth->user('role_id')==1 && !$this->Auth->user('role_id')==2 && ($this->request->action === 'index' || $this->request->action === 'delete')){
$this->Flash->error(__('You are not authorized to perform this action'));
return $this->redirect(['controller' => 'Users', 'action' => 'index']);
}
But it doesn't seem the proper way to do this in all the controllers that I want to deny some actions. Is there another way?
Cakephp provides isAuthorized function for the same. You can take advantage of it.
Just define isAuthorized in your App controller or in separate controllers(if you want to put separate conditions for each controllers.)
public function isAuthorized($user)
{
$roleArray = [1, 2]; // your role ids array
if ( !in_array($user['role_id'], $roleArray) && in_array($this->request->getParam('action'), ['index', 'delete'])) { // put your conditions here
return false;
}
return true;
}
Cakephp -> Authentication and Authorization -> Authorization (who’s allowed to access what)
I added a prefix admin and changed the controller from Userscontroller to LoginController now the localhost:8765/admin works fine but localhost:8765/admin/login/check_login sbut when i submit my form it give me a error Error: The requested address '/admin/login/check-login' was not found on this server where check_login is my auth action
Router
Router::prefix('admin', function ($routes) {
$routes->connect('/', ['controller' => 'Login', 'action' => 'display','login']);
});
App Controller
public function initialize() {
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'username',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Login',
'action' => 'check_login',
'prefix' => 'admin'
],
]);
// Allow the display action so our pages controller
// continues to work.
$this->Auth->allow(['display']);
Check Login Action
public function check_login() {
if ($this->request->is('post')) {
$admin = $this->Auth->identify();
if ($admin) {
$this->Auth->setUser($admin);
$result['status'] = 'success';
echo json_encode($result);
}
$result['status'] = 'failure';
$result['message'] = 'Your username or password is incorrect';
echo json_encode($result);
die;
}
}
Thanks in advance
Why are you complicating? The easiest way:
Create Admin folder in you Controller app folder.
Inside - create UsersController.php and make login method. All functions inside Admin folder will be using only with Admin prefix in URL.
Next - You can map the URL /admin/login to your login() action of users controller using following route:
Router::prefix('admin', function ($routes) {
// Because you are in the admin scope,
// you do not need to include the /admin prefix
// or the admin route element.
$routes->connect('/login', ['controller' => 'Users', 'action' => 'login']);
});
This is the easiest way.
I just started trying to learn cakephp auth, i just copied and pasted the code, trying to understand it. I can't figure out what is directly the redirect
//app controller
//is empty i know in some cases you put it here, i'm just tested it in the user controller
//user controller
public $components = array('Paginator',
'Session',
'Auth' => array(
'loginAction' => array(
'controller' => 'users',
'action' => 'login',
'plugin' => 'users'
),
'authError' => 'Did you really think you are allowed to see that?',
'authenticate' => array(
'Form' => array(
'fields' => array('username' => 'email')
)
)
)
);
public function beforeFilter() {
$this->Auth->allow('index', 'view');
}
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirectUrl());
// Prior to 2.3 use
// `return $this->redirect($this->Auth->redirect());`
} else {
$this->Session->setFlash(
__('Username or password is incorrect'),
'default',
array(),
'auth'
);
}
}
}
I understand the before filter makes sense, it only allows index and view, i have another controller called admin which redirects to the login page if your not logged in
but for somme reason it keeps redirecting to users/users/login, i want it to go to users/login? How do i fix this?
you just have to put the controller wherever you go and the action
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
return $this->redirect(array('controller' => 'users', 'action' => 'login'));
}
} else {
$this->Session->setFlash(__('Invalido nombre de usuario o contraseña'));
}
}
When i follow $this->Auth->allow('login'); this approach, we are not able to redirect to dashboard page. If i follow $this->Auth->allow(); this approach we are able to redirect to dashboard page.
But i need to use first approach for login.
I have a AppController.php which is performed Login Action.
Below is the AppController.php Code
function beforeFilter() {
Security::setHash('md5');
// Authenticate
$this->Auth->authorize = 'Controller';
$this->Auth->autoRedirect = false;
$subdomain = substr( env("HTTP_HOST"), 0, strpos(env("HTTP_HOST"), ".") );
if($this->params['controller']=="moderator")
{
//$this->Auth->allow(); // if i use this one my code is working fine.
$this->Auth->allow('login');
$this->Auth->loginAction = array('controller' => 'moderator', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'moderator', 'action' => 'dashboard');
$this->Auth->loginError = 'No username and password was found with that combination.';
$this->Auth->logoutRedirect = array('controller' => 'moderator', 'action' => 'login');
AuthComponent::$sessionKey = 'Auth.Admin';
}
}
when we are login the page it will goto ModeratorController.php controller page and then check the usename and password using Auth , if username & password correct it will call the dashboard function.
Below is the ModeratorController.php Code
class ModeratorController extends AppController {
public $components = array('RequestHandler','Session',
'Auth'=>array(
'authenticate' => array(
'Form' => array(
'fields'=>array(
'username'=>'username',
'password'=>'password'
),
'userModel'=> 'Admin'
),
),
'loginAction' =>array(
'Controller' => 'moderator',
'action' => 'login'
),
'loginRedirect'=>array('Controller'=>'moderator', 'action'=>'index'),
'logoutRedirect'=>array('Controller'=>'moderator', 'action'=>'index'),
'authError'=>"You can't access that page",
'authorize'=>'Controller',
'loginError'=> 'Login error',
)
);
public $helpers = array('Js' => array('Jquery'),'Html','Paginator');
function beforeFilter() {
//used version cake 2.3.0.
parent::beforeFilter();
}
// for moderator login process
public function login() {
if($this->Session->read('Auth.Admin.admin_id'))
{
$this->redirect('dashboard');
}
if($this->request->data)
{
$this->Admin->set($this->data);
if($this->Admin->validates())
{
if($this->Auth->login()){
//$this->redirect('/moderator/dashboard'); // result: redirect to moderator/login
//$this->redirect(array('controller' => 'moderator', 'action' => 'dashboard')); // result: redirect to moderator/login
//$this->redirect($this->Auth->redirect()); // result: redirect to moderator/login
//$this->redirect(array('action' => 'dashboard')) // result: redirect to moderator/login
//$this->redirect('dashboard12'); // result: redirect to moderator/dashboard12 and give me 404. That result is correct
//$this->redirect('/dashboard'); // result: redirect to /dashboard and give me 404. That result is also correct
$this->redirect('dashboard')) // result: redirect to moderator/login
}
else
{
$this->Session->setFlash(__('Invalid email or password, try again'));
}
}
}
}
public function dashboard()
{
echo "I am in dashboard";
exit();
}
}
Thanks
I see a few things that are technically wrong:
When building in CakePHP you should follow its standards
You should have moderators as your controller and not moderator
You make reference to dashboard.php
Is that a controller? or are you referring to the method
If you have a controller name ModeratorsController.php and a method there name dashboard(), then you can redirect like this:
$this->redirect(array('controller' => 'moderators', 'action' => 'dashboard'));
or you could even redirect like this
$this->redirect('/moderators/dashboard');
If the dashboard.php file you make reference is actually the controller, which in this case should be DashboardsControler.php, then you have to redirect to
$this->redirect('controller' => 'dashboards');
or
$this->redirect('/dashboards');
$this->redirect('dashboard');
Should be
$this->redirect(array('action' => 'dashboard'));
if this action is also in the dashboards controller.
Never use strings for links and redirects to internal pages of your app, only external links should use the string notation.
Also, is there any specific reason why you're not using the CookieComponent? Your cookie content won't be encrypted at all and you set a ton of cookies. Use the component.
I've put a considerable amount of digging into this but I haven't been able to figure out what the best method would be.
I have an employee management system where everyone who logs in is either an "employee", a "supervisor" or a "manager".
At the moment, I display different versions of pages just fine depending on what their rank is. However, regular "employees" can still get to pages they shouldn't if they manually enter the URL. According to CakePHP's documentation, it says all pages are restricted by default, and you have to grant access to each one. But I haven't granted access and it seems all the pages are accessible.
What is the best method for page access?
Thanks!
Edit: Here is the configuration of the AppController:
public $components = array(
'DebugKit.Toolbar',
'Session',
'Auth' => array(
'authenticate' => array(
'Form' => array(
'userModel' => 'Employee'
)
),
'loginAction' => array(
'controller' => 'employees',
'action' => 'login',
//'plugin' => 'users'
),
'loginRedirect' => array('controller' => 'employees', 'action' => 'dashboard'),
'logoutRedirect' => array('controller' => 'employees', 'action' => 'login'),
'authError' => 'You must be logged in to see that.'
)
);
And then there is the isAuthorized() method which always is set to return false:
public function isAuthorized($user = null) {
// Any registered user can access public functions
/*if (empty($this->request->params['admin'])) {
return true;
}*/
// Only admins can access admin functions
/*if (isset($this->request->params['admin'])) {
return (bool)($user['role'] === 'admin');
}*/
// Default deny
return false;
}
Crete two tables in database
resources (id, controller, action) (This will contain controller names and action names.)
permission (roll_id, resource_id)
In isAuthorized() function
If roll is admin, then return true.
Else Check the following.
using $this->request->controller Get current controller name.
using $this->request->action Get current action name.
Get resource_id from resources table for current controller and action.
Check record in permission table for resource_id and roll_id.
If record exist then return true.
Else at the end it is returning false by default.
Your code is missing this in the AppController.php
class AppController extends Controller {
public function isAuthorized($user) {
return true;
}
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => ['fields' => ['username' => 'email']]
],
'authorize' => ['Controller'],
]);
//some other code here
}