get the requested controller in hook precontroller in codeigniter - php

I'm trying to make a simple routing rule to allow only authenticated users to certain controllers.
To achieve this, I would like to run precontroller hook and check if the user is logged in using ci session. However, I have to know which controller the user wants to access. How do I know this in a hook function?

$this->router->fetch_class();
Extend CI_Controller and this should work.

I dont think that hooks are best practice for what you want to achieve here
you may try the following:
You need to create middle controller that you will extend instead of CI_Controller that will have authentication check and redirect the user to right controller
read this tutorial created by jondavidjohn step by step
http://jondavidjohn.com/blog/2011/01/scalable-login-system-for-codeigniter-ion_auth
You shoud be able to get the idea after 10 mins

Cant you just put the authentication on the controller constructor ?
It will be called when item instantiate and you can do the check there.
Also you can always extend the CI_Controller and put the logic in there so that you can do your check in there (and use this->router->fetch_class() as suggested).

If you don't want to go the extended controller route, and I can see your logic there, then you have to use native PHP here because the CI object doesn't exist in the pre_controller_hook.
So, get the URI, then parse it to find the controller:
$uri = $_SERVER['REQUEST_URI'];
$segments = explode("/", $uri);
// if you're removing index.php from your urls, then
$controller = $segments[0];
// if you're not removing index.php
$controller = $segments[1];

Extend CI_Controller in your autoload library Class.
Something like this:
class MyAuthLibrary extends CI_Controller {
var $ci;
function __construct() {
$this->ci = &get_instance();
$route = $this->ci->router->fetch_class();
if( ($route !== 'login') && !$this->ci->session->userdata('email_address') ) {
redirect('login');
}
}
}

Related

CodeIgniter : Using both core classes and extended classes ?

I'm currently working on CI for my website, and i'm having some trouble about extending Controller_CI.
I have one controller that deals with login/signin actions, which doesn't need authentication, and others controllers that check if a user session exists before loading content.
For that purpose, I created MY_Controller class and add authentication code in the constructor.
Then I made all my controller extend MY_Controller, except the first one that still extends Controller_CI
My question is : Is it the right way to deals with authentication ? Is it still possible to use Controller_CI even if it's extended ?
I found another pattern :
http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-Base-Classes-Keeping-it-DRY
I guess it's better, but still, I don't understand why not using the first solution.
Thanks
Extending controller class for that purpose will work, but this solution is not much flexible. I would rather create a library that handles authentication, and run it from a controller when it is desired. Please read http://ellislab.com/codeigniter/user-guide/general/creating_libraries.html for details about creating custom libraries in CI.
Please remember you can only extend the CI_Controller with MY_Controller only once. In that aspects it's not a good idea. Suppose you want to implement another feature (e.g. a piece of code that makes a specific entry in the log) for some controllers, but not necessarily the controllers that need authentication you cannot make another MY_Controller.
Using a library is a better thing.
I'm using the flexi auth library on a big CI site. On every controller that requires authentication I just add the following:
public function __construct() {
parent::__construct();
$this->load->library('flexi_auth');
if (!$this->flexi_auth->is_logged_in())
redirect('auth/login');
}
I think a combination of what Phil Sturgeon suggests in that blog post and using a library would be best. So I would create a core controller (by that I mean a controller you place into application/core that extends CI_Controller) called MY_Controller which will look something like this
class MY_Controller extends CI_Controller
{
function __construct()
{
parent::__construct();
}
//Any other functions you want
}
Then judging by your question you currently have controllers that fit into two categories
Controllers that do require a logged in user before they do
anything
Controllers that don't require a logged in user before they do anything
So I would then create another controller in the /application/core directory that extends MY_Controller but in its constructor it checks to see if the user is logged in
class Auth_Controller extends My_Controller
{
function __construct()
{
parent::__construct();
//Check to see if the user is logged in
$this->load->library('authentication');
if(!$this->authentication->user_logged_in())
{
redirect('/login');
}
}
//Any other functions you want
}
Now when you create you controller you can choose which one of the core controllers you want to extend. If its a controller than doesn't require a logged in user you can extend MY_Controller but if it does required a logged in user you can extend Auth_Controller. That way it means you only need to do the user login check once in your code.
Like others have said if may be a good idea to place any authentication code into a library as that's a better place to put it than in a controller.
Summary
So to summarise by having all of your controllers extend core controllers rather than CI_Controller it should cut down on code repetition.
I also currently working on a CI project and had the same issue. I have came up with a different solution to deal with the authentication.
I extended the core controller as bellow,
class MY_Controller extends CI_Controller
{
public $data = array();
public $calledClass ;
public $calledMethod ;
public function __construct()
{
parent::__construct();
$authException['authentication']['login'] = true;
$authException['authentication']['logout'] = true;
$authException['welcome']['index'] = true;
$this->load->library("router");
$this->calledClass = $this->router->fetch_class();
$this->calledMethod = $this->router->fetch_method();
if(!#$authentication[$this->calledClass][$authentication->calledMethod] && !Auth::isUserLoggedIn())
{
# IS_AJAX is a contant defined in the contants.php
# define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
if(IS_AJAX)
{
# if this is an AJAX call, it sets a value in the header ,
# which can be captured in the AJAX response
# to redirect the user to the login page.
$this->output->set_header("auth-him:1");
exit;
}
else
{
redirect("authentication/login");
}
}
}
}
Hope the code above is clear and this helps.
To extend core controller more than one time, If you still need to use 2 controllers to handle authentication, I have followed this method
https://stackoverflow.com/a/22125436/567854
Hope this also helps :)

CakePHP: Can you use admin prefix with pages?

In my controllers I'm limiting access by prefixing my functions with admin_ and then letting the AppController isAuthorized() function check if the user is an admin. Is there a way to do this with pages (from PageController)? We created an admin homepage (like a dashboard) which users cannot view if they are not logged in, but non-admin users CAN view it. I can't figure out how to prevent this.
What do you mean with pages? Something static served by the PagesController or a .htm(l) file located in your webroot?
If your case is the first thing, you can implement this logic for the Pages Controller.
If it is the second - the request doesn't go through CakePHP (or any other server-side script) at all, so no you cannot controll access to it through Cake.
If your situation is something else, then refine your question and I'd be happy to help.
As the comment suggests you're in situation No:1. In the class declaration of the PagesController it says:
class PagesController extends AppController {
this means that you can use any logic that is in AppController in whichever class that extends AppController. Thus you can use isAuthorized() in the PagesController.
All you need to do is create a method with a name the same as your "admin dashboard view" and allow access to it only for admins. Or just check the user role.
Assuming that the first parameter is the requested Page and you can catch it using this statement:
$this->request->pass[0];
you can use the isAuthorized function to solve your problem doing something like this...
public function isAuthorized()
{
$page = strtolower($this->request->pass[0]);
if ($page = 'admin_page')
{
if ( $this->Auth->user('Role.role_field') == 'Admin' )
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
// This will authorize users for the other pages
return TRUE;
}
}
Hope this helps. Always check the CookBook: sometimes you need to check the older Books for finding what you really need. Happy Coding!

Passing data to the view through the constructor method in Codeigniter

I am currently using the codeigniter tank_auth, at the start of every controller method I have to do the following:
$data['profile'] = $this->tank_auth->get_profile();
The main reason I do this is to display the current logged in username, and also get their privilege level.
I am going over the code trying to go by the DRY principle and have moved a lot of repeated code over to the _constructor method (Like checking if the user is logged in). I am just wondering if there is a way to move this code from the start of every method to the constructor.
My current constructor method looks like so:
public function __construct()
{
parent::__construct();
// If the user isn't logged in redirect to login page.
if (!$this->tank_auth->is_logged_in())
redirect('auth/login');
}
Thanks!
Add variable $data to the controller and use it for all your view data. For example:
public function __construct()
{
parent::__construct();
$this->data['profile'] = $this->tank_auth->get_profile();
}
When calling the view remember to call it like this:
$this->load->view('my_view', $this->data);
You can also extend CI_Controller with MY_Controller and put the login check in the constructor of MY_Controller. Just extend all controllers which need this check from MY_Controller instead of CI_Controller.

Code Igniter 2 - Controller/Method override

If a user is not logged in I would like to override the existing Controller and call the auth/login one instead keeping the browser URI in tact. (Every user has to be logged in to use the site)
I have tried a pre controller hook, but its too early. The auth library has not been instantiated by this point.
The post controller hook is too late as the target controller has been instantiated.
Editing the constructor of the library seems pointless also, as it has already been created.. So i am a little stuck..
Any ideas please? :)..
Thanks
I would override the default controller and extend in each controller. Create a new file: ./app/core/MY_Controller.php with the next code:
class Secure_Controller extends CI_Controller {
function __construct() {
parent::__construct();
/* replace this code with yours */
if( !$this->session->userdata('logged_in') ) {
redirect(base_url() . 'login', 'refresh'); // your login url
}
}
}
Then, use this code in each controller that you should be accessible by logged users
class Main extends Secure_Controller {
Have you considered just saving the URI to flashdata, redirecting the visitor to another controller (login page) and then just putting the referrer into a hidden form field? I'm doing the same thing with one of my sites using Codeigniter and it works fine. The only difference is that the URI is not intact during this process.
Perhaps the answer to this question will be the same for you ? CodeIgniter only allow access to certain controllers when logged in
(If so, don't forget to replace the "function className()" by "function __construct()" and "parent::Controller();" by "parent::__construct();")

All admin/user functions checking if he/she is logged in - How to avoid this?

I am very, very new with MVC (just started yesterday) so this question is probably stupid, but I need to know how to check automatically if user is logged in on my functions that are in admin/user models.
I can put the checking in construct, this would help, but I have several models and maybe there is some even better way. I hope you will understand better what I want after you see my folder structure and code. Oh, and by the way - I use Code Igniter 2.0
Folders:
controllers/
../admin/
../../item.php
../../cat.php
Let's see my item.php file...
<?php
class Item extends CI_Controller
{
function Index()
{
//Checking if admin is logged in on every function is bad
/*
* Is it possible to somehow make all admin functions go through
* some kind of Admin class that will check automatically?
*/
$isLogged = $this->session->userdata('is_logged_in');
if ($isLogged == true)
{
$this->load->view('admin/item/main');
}
else
{
$this->load->view('admin/login');
}
}
function Add()
{
$this->load->view('admin/item/add');
}
function Edit()
{
$this->load->view('admin/item/edit');
}
function Delete()
{
$this->load->view('admin/item/delete');
}
}
I hope that this is easy question, thanks in advance :)
I would implement the login-function in CI_Controller.
Then I would set an protected variable in Item protected $loginRequired = true;.
In function __construct() or Item() I would call parent::isLoginRequired($this->loginRequired) which checks if a login is required.
I would also redirect to a specific login page with a parameter which redirects the user back to the page he needs to be logged in.
Make a new class, for example, My_Controller extends Ci_Controller and write some auth checker code in it... in controller file just extend My_Controller
what i usually do is -like Teeed recommends- Create my own controller Class which is between the CI_Controller and each controller you might create.
In that class (MY_Controller) you can instantiate a model which handles all user related data and logics (loading session data, executing specific checks, etc..) and finally set as class variables those results, so you will end up having:
$this->isLogged ;
$this->isPaying ;
$this->isPlatinumMember ;
etc..
in any of your classes extending from MY_Controller
that makes very easy to check any condition within any of your Controllers.

Categories