troubleshooting Cakephp auth component not allowing allowed actions - session

Sept 2 update:
This has become a very difficult puzzle to solve. Setting up a basic auth, which is all that I want, should involve very few steps. I have done many tests, adding and removing code, reviewing the cake manual, reading tutorials and going step by step through the cakePHP 1.3 application development cookbook by Mariano Iglesias - good book. http://goo.gl/93BGw
But the problem I'm still facing is that the app controller is the only place the 'allowed' actions work. In individual controllers the parent:beforeFilter doesn't get recognized and I'm redirected back to the users login page.
Any help with this is really appreciated. What I'm wondering is how I might debug this type of problem. Are there any other configuration settings I should look at, like 'prefix routing'?
=======================
Sept 1 update:
After a lot of testing what appears to be the issue is that the 'before:filter' in individual controllers isn't being recognized. Example in the post controller:
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow = array('edit');
}
Has anyone had this happen before? I've referred to the cakePHP manual as well as many online articles and tutorials and it doesn't make any sense to me. I've even tried to build a simple application with just the users and post controller and still, the before:filter settings in each controller aren't being recognized.
=======================
Original question.
I am using the Cakephp auth component to manage an admin section. This is using version 1.3.11
The problem I'm having is that even with allowed actions in each controller, I'm being redirected to the user login page.
Here is what's in the app controller:
class AppController extends Controller {
var $components = array(
'Auth' => array(
'authorize' => 'controller'
),
'Session',
'RequestHandler'
);
public function isAuthorized() {
return true;
}
function beforeFilter(){
$this->Auth->authorize = 'controller';
$this->Auth->fields = array('username' => 'username', 'password' => 'password');
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->authError = 'Please login to view that page ';
$this->Auth->loginError =' The user name or password you entered did not work, please try again ' ;
$this->Auth->allow('display');
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'logout');
$this->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'display', 'home');
}
This is what's in the users controller:
class UsersController extends AppController {
var $name = 'Users';
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow = array('add');
}
This is what's in the posts controller:
class PostsController extends AppController {
var $name = 'Posts';
var $components = array('Session','RequestHandler', 'Email');
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow = array('edit');
}
What I do find is that after I've logged in I'm able to access the home page, as expected. Then when I go to the logout the session isn't entirely destroyed so I can go back to the 'admin' section.
I did try using $this-session('destroy'); in the logout action, but when I did the allowed actions didn't work again.
Does this make sense? Shouldn't allowed actions be independent of a current session?
Thanks, Paul

Make sure you are not using requestAction in any of your elements or views, make sure that the actions called by requestAction are allowed too.... this should fix it.

For the one when you logout and I can still access admin section: the logout() should have $this->redirect($this->Auth->logout()); It should clear the Auth session data.
Here's what I suggest for the beforeFilter() in appcontroller:
function beforeFilter(){
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'dashboard');
}
and for the pages controller: $this->Auth->allow('display', 'view');

Related

Caching problem in TYPO3 extension controller while redirecting

When delete action is called, the record is deleted in the database but when it is redirected to listAction the value are still there until caches are flushed in the TYPO3 backend.
public function listAction()
{
// some code here
}
public function deleteAction()
{
// some code here
$this->redirect('list');
}
Before the redirect, include this:
$this->cacheService->clearPageCache([$pageIds]);
where the $pageIds is an array of the UID of the pages you need to be cleared.
You can also define actions as non-cacheable actions in ext_localconf.php
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'TYPO3.' . $_EXTKEY,
'Yourplugin',
array(
'Controller' => 'list, delete',
),
// non-cacheable actions
array(
'Controller' => 'list, delete',
)
);

Cartalyst Sentinel Laravel - How do you create roles?

