CakePHP: how to check a session before each action? - php

I have two things I need to do in Cake 2.2. I need to check that someone is logged in on every page, and then I need to store their user ID as a constant named UID.
I want to avoid being on each controller and having to check the user. I want it done automatically, so naturally I go to AppController and try BeforeFilter. It doesn't seem to have initialized the session, so I scratch that and try afterFilter, but that (and BeforeRender) is really not what I want to do; I don't want a single thing that I can stop from happening, to happen on page request if they're not logged in. Bootstrapping was also not the answer, as far as I could tell.
So how can I check if a user's logged in, and at the same time define a constant for the rest of my application?

You can do the same by invoking a user session check method in beforeFilter() method in AppController. Your code should looks like:
<?php
App::uses('Sanitize', 'Utility');
App::uses('Controller', 'Controller');
class AppController extends Controller
{
public $components = array(
'Session',
'RequestHandler'
);
public $helpers = array('Html', 'Form', 'Session', 'Js' => array('Jquery'), 'Text');
public $uses = array('User');
public function beforeFilter()
{
if($this->Session->check('User.id'))
{
$this->set('logged_in', true);
$this->set('username', trim($this->Session->read('User.first_name'). ' '. $this->Session->read('User.last_name')));
}
else
{
$this->set('logged_in', false);
}
}
/*.......... Rest of your code ..............*/
}
Kindly ask if it not worked for you.

Related

CakePHP: calling function in the AppController but not effect some exception

Normally, in cakephp there is Auth component to help user login and there is function Auth->Allow() to make the guests users still can access to some pages like Index. But now i want that only Activated account can access almost every function of the web, but still except some normal pages like index, view etc.
I have a fucntion in Appcontroller
public function is_activated(){
$userId = $this->Auth->user('id');
$user = $this->Users->find('all', [
'conditions' => ['id' => $userId],
'fields' => ['id', 'email', 'activated']
])->first();
$activated = $user->activated;
if($activated !== 1){
$this->Flash->error(__('Your account is not yet activated'));
return $this->redirect('/users/activate');
}
}
I call it in BeforeFilter along with Auth->allow() in ProjectsController:
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->Auth->allow(['index', 'getMyProjects']);
$this->is_activated();
}
But in this way, every pages are affected and Auth->allow() not working anymore. Can anybody show me a better way for my is_activated() function, i guess that this way i redirect the web is not a good way.
What you are looking for is isAuthorized() function:
public function isAuthorized($user){
if($user->activated){
return true;
}
return false;
}
Put it in your AppController, you can also override it in your other controllers. If present, it will be automatically called.
Further reading:
https://book.cakephp.org/3.0/en/tutorials-and-examples/blog-auth-example/auth.html#authorization-who-s-allowed-to-access-what
https://book.cakephp.org/3.0/en/controllers/components/authentication.html#authorization

How to access one controller action inside another controller action?

