I'm fairly new to CodeIgniter and I'm using Ion Auth for user authorization. With the Ion Auth library, you get a user object like:
$user = $this->ion_auth->get_user();
echo $user->email;
If a user is logged in, I want the $user object to be available on any of the pages (in any of the controllers). How can I do this? (I'm using CodeIgniter 2)
This blog post of mine goes into a lot of detail in specifically using Ion_Auth to allow your entire application (including views) to access your current user's information.
The short version... (specifically for the topic at hand)
Adding this to your MY_Controller.php file
class User_Controller extends CI_Controller {
protected $the_user;
public function __construct() {
parent::__construct();
if($this->ion_auth->logged_in()) {
$data->the_user = $this->ion_auth->get_user();
$this->the_user = $data->the_user;
$this->load->vars($data);
}
else {
redirect('/');
}
}
}
Then in your application just create your controller like this
class App_Controller extends User_Controller {
public function __construct() {
parent::__construct();
}
function index() {
//do stuff
}
}
Then not only in your controller will you have access to $this->the_user but you will also have $the_user loaded into every view with $this->load->vars()
Normally you'd cache something like this in the session, however I've moved away from that in most of my CI apps.
An example: you log the user in (caching the user data) and then they go to update their email on their profile page. You now have to reload you cache. Many other things could drive this reload. Usually the most I cache any more is the ID, then I do what you're doing and get the user's data on any page I need it.
One thing I found that helps is to have base controllers. For example I have a RegisteredUserController. Any controller whose actions all require the user to be logged in extend this. That way I can keep the logged in check and things like get the user data in one spot (in the constructor). For example:
class RegisteredUserController extends CI_Controller {
var $user;
function __construct() {
parent::__construct();
$this->user = $this->ion_auth->get_user();
}
}
Then any controller that extends RegisteredUserController instead of just controller can get to the user with $this->user. Following this pattern would get it on every page (that extends the controller) without resorting to caching.
Related
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 :)
There are two parts to this application I've been building. There's the website which is powered by the CMS and there's the CMS (wrestling manager) that goes with it. With the two controllers I created called frontend and backend I use those accordingly. The frontend controller is responsible for the code that needs to be ran across all controllers on the website. The backend controller is responsible for the code that needs to be ran across all controllers on the CMS.
I'm looking for the ideal way to work with the user data after the user successfully logs in again gets directed to the dashboard which is extended by the backend controller. I've heard different solutions for example a hook. That's just one I've heard.
Inheritance is your friend here
You would simply create a master controller(front-controller) that extends CI_Controller
You might consider doing a SPA application, if so there are many great frameworks to help you achieve this, quite a popular one is angular.js
some more useful reading on the subject...
class MY_Controller extends CI_Controller
{
protected $currentUser;
protected $template;
public function __construct()
{
//You talked about hooks
//this constructor is the same as saying
//post_controller_constructor
$this->template = 'master/default';
}
//Ok so what functions need to be run
//throughout the application
//run them once here, keeping our app DRY
protected function isAjax()
{
return ($this->input->is_ajax_request())
? true
: show_error('Invalid Request!');
}
protected function isLoggedIN()
{
//load your auth library maybe here
$this->load->library('auth');
//check you have a "current logged In user"
//expect object or null/false
$this->currentUser = $this->auth->getLoggedInUser();
return ($this->currentUser) ?: false;
}
}
class Frontend_Controller extends MY_Controller
{
public function __construct()
{
parent::__construct();
}
public function testFirst()
{
//maybe you just need to test for ajax request here
//inheritance from parent
$this->isAjax();
}
public function testSecond()
{
//maybe you DO need a user logged in here
//inheritance from parent
$this->isAjax();
//or yet again, abstract this to the parent level
if(!$this->currentUser || is_null($this->currentUser))
{
//handle errors on frontend
return $this->output->set_status_header(401); //un-authorized
}
}
}
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.
By default the cakephp Auth class redirects the viewer to the front page if he/she is not logged in and tries to access a denied page.
I wish for the server to run some code when this happens (set some variables and stuff). How would I go about modifying the Auth class's behavior when it redirects the user to the home page due to lack of authentication.
From the sounds of your question, you need to specify your app's allowed actions, then test for authentication and call your method if users are not logged in. Place in AppController if you want this to be application-wide.
Auth::user() returns null if the user is not logged in.
class AppController extends Controller {
var $components = array('Auth', 'Session');
function beforeFilter() {
parent::__construct();
// Your app-wide beforeFilter code, if any
$this->Auth->allow('index', 'view', 'register', 'whatever');
if ($this->Auth->user() == null) {
$this->_attemptRestricted();
}
}
function _attemptRestricted() {
// set your variables, etc...
}
}
All of Auth's defaults can be customized as your application requires.
For more about Auth::user() --
http://book.cakephp.org/view/1264/user
All about AuthComponent --
http://book.cakephp.org/view/1250/Authentication
At first it is no good idea to change the Auth-Component-code itself since in an update you would loose all your changes.
You should write your own component which extends the built-in component like this:
// /app/controllers/components/my_auth.php
App::import('Component', 'Auth');
class MyAuthComponent extends AuthComponent {
function redirect($url = null) {
//have a look in the original auth-component to see how to change this behaviour
}
}
This way you can now use your new component with your extended redirect-method.
Unfortunately you would now have to change each $this->Auth to $this->MyAuth. If you do not want that you can do something like this in your AppController:
public function constructClasses() {
parent::constructClasses();
$this->Auth = $this->MyAuth;
}
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.