Im new to Cartalyst Sentinel and this concept of ACL. I've managed to create a user, perform activation and login and logout.
I'd like to take my learning to the next level. I would like 2 types of Users on this laravel app. 1 is Administrator another is Subscriber. I'm assuming my account creation method should by default create the user a subscriber.
public function postCreate() {
/* Validation */
$validation = Validator::make(Input::all(), [
'email' => 'required|email|max:50|unique:users',
'username' => 'required|min:3|max:20|unique:users',
'password' => 'required|min:6',
'password_repeat' => 'required|same:password',
]);
if ($validation->fails()) {
return Redirect('login')->withErrors($validation)->withInput();
} else {
$credentials = Input::all();
$user = Sentinel::register($credentials);
$activation = Activation::create($user);
$activation_code = $activation->code;
if ($user) {
Mail::send('emails.auth.activate', ['link' => URL::route('account-activate', [$user->id, $activation_code]), 'username' => $user->username], function($message) use ($user) {
$message->to($user->email, $user->username)->subject('Activate your account');
});
return Redirect::route('home')->with('global', 'Thank you for registering! We have sent you an email to activate your account');
}
}
}
Do i alter the code like so
$user = Sentinel::register($credentials);
$user = Sentinel::findById(1);
$role = Sentinel::findRoleByName('Subscribers');
$role->users()->attach($user);
The thing is i have not even created any roles to begin with. Where do we write that functionality? Right now i have the following Controllers
AccountController - handles activation
AuthController - handles login/logout
RegistrationController - handles registration of user
RolesController - i've not written anything inside here yet. Im a bit lost.
Please guide me. Any help is greatly appreciated.
You do not need to do a search for your user if you already registered them, the register method returns the user.
You can do the following to attach a role to a user:
$user = Sentinel::register($credentials);
$role = Sentinel::findRoleByName('Subscribers');
$role->users()->attach($user);
// OR
$user->roles()->attach($role);
you have both a user and a role object and they have a many to many relation so it doesn't matter which one you use.
You will need to create a db seeder or a method to create your permissions. But to create your Subscribers Role you will need to do the following:
Sentinel::getRoleRepository()->createModel()->create([
'name' => 'Subscribers',
'slug' => 'subscribers',
'permissions' => [
'user.view' => true,
'user.delete' => false,
// any other permissions you want your Subscribers to have
]
]);
A similar call can build your Administrator roles as well.
Your Roles Model and Controller are already built for you, you just need to access them through Sentinel, which you already have Sentinel::findRoleByName('Subscribers'); call.
Cartalyst has some pretty decent documentation about setting up roles and permissions for your users:
https://cartalyst.com/manual/sentinel#roles
https://cartalyst.com/manual/sentinel#permissions
It's just a matter of figuring out what you want each role to do or not do.
Also, you can set specific permissions per user to override the role permissions.

Laravel login redirect doesn't work

I have made the login/tregistration form. Registration works well but login redirect doesn't work. I have the following function in my controller:
public function doLogin() {
$credentials = [
'email' => Input::get('email'),
'password' => Input::get('password')
];
if (Auth::attempt($credentials)) {
return Redirect::to('/');
} else {
dd('error');
}
}
and the routes.php
Route::resource('car', 'CarController');
Route::get('users', 'UserController#index');
Route::post('users/register', array('uses' => 'UserController#store'));
Route::post('users/signin', array('uses' => 'UserController#doLogin'));
Route::get('users/logout', array('uses' => 'UserController#doLogout'));
Route::get('/', 'CarController#index');
CarController
public function index() {
$cars = DB::select('select * from cars');
$result = DB::select('select c.*, i.sgs, i.tpl, i.kasko, i.inter_permis from cars as c left join insur_docs as i on i.car_id = c.id');
$date = Carbon::now();
$limit_date = Carbon::now()->addMonths(1);
return View::make('pages.index', array(
'cars' => $cars,
'result' => $result,
'date' => $date,
'limit_date' => $limit_date,
));
}
The problem is that it doesn't redirects to index page just refresh the page. If not correct credentials it shows "error" else if correct credentials it just refresh page and doesn't redirects. I f I replace redirect with success message it shows it. I have the same code localy and login with redirect is ok, but in google app engine (my project online) doesn't redirect.
The example you have used wouldn't actually redirect the user for two reasons.
The use of Redirect::route() excepts the parameter passed to be the name of a route, eg one defined like so
Route::get('/', ['as' => 'home', 'uses' => 'YourController#yourMethod']);
To redirect here you would use Redirect::route('home').
You aren't actually returning the redirect. Any response for a route, whether it be within a controller method or a closure, must be returned using the return keyword.
So to correct your code, it'd be like this:
public function doLogin() {
$credentials = [
'email' => Input::get('email'),
'password' => Input::get('password')
];
if (Auth::attempt($credentials)) {
return Redirect::to('/');
} else {
dd('error');
}
}
I moved the credentials to an array as it looks tidier and it makes it easier to read when displaying on this site, so you don't have to do that, but it may make things easier for you.