I am using cakephp-2.x. I have one function name user_info() in the UsersController.php i want to access this in another controller name MessagesController.php
Code -
UsersController.php
public function user_info(){
$user_id=$this->Session->read('Auth.User.id');
$data=$this->User->findById($user_id);
$this->set('user_info',$data);
}
MessagesController.php
public function index(){
//$userInfo=new UsersController();
//$userInfo->user_info();
$this->user_info();
pr($data);
}
Error Message-
Fatal Error
Error: Call to undefined method MessagesController::user_info()
File: E:\xampp\htdocs\2014\myshowcam\msc\app\Controller\MessagesController.php
Line: 18
Notice: If you want to customize this error message, create app\View\Errors\fatal_error.ctp
Typically if you're trying to access a function in one controller from another controller you have a fundamental flaw in your project's logic.
But in general object usage is thus:
$otherController = new whateverMyControllerNameIs();
$otherController->functionName();
However I'm not familiar enough with cake to tell you the pitfalls of doing such a thing. For example I have no idea what this would do to routes or what other variables/objects are required to initialize a controller correctly.
EDIT:
Ref: CakePHP 2.3.8: Calling Another Controller function in CronController.php
App::import('Controller', 'Products'); // mention at top
// Instantiation // mention within cron function
$Products = new ProductsController;
// Call a method from
$Products->ControllerFunction();
Try requestAction function of cakephp
$result = $this->requestAction(array('controller' => 'users', 'action' => 'user_info'));
Why would a simple, When can complicated?
All the information for a registered user of User model is accessible in the following manner:
AppController.php
public $user_info; /* global scope */
public function beforeFilter(){
$this->user_info = $this->Auth->user(); // for access user data in any controller
$this->set('user_info_view',$this->Auth->user()); // for access user data in any view or layout
}
MessagesController.php
public function index(){
debug($this->user_info);
$my_messages = $this->Message->find('all',
array('conditions' => array('Message.user_id' => $this->user_info['id']))
}
....
layout or view.ctp
<?php echo $user_info_view['name']; ?> // email, etc
Why not take advantage of the way CakePHP handles relationships? There's a very easy way to achieve what you're trying to do without extending controllers or loading in additional controllers which seems excessive for your example.
Inside AppController's beforeFilter()
Configure::write('UserId', $this->Session->read('Auth.User.id'));
This will allow you to access the UserID from your models
Inside your User's model, create the following function
/**
* Sample query which can be expanded upon, adding fields or contains.
*
* #return array The user data if found
*/
public function findByUserId() {
$user = $this->find('first', array(
'conditions' => array(
'User.id' => Configure::read('UserId')
)
));
return $user;
}
Inside your Users controller (Minimal is better, no?)
public function user_info() {
$this->set('user', $this->User->findByUserId());
}
Inside your Messages controller
public function index() {
$this->set('user', $this->Message->User->findByUserId());
// --- Some more stuff here ---
}
And that's it, no need to be extending controllers, just make sure your Message and User model are related to each other, failing that you can bindModel or use ClassRegistry::init('User')-> for example.

Static Variable/Methods in PHP using CakePHP

I'm saving the ID of the conected user in a static variable at MainController, but I need to access this variable in others controllers. When I try to get the value from the variable, the result is always the initial value of the variable, even when I have already modified it.
class MainController extends AppController {
//...
public static $loggedClienteId;
//functions
public function loginCliente(){
//code...
self::$loggedClienteId = $cliente['Cliente']['id'];
var_dump(MainController::$loggedClienteId); //returns the correct value.
return $this->redirect(array('controller' => 'clientes', 'action' => 'index'));
}
}
So, in another controller...
include "MainController.php";
class ClientesController extends AppController {
public $helpers = array('Html', 'Form');
public function index() {
var_dump(MainController::$loggedClienteId); //null, althought it already has a value...
$this->set('clientes', $this->Cliente->find('all'));
}
//functions...
}
Why is that happening?
Use $this->Auth->user('id') to get the current logged in user's id.
The reason your code does not work is because once the request for the login action is completed, the script is over. Setting a variable does not persist across requests. You have to save variables in the session for that.
If it's not the logged in user's id you need, what you have to do is use the SessionComponent and use $this->Session->write('key', 'value'); and to read it in another request/controller $this->Session->read('key');.

CakePHP - BaseAuthenticate altering loginRedirect

I have a number of authentication components that extend the BaseAuthenticate class. These are setup in the AppController in the normal way.
Is it possible for an authentication component to alter the AuthComponent's loginRedirect variable?
To clarify the situation, one of my authentication components looks at a certain subset of users. It checks to see if the credentials are valid before checking to see if that person has any outstanding invoices. Depending on the outstanding value, I'd like to redirect the user to a given page or block them out altogether.
Thanks
Yes it's possible. The AuthComponent's redirect location is just a session variable (so technically it can be set anywhere).
To change the redirect location, you can set it manually:
$this->Session->write('Auth.redirect', 'http://example.com');
On the next request, they will be redirected by the AuthComponent.
Or, have your component redirect them then and there:
$this->_Collection->getController()->redirect('http://example.com');
A big thanks to #jerermyharris for pushing me in the right direction. Here goes with what I ended up doing.
1. Extended the AuthComponent
App::uses('AuthComponent', 'Controller/Component');
class MyAuthComponent extends AuthComponent {
var $components = array('Session');
public function identify(CakeRequest $request, CakeResponse $response) {
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
}
foreach ($this->_authenticateObjects as $auth) {
$result = $auth->authenticate($request, $response);
if (!empty($result) && is_array($result)) {
if(isset($result['Redirect']))
{
$this->Session->write('Auth.redirect', $result['Redirect']);
}
return $result;
}
}
return false;
}
}
2. Add this the AppController components
public $components = array(
'Auth' => array(
'className' => 'MyAuth',
)
);
Add this bit around whatever other definitions you have for your AuthComponent.
3. Return a redirect from your authentication component
App::uses('BaseAuthenticate', 'Controller/Component/Auth');
class TutorAuthenticate extends BaseAuthenticate {
public function authenticate(CakeRequest $request, CakeResponse $response) {
$user = ...... // However you authenticate your user
$user['Redirect'] = "http://example.com";
return $user;
}
}
So now if you want to redirect based on the user you can just add it in, if you don't then cake will obey the directives you set up in AppController.
Wow, that seems like I've had to do loads extra, but it's the right thing to do.

Zend Framework how to do this in order to not repeat myself

I have this thing that I need in multiple places:
public function init()
{
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) $this->_redirect('/'); #Logout the user
}
These two lines:
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) $this->_redirect('/'); #Logout the user
Whats the best way to do it in ZendFramework?To create a plugin or? I mean I want to execute it in multiple places but If I need to edit it I want to edit it in one place.
Here is an example of an Action Helper that you can call from your controllers easily.
<?php
class My_Helper_CheckFbLogin extends Zend_Controller_Action_Helper_Abstract
{
public function direct(array $params = array())
{
// you could pass in $params as an array and use any of its values if needed
$request = $this->getRequest();
$view = $this->getActionController()->view;
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) {
$this->getActionController()
->getHelper('redirector')
->gotoUrl('/'); #Logout the user
}
return true;
}
}
In order to use it, you have to tell the helper broker where it will live. Here is an example code you can put in the bootstrap to do so:
// Make sure the path to My_ is in your path, i.e. in the library folder
Zend_Loader_Autoloader::getInstance()->registerNamespace('My_');
Zend_Controller_Action_HelperBroker::addPrefix('My_Helper');
Then to use it in your controller:
public function preDispatch()
{
$this->_helper->CheckFbLogin(); // redirects if not logged in
}
It doesn't go into much detail, but Writing Your Own Helpers is helpful as well.
If you need this check in every Controller you could even set up a baseController from which you extend instead of the default one:
class My_Base_Controller extends Zend_Controller_Action
{
public function init()
{ ...
class IndexController extends My_Base_Controller
{ ...
Shift your init() into the base controller and you don't need to repeat yourself in every specific controller.
Need a varying init() in a specific controller?
class FooController extends My_Base_Controller
{
public function init()
{
parent::init();
...

Categories