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 :)
Related
Basically in trying to create an inbox message which "read details" should redirect the user to a custom controller, however i can see the desired url in the browser for a second and then it redirects to the dashboard; this is how, currently, im trying to achieve that:
$myId = $myJson['id'];
$title = "Title of my notice";
$description = $myJson['text'];
$url= Mage::helper("adminhtml")->getUrl('My_Module/Controller/index', array('id' => $myId));
$sendingMessage = Mage::getModel('adminnotification/inbox')->addNotice($title,$description,$url);
The code above successfully adds the message to the inbox, however as i said before, i can see the desired URL in the browser before it gets redirected to the dashboard.
I'm accessing the same controller from another one and it does it as expected, the one that is actually working is a Grid and it looks something like this:
$this->addColumn('action',
array(
'header' => __('Answer'),
'width' => '100',
'type' => 'action',
'getter' => 'getId',
'actions' => array(
array(
'caption' => __('Answer'),
'url' => array('base'=> '*/Controller'),
'field' => 'id'
)),
'filter' => false,
'sortable' => false,
'index' => 'stores',
'is_system' => true,
));
So, am i missing something here ?
BTW, is there any way to make the "read details" link to open in the same page instead of a new tab?
==================================================================
UPDATE
Disabling the "Add Secret Key to URLs" in the security options allowed me get it work, however i would like to make use of the secret keys.
The URLs i'm generating in the first code block actually have a key/value in the URLs, they look something like this:
https://example.com/index.php/mymodule/Controller/index/id/3963566814/key/f84701848a22d2ef36022accdb2a6a69/
It looks like you're trying to generate an admin URL. In modern versions of Magento, admin urls must use the adminhtml front name, using the Magento Front Name Sharing technique (described in this article). That's must as in if you don't, the URLs won't work. Magento removed the ability to create non-adminhtml URLs in the backend.
Second, here's where Magento generates the secret keys
#File: app/code/core/Mage/Adminhtml/Model/Url.php
public function getSecretKey($controller = null, $action = null)
{
$salt = Mage::getSingleton('core/session')->getFormKey();
$p = explode('/', trim($this->getRequest()->getOriginalPathInfo(), '/'));
if (!$controller) {
$controller = !empty($p[1]) ? $p[1] : $this->getRequest()->getControllerName();
}
if (!$action) {
$action = !empty($p[2]) ? $p[2] : $this->getRequest()->getActionName();
}
$secret = $controller . $action . $salt;
return Mage::helper('core')->getHash($secret);
}
and here's where it validates the secret key
#File: app/code/core/Mage/Adminhtml/Controller/Action.php
protected function _validateSecretKey()
{
if (is_array($this->_publicActions) && in_array($this->getRequest()->getActionName(), $this->_publicActions)) {
return true;
}
if (!($secretKey = $this->getRequest()->getParam(Mage_Adminhtml_Model_Url::SECRET_KEY_PARAM_NAME, null))
|| $secretKey != Mage::getSingleton('adminhtml/url')->getSecretKey()) {
return false;
}
return true;
}
Compare the pre/post hash values of $secret to see why Magento's generating the incorrect key on your page.
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.
To the point, I'm trying to tighten up my code and stop repeating myself. It's a constant struggle, haha
In both my views and my controllers I am checking to see if the user is signed in. That is fine, but I would like to only have to check to see if the user is in the view and then display the right content.
Originally, in my controller, I'm seeing if the user is there and then if it is I load the same views with different varables. It seems a bit repetative.
public function recentStories(){
//This pulls back all the recent stories
if (empty($this->user)) {
$this->load->view('header_view', array(
'title' => 'Recent Stories',
));
$this->load->view('storyList_view', array(
'query' => $this->data_model->pullAllStories(),
));
$this->load->view('footer_view');
}else{
$this->load->view('header_view',array(
'user' => $this->users_model->getUser($this->user->user_id),
'title' => 'Recent Stories',
));
$this->load->view('storyList_view', array(
'query' => $this->data_model->pullAllStories(),
'admin' => $this->admin_model->pulladminRecent(),
'user' => $this->users_model->getUser($this->user->user_id),
));
$this->load->view('footer_view');
}
}
Ultimitly, I would like to reduce that chunk down to something like this.
public function recentStories(){
//This pulls back all the recent stories
$this->load->view('header_view',array(
'title' => 'Recent Stories',
));
$this->load->view('storyList_view', array(
'query' => $this->data_model->pullAllStories(),
'admin' => $this->admin_model->pulladminRecent(),
'user' => $this->users_model->getUser($this->user->user_id),
));
$this->load->view('footer_view');
}
but when I do reduce that code and take out the part that checks the user I am getting back an error saying that I'm trying to get property of a non-object. Obviously, that is talking about the user.
To try and fix this I have tried both
<?if(isset($user)){
user header code in here
}else{
non-user header code in here
}?>
and
<?if(!empty($user)){
user header code in here
}else{
non-user header code in here
}?>
both have returned back "Trying to get property of non-object" Any ideas would be much welcomed. I really don't like having excess code.
public function recentStories(){
// default values
$header_data = array('title' => 'Recent Stories');
$storyList_data = array('query' => $this->data_model->pullAllStories());
// extended data for logged user
if (!empty($this->user)) {
$header_data['user'] = $this->users_model->getUser($this->user->user_id);
$storyList_data['admin'] = $this->admin_model->pulladminRecent();
$storyList_data['user'] = $this->users_model->getUser($this->user->user_id);
}
// load your views
$this->load->view('header_view', $header_data);
$this->load->view('storyList_view', $storyList_data);
$this->load->view('footer_view');
}
then in your views check "if (isset($user)) {" etc.
Hey guys got an issue with Cakephp validation..
I want to know why is partytwo validation going straight to false?
Here is my Relationship model:
<?php
class Relationship extends AppModel{
var $name='Relationship';
public $useTable = 'relationships_users';
public $primaryKey = 'id';
var $validate = array(
'date' => array(
'rule' => array('datevalidation', 'systemDate'),
'message' => 'Current Date and System Date is mismatched'
),
'partytwo'=>array(
'partytwoExists'=>array(
'rule'=> 'userExists',
'message'=>'That username doesnt exist.'
)
)
);
function datevalidation( $field=array(), $compare_field=null ) {
if ($field['date'] > $compare_field)
return TRUE;
else
return FALSE;
}
function userExists($check) {
$userExists= $this->find('count', array('conditions'=>$check));
if($userExists == 1) {
return TRUE;
}else{
return FALSE;
}
}
...
According to the CakePHP book Adding Your Own Validation Methods section, a custom rule that wrote like this
'rule' => array('datevalidation', 'systemDate')
means Cake will runs your datevalidation method like this:
$valid = $Relationships->datevalidation(array(
'date' => 'some user input value'
), 'systemDate');
With the same fashion,
'rule' => array('userExists')
causes Cake to run
$valid = $Relationships->userExists(array(
'partytwo' => 'some user input value'
));
(Simulated calls. Actual calling is using dispatchMethod at line 3155 of Model.php)
So you are most probably need to rewrite your datevalidation method. Furthermore, your code
$userExists= $this->find('count', array('conditions'=>$check));
$userExists can return you a number larger or equal to 0. Your logic is wrong if it returns 2 or more. Consider Model::hasAny instead. It could be the reason why it is always validated as false for your case.
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');