Cakephp auth component with two models session

I have two cakephp2 applications running on same database, but having different Auth tables and different $this->Auth->userModel values accordingly.
Authentication works well and users from one app can't log into other.
BUT.. as apps uses same CAKEPHP session cookie, this happens:
when user from app 'one' logs in, it can access any Auth protected action in app 'two'!
I will probably use different user roles and cookie names.
But still, why Auth component is ignoring Auth->userModel settings when checking the session? Is there a way to configure it to work right in this situation?
Thanks in advance for any suggestions.
If not configured otherwise, AuthComponent will write the authenticated user record to the Auth.User session key in CakePHP 2. But it can be changed:
AuthComponent::sessionKey
The session key name where the record of the current user is stored. If unspecified, it will be "Auth.User".
(In CakePHP 1.3 this was different: Auth.{$userModel name})
So, if your apps share a Session, which they do, if cookie name and Security.salt match, the logged in record will be shared.
There are two possibilities to solve this:
Separate the logins
Simply set a different AuthComponent::sessionKey for your two models. This will allow them to keep the logged in user separately
Separate the sessions
Configure different Cookie names and Salts for both apps, so their sessions cannot override each other. This is probably the cleaner solution, because it also covers the risk of other session keys being double-used.
I have a similar issue which is why I've started a bounty on this question. Basically I have a public facing part of the application which lets users login from one table and an administrative part of the application which lets admins login using a different table. My AppController looks something like this:
public $components = array(
'Session',
'Auth' => array(
'autoRedirect' => false,
'authenticate' => array(
'Form' => array(
'userModel' => 'User'
)
),
'loginAction' => array('controller' => 'users', 'action' => 'login'),
'loginRedirect' => array('controller' => 'users', 'action' => 'overview'),
'logoutRedirect' => array('controller' => 'users', 'action' => 'loggedout')
)
);
and I have another AdminController where I have this:
public $components = array(
'Session',
'Auth' => array(
'authenticate' => array(
'CustomForm' => array(
'userModel' => 'Admin'
)
),
'loginAction' => array('controller' => 'admin', 'action' => 'login'),
'loginRedirect' => array('controller' => 'admin', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'home', 'action' => 'index')
)
);
But as mentioned in this question, sessions from the two don't get along and overwrite each other. What's the best way to overcome this?
Extend the Model/Datasource/Session/DatabaseSession.php session handler with something like MyDatabaseSession and overwrite the write and read methods. Maybe simply copy the existing code of both methods and add something like
'app_id' => Configure::read('App.appId')
to the read() conditions and do the same in the write method. And do not forget to add the field to your session database schema and to configure the session to use your handler.
<?php
App::uses('DatabaseSession', 'Model/Datasource/Session');
class ExtendedDatabaseSession extends DatabaseSession {
public function read($id) {
$row = $this->_model->find('first', array(
'conditions' => array(
'app_id' => Configure::read('App.appId'),
$this->_model->primaryKey => $id)));
if (empty($row[$this->_model->alias]['data'])) {
return false;
}
return $row[$this->_model->alias]['data'];
}
public function write($id, $data) {
if (!$id) {
return false;
}
$expires = time() + $this->_timeout;
$record = compact('id', 'data', 'expires');
$record[$this->_model->primaryKey] = $id;
$record['app_id'] = Configure::read('App.appId');
return $this->_model->save($record);
}
}
I do not know your app, so were you write the app id to the config data is up to you, bootstrap or beforeFilter() maybe. You should add it before the session gets initialized I think or you'll need to re-init the session or something. I leave it up to you to look the right point up. :)

Handle permissions from login action with Session

I'm building a cakePHP application where students can just manage their own data(like a profile).
My idea to deny the access to other students to the 'view' action is this:
(students_controller.php)
function view($id = null) {
//We check the permissions so a student cannot browse other student's pages
$studentid=$this->Student->field('id',array('user_id'=>$this->Session->read('Auth.User.id')));
if (!$id) {
$this->Session->setFlash(__('Invalid student', true));
$this->redirect(array('action' => 'add'));
}
if ($this->Auth->user('id') <> ($this->data['Student']['user_id'])){
$this->Session->setFlash(__('You are not authorized to access this page,not allowed', true));
$this->redirect(array('controller' => 'users','action' => 'logout'));
}
$this->set('student', $this->Student->read(null, $id));
}
The problem comes (I guess) because in my login action I have this code:
(users_controller.php)
function login() {
$studentid= $this->User->Student->field('id',array('user_id'=>$this->Session->read('Auth.User.id')));
$studentformactivated=$this->User->Student->field('form_activated',array('user_id'=>$this->Session->read('Auth.User.id')));
if ($this->Session->read('Auth.User') ) {
if($this->Session->read('Auth.User.group_id') == '1')
$this->redirect(array('controller'=>'specializations', 'action' => 'index'));
else if ($this->Session->read('Auth.User.group_id') == '2')
$this->redirect(array('controller'=>'specializations', 'action' => 'index'));
else if ($this->Session->read('Auth.User.group_id') == '3')
if ($studentformactivated == '1')
$this->redirect(array('controller'=>'students', 'action' => 'view',$studentid));
else
$this->redirect(array('controller'=>'students', 'action' => 'add'));
$this->Session->setFlash('You are logged in!');
$this->redirect('/', null, false);
}
}
So, everytime a student logs in is redirected to the 'view' action (if the 'add' action was done), and in the view action we are taking information from the session, and it seems while doing login action we cannot take this information ( the sql log says SELECT Student.id FROM students AS Student WHERE user_id IS NULL LIMIT 1, so it's not getting user_id).
Now , the questions:
First of all, I would like to know what do you think about this way of managing the permission control, because it's the first time I do it and maybe there are other simpler ways to do it.
Am I right supposing that I cannot take information from the User Session just after login action?
Would it be a good solution to do in the view function something like :
if (user_is_logged_in){
if ($this->Auth->user('id') <> ($this->data['Student']['user_id'])){
$this->Session->setFlash(__('You are not authorized to access this page,not allowed', true));
$this->redirect(array('controller' => 'users','action' => 'logout'));
}
}
It's the only thing I see to solve it. But I tried with ($this->Session->check('Auth.User')) for the (user_is_logged_in) condition and it didn't work...
Thank you in advance!
ALf.
-----------------------------------------EDIT-------------------------------
After discussing it, the solution seems to be adding the code to the AppController beforeFilter. It's not redirecting well still, maybe it is because of the arguments passed? I ran out of ideas...
function beforeFilter($id = null) {
//Configure AuthComponent
$this->Auth->authorize = 'actions';
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'students', 'action' => 'add');
$this->Auth->actionPath = 'controllers/';
$this->Auth->allowedActions = array('display');
if( $this->Auth->User('id') != $id ) // bad, redirect them to safe page
$this->redirect(array('controller' => 'users','action' => 'logout'));
}
Assuming the profile is the the Student model, why can't you just do this?
class StudentsController extends Model {
function view( $id = null ) {
if( $this->Auth->User('id') != $id ) // bad, redirect them to safe page
$this->redirect( '/badaccess/denied' );
}
}
Since $id is the id of the student profile, just check it against the id of the logged in user.
not sure if this will help, but i'm currently working on a system that required extra info be available at login. in my case it was from the same users table (just extra fields) but may work with other tables/models. i would assume you would have to put a var $uses = array('User, 'Student'); at the top to do this.
in the users controller i have the following:
function login(){
...
$this->Session->write('Auth.User.type', $this->User->field('type'));
$this->Session->write('Auth.User.isapproved',
$this->User->field('isapproved'));
...
}
obviously, you'd have to change these to something like
$this->Session->write('Auth.User.student_id',
$this->User->Student->field('id',array('user_id'=>$this->User->field('id')));
or something along those lines
then you should be able to access it by if( $this->Auth->User('student_id') != $id ), similar to the answer above.
not sure if this will work or not as i'm currently looking up suggestions to my own problem when i stumbled on this, so i don't have time to try to see if i've done it right :)

Resources