When isAuthorized = false the user is redirected to '/' is there a way to change this. I want to redirect to the user dashboard (/users/dashboard) with a flash message saying 'Access prohibited' or something like that.
Cheers!
public function isAuthorized($user) {
if (isset($user['role']) && $user['role'] === 'admin') {
return true; //Admin can access every action
}
return false; // The rest don't
}
If your isAuthorised variable is being evaluated in your controller.
You can call the redirect function.
$this->redirect(array('controller' => 'users', 'action' => 'dashboard'));
If you are actually inside the users controller allready, just call
$this->redirect(array('action' => 'dashboard'));
If not, where are you checking the isAuthorised value?
This is not an ideal solution. However it seems that there is no way to do this with the current built in AuthComponent
Edit: Added code as an example.
public function isAuthorized($user) {
if (parent::isAuthorized($user)) {
return true;
}
// Authorised actions
if (in_array($this->action, array('dashboard'))) {
return true;
}
// Will break out on this call
$this->redirect(array('controller' => 'users', 'action' => 'dashboard'));
return false;
}
I think the best way is to use exception and extends like that :
AppController.php
public function isAuthorized($user) {
throw new ForbiddenException(__('You are not authorized to access.'));
}
AnotherController.php
public function isAuthorized($user) {
if (isset($user['role']) && $user['role'] === 'admin') {
return true;
}
return parent::isAuthorized($user);
}
With this code you are able to manage the roles and the error.
If they are being logged out you can send them where you want with:
$this->Auth->logoutRedirect
I would personally use:
$this->Auth->authError = "You are not authorized to access.";
In order to redirect them to root with a flash message notifying them of the error.
Is a wrong behavior of AuthComponent.
In a nutshell: if the url is visited by a link, the framework is able to reconstruct the path and then redirect to the referring page. Otherwise (by direct entry into a url bar) it fails and it redirects to the home page.
The "bug" is documented and it will be corrected in a future release.
See: http://cakephp.lighthouseapp.com/projects/42648/tickets/591-inconsistent-redirect-behaviour-by-auth-acl
I do a little better than #deep55.
isAuthorized() method can throw an exception no problem, but i think that the inheritance of the Controllers would permit us to improve authorization algorithm using first AppController.isAuthorized(), not last.
So, here is my solution, assuming that I use a user model called Utilisateur, and a role model called Role.
AppController :
/**
* Parent method
*/
public function isAuthorized($user){
App::uses('Utilisateur','Model');
$User = new Utilisateur();
$isAdmin = $User->hasRole(10,$user['id']);
if ($isAdmin) {
return true;
}
}
/**
* Reject unauthorized actions
*/
public function rejectRequest(){
$errorMessage = __("Sorry, you can't do this.");
if ($this->isRest()) {
throw new ForbiddenException($errorMessage);
} else {
$this->Auth->authError = $errorMessage;
$this->Auth->flash['params']['class'] = 'alert-danger';
}
return false ;
}
Utilisateur model :
/**
* hasRole method return true if the user belongs to the correct role group
*/
public function hasRole($role_id, $user_id){
if (!isset($user_id)) {
if (!empty($this->id)) {
$user_id = $this->id ;
} else throw new Exception("Error, parameter $user_id is missing", 1);
}
$user = $this->find('first',array(
'conditions' => array('Utilisateur.id' => $user_id),
'fields' => array('id'),
'contain' => array('Role.id')
));
$roles = $user['Role'];
foreach ($roles as $r) {
if ($role_id == $r['id']) {
return true;
}
}
}
And last, in a specific controller :
/**
* Child method
*/
public function isAuthorized($user){
if (parent::isAuthorized($user)) {
return true;
}
if ( false ) {
return true ;
}
if ( false ) {
return true ;
}
return $this->rejectRequest() ;
}
For Cake version 2, as written in documentation for AuthComponent:
AuthComponent::$unauthorizedRedirect
Controls handling of unauthorized access. By default unauthorized user is redirected to the referrer URL or AuthComponent::$loginRedirect or ‘/’. If set to false a ForbiddenException exception is thrown instead of redirecting.
you can configure AuthComponent to redirect you to custom page in one place using unauthorizedRedirect property.
Just set it in the place where you configure Auth as a component
'Auth' => array(
... other settings...,
'unauthorizedRedirect' => '/users/dashboard'
)
After being redirected, you can print error message defined by authError property
echo $this->Session->flash();
echo $this->Session->flash('auth');
but it will be the same message for any authentication or authorization error.
Related
My app is developed in CakePHP 3.x.
I use CakedDC Users plugin, and it works fine, except when the user wants to change his password, and click on Submit.
Let's say we have a Profile ID = 52606b3f-c72d-4485-9c76-3b0f8
The Edit page has a url like this:
localhost/my_app/profile/52606b3f-c72d-4485-9c76-3b0f8
The changePassword page has a url like this:
localhost/my_app/users/users/change-password/52606b3f-c72d-4485-9c76-3b0f8
When I click on Submit, it redirects to the profile page, but the ID is lost:
localhost/my_app/profile
and I get this error message:
Record not found in table "users" with primary key [NULL]
I think the reason is that the ID is not passed. And I don't find where and how to fix it.
Any help please ?.
When id is not passed, the id is taken from the logged in user. You can take a look at src/Controller/Traits/ProfileTrait.php. Could you debug $this->Auth->user('id')?
Also, you could customize the redirect url after changing the password. Configure::write('Users.Profile.route', [{url}]), see src/Controller/Traits/PasswordManagementTrait.php Ln44.
I don't remember my initial code, but, after months, I found the solution.
in src/Controller/Traits/ProfileTrait.php, set $redirect = Configure::read('Users.Profile.route');
public function changePassword()
{
$user = $this->getUsersTable()->newEntity();
$id = $this->Auth->user('id');
if (!empty($id)) {
$user->id = $this->Auth->user('id');
$validatePassword = true;
//#todo add to the documentation: list of routes used
$redirect = Configure::read('Users.Profile.route');
} else {
$user->id = $this->request->session()->read(Configure::read('Users.Key.Session.resetPasswordUserId'));
$validatePassword = false;
if (!$user->id) {
$this->Flash->error(__d('CakeDC/Users', 'User was not found'));
$this->redirect($this->Auth->config('loginAction'));
return;
}
//#todo add to the documentation: list of routes used
$redirect = $this->Auth->config('loginAction');
}
$this->set('validatePassword', $validatePassword);
if ($this->request->is('post')) {
try {
$validator = $this->getUsersTable()->validationPasswordConfirm(new Validator());
if (!empty($id)) {
$validator = $this->getUsersTable()->validationCurrentPassword($validator);
}
$user = $this->getUsersTable()->patchEntity($user, $this->request->data(), ['validate' => $validator]);
if ($user->errors()) {
$this->Flash->error(__d('CakeDC/Users', 'Password could not be changed'));
} else {
$user = $this->getUsersTable()->changePassword($user);
if ($user) {
$this->Flash->success(__d('CakeDC/Users', 'Password has been changed successfully'));
return $this->redirect($redirect);
} else {
$this->Flash->error(__d('CakeDC/Users', 'Password could not be changed'));
}
}
} catch (UserNotFoundException $exception) {
$this->Flash->error(__d('CakeDC/Users', 'User was not found'));
} catch (WrongPasswordException $wpe) {
$this->Flash->error(__d('CakeDC/Users', '{0}', $wpe->getMessage()));
} catch (Exception $exception) {
$this->Flash->error(__d('CakeDC/Users', 'Password could not be changed'));
}
}
$this->set(compact('user'));
$this->set('_serialize', ['user']);
}
In my app I have a user with 3 different roles and each one with their own respective page that they can visit. I would like to redirect them to their own respective home page when they try to access any pages that they should not be viewing, but are unable to do so.
I found the below thread with a very similar problem, but after modifying and putting the code in AppController::beforefilter, I am unable to get the desired result.
How to redirect each type of user to a different page when access is denied?
Updated with code from the thread above:
public function beforeFilter(Event $event)
{
$user_role = $this->Auth->user('role');
if ($user_role == "admin")
{
$redirectController = 'Pages';
$redirectMethod = 'adminhome';
}
elseif ($user_role == "teacher")
{
$redirectController = 'Pages';
$redirectMethod = 'teacherhome';
}
elseif ($user_role == "studentparent")
{
$redirectController = 'Pages';
$redirectMethod = 'parenthome';
}
$this->Auth->unauthorizedRedirect = array(
'controller' => $redirectController,
'action' => $redirectMethod,
'prefix' => false
);
}
I've been working on an application using CakePHP 2.6. We have a class called AuthUser which builds upon the functionality of AuthComponent and allows us to check permissions against our roles for sections in our database.
However I have noticed that our "isAuthorised" function ignores the $this->Auth->allow() which means actions that shouldn't need authorisation are being caught by our checks and this needs to be updated to check properly.
Is it possible to access the $this->Auth->allow() array of actions and if so how would someone go about accessing it?
Below I have included the "isAuthorised" function from the AuthUser class:
public function isAuthorised($controllerName = null) {
//Admin has access to everything
if (AuthUser::isAdmin() === true) {
return true;
}
$roles = array();
//Get the roles allowed for the section
$results = AppController::runStoredProcedure('spGetCurrentSectionRolesForSectionBySectionName', array( $controllerName ));
if (isset($results) && is_array($results)) {
foreach ($results as $row) {
if (isset($row['RoleName'])) {
array_push($roles, $row['RoleName']);
}
}
}
//Check if authenticated user has permission to current controller (is one of the allowed roles)
$userRoles = AuthComponent::user('role');
if (isset($userRoles) && is_array($userRoles)) {
foreach ($userRoles as $key => $value) {
if ($value == true) {5
if (in_array($key, $roles)) {
return true;
}
}
}
}
return false;
}
Please try this
pr($this->Auth->allowedActions);
This will list you all auth->allow() function name that are defined in $this->Auth->allow()
I try to do a contact form with Symfony 2.4.1. My form is a simple contact one without an entity.
The following error happens in my handler :
FatalErrorException: Error: Call to undefined method Symfony\Component\Form\Form::render() in C:\Program Files\wamp\www\sf2\src\Open\OpcBundle\Form\Handler\ContactHandler.php line 75
And the form handler code :
<?php
// src/Open/OpcBundle/Form/Handler/Handler.php
namespace Open\OpcBundle\Form\Handler;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
class ContactHandler {
protected $request;
protected $form;
protected $mailer;
public function __construct(Form $form, Request $request, $mailer) {
$this->form = $form;
$this->request = $request;
$this->mailer = $mailer;
}
public function process() {
if ('POST' == $this->request->getMethod()) {
$this->form->handleRequest($this->request);
$data = $this->form->getData();
$this->onSuccess($data);
return true;
}
return false;
}
protected function onSuccess($data) {
$message = \Swift_Message::newInstance()
->setContentType('text/html')
->setSubject($data['sujet'])
->setFrom($data['courriel'])
->setTo('me#gmail.com')
->setBody($this->render('OpcOpenBundle:Opc:Mails/contact.html.twig',
array('ip' => $request->getClientIp(),
'nom' => $data['nom'],
'msg' => $data['msg'])
)
);
$this->get('mailer')->send($message);
$request->getSession()->getFlash()->add('success', 'Your email has been sent! Thanks!');
return $this->redirect($this->generateUrl('contact'));
}
}
So the problem is that I don't have an object/instance for the renderView() method to call the template mail with setBody in the function onSuccess()
Which object/instance should I use for ?
Thanks
PS: sorry for my english, i'm French!
It's Symfony\Bundle\TwigBundle\TwigEngine class. service name is 'templating'.
BTW, why is you handler is not service?
I am using a custom module to restrict a role to a url in drupal 7 the code is as follows:
<?php
// Implements hook_init()
function restrict_access_init() {
$restrictions = restrict_access_restrictions();
global $user;
foreach ($restrictions as $path => $roles) {
// See if the current path matches any of the patterns provided.
if (drupal_match_path($_GET['q'], $path)) {
// It matches, check the current user has any of the required roles
$valid = FALSE;
foreach ($roles as $role) {
print implode("','",$user ->roles);
if (in_array($role, $user->roles)) {
$valid = TRUE;
break;
}
}
if (!$valid) {
drupal_access_denied();
}
}
}
}
function restrict_access_restrictions() {
// This array will be keyed by path and contain an array of allowed roles for that path
return array(
'path/path' => array('admin'),
);
}
?>
This does restrict access just fine but it then renders the page un-styled after the footer.
Any ideas why this may be happening?
I'm at a lost end with this now.
i needed to add module_invoke_all('exit'); exit(); under drupal_acess_denied();
e.g.
drupal_acess_denied();
module_invoke_all('exit');
exit();