I'm experiencing a problem using the HMVC implementation of Codeigniter, with modules.
I created an authentication module, called autenticacion, which, for obvious reasons, in order to check session validity and expiration, is always used within the MX_Controller, this way:
Modules::run('autenticacion/logout', $user, true);
or
Modules::run('autenticacion/update', $user);
depending on certain conditions.
Ever since I added this athentication implementation, the normal access to other modules was broken.
Now if I try to, for instance, access
www.domain.com/items
having that I coded this module files:
modules/items/controllers/Items.php (this extends MX_Controller)
, and the view:
modules/items/views/items_view.php
The view won't be found despite it's loaded within the Items controller.
If I print out $this in the Items constructor, the MY_Loader instance displays this property:
[_module:protected] => autenticacion
I understand this means the items module is not being loaded within the loader, despite I can reach its controller. The other module (autenticacion) seems that is messing it all.
How could I fix this?
EDIT:
This is what I changed in MX_Controller in order to handle session checks and updates:
<?
class MX_Controller
{
public $autoload = array();
public function __construct()
{
// Validación de la autenticación/caducidad de la sesión
$this->_validar_sesion();
// Original actions
//....
}
//... method __get()
/**
* Checks whether the user has not yet created a valid authenticated user session, or if it has expired.
* In both cases, it redirects to the authentication page, deleting the expired session if it was already created.
*/
private function _validar_sesion()
{
if (stristr(get_class($this), 'autenticacion')) return;
require_once APPPATH . 'third_party/usuarios/Usuario.php';
$this->load->model('autenticacion/autenticacion_model');
// Get user instance from session
$usuario = unserialize($this->session->userdata('usuario'));
// No authenticated session yet: redirecting to the authentication page
if (!stristr(current_url(), 'autenticacion') && ! $this->session->logged_in) {
$this->load->library('uri');
$uri = $this->uri->uri_string();
redirect(base_url() . 'autenticacion' . ($uri ? '?redirect=' . $uri : ''));
}
// There is already an authenticated session, and the request is not coming from the authentication page
elseif (!stristr(current_url(), 'autenticacion')) {
// Check session expiration, in which case, we do logout
if ($this->autenticacion_model->sesion_caducada($usuario, config_item('sess_companyname_expiration'))) {
// This will delete the session from DB, destroy it, and redirect to the authentication page
Modules::run('autenticacion/logout', $usuario, true);
}
// Session has not expired yet: we update the session timestamp in DB to extend the expiration time
else {
Modules::run('autenticacion/update', $usuario);
}
}
}
}
Related
How do I change the content for a user when he logs in? I mean like enabling voting, changing "login" to "logout" etc.
What I think to do is to start the session when user logs in (I am preferring to start session only when user logs in, not all the time). Then add data to the session's cookie like-
//controller
$moredata = array(
'username' => $this->username,
'login' => TRUE
);
$this->session->set_userdata($modedata);
//redirect
Then in the other controller, where he has been redirected I check the following-
$login = $this->session->userdata('login');
if ($login==TRUE)
Depending on the 'if' condition I will pass a variable to the view, with the help of that variable I will forward only the div/sections which should be shown to a logged-in user.
The problem is, while performing the above comparison Codeigniter shows following error (remember I haven't added 'session' in autoload array yet)
Message: Undefined property: NameOfController::$session
And If I set following in the autoload file
$autoload['libraries'] = array('session');
then the "if ($login==TRUE)" comparison always shows FALSE.
What should I do?
If I were you, I'd place all your session checks in a base controller which all your other main controllers extend. This allows you to keep things DRY:
class BaseController extends CI_Controller {
public function __construct()
{
parent::__construct();
}
public function isLoggedIn()
{
// Will return true or false
return $this->session->userdata('login');
}
}
And in one of your functional controllers (the example below handles users):
class UserController extends BaseController {
public function __construct()
{
parent::__construct();
}
public function profile()
{
// Redirect if not logged in
if (!$this->isLoggedIn()) {
$this->redirect('some/other/page')
}
}
public function register()
{
// Show different HTML if not logged in
$data = array(
'isLoggedIn' => $this->isLoggedIn()
);
$this->load->view('register', $data);
}
}
The second method in UserController allows you to render different content in your view:
<? if ($isLoggedIn): ?>
<p>You're logged in!</p>
<? else: ?>
<p>Not logged in</p>
<? endif; ?>
On my last project we created a simple permissions helper that had functions to check for logged-in status and for privilege levels. Then we'd just call the helper's functions as needed from anywhere in the system. If the user is logged in and has privs for that content then they get the content - otherwise we'd redirect them to a registration or other error page. Since all of that logic is in the helper functions, we could wrap any permission-requiring code in a quick permissions call like if(is_logged_in()){code requiring login to access}.
I am using zend framework. I have built a simple login screen. when the user logs in, I want to set a session and then in the init function of the member area controller its meant to check for the session and grant access, else, redirect to login screen.
I have set my login controller like so, this check the username and password and sets the session:
if (isset($_POST['loginSubmit']) && $form->isValid($_POST)){
$inputtedUsername = $_POST['username'];
$inputtedPassword = $_POST['password'];
if ($inputtedUsername == $username && $inputtedPassword == $password) {
$loggedIn = new Zend_Session_Namespace('loggedIn');
$loggedIn->success;
$this->_forward('index', 'home');
} else {
echo 'invalid';
}
}
I have a home controller, which only logged in users should be able to see, so in the innit function I have:
$loggedIn = new Zend_Session_Namespace('loggedIn');
if (isset($loggedIn->success)) {
echo 'success';
}else{
$url = $this->view->url(array(
'controller' => 'index',
'action' => 'index'));
header('Location:' . $url);
}
}
when i log in, using the correct credentials, it redirects me to login screen as stated in the else function.
what am i doing wrong?
First your use of Zend_Session_Namespace is incomplete (you never assigned a value to the namespace):
$loggedIn = new Zend_Session_Namespace('loggedIn');//here you created a namespace
$loggedIn->success;//here you created a key in that namespace with no value
The way your code seems to be structured any value assigned to $loggedIn->success would return true, so maybe try :
$loggedIn = new Zend_Session_Namespace('loggedIn');//here you created a namespace
$loggedIn->success = true;
While this might fix your current issue, I want to suggest you take a look at two Zend components that can really help with authentication and authorization.
The first is Zend_Auth, a component that deals with application authentication and will also help handle user session persistence. Rob Allen has a tutorial to help get you started.
The second is Zend_Acl, The Access Control List component, deals with authorization, who has access to what. A place to start with Zend_Acl
I am having a web project where some access decisions are dependant on the page itself (e.g. /logout which shall only be visible to logged in users) and some are dependant on dynamic model objects (e.g. /article/delete/1234 where we have to check if 1234 was written by the logged in user or if he is an admin).
Now, I am facing the problem of how to bring both things together. No matter how I tried, I cannot rely on any of the two alone:
Some pages do not use any models, so I cannot setup any model rules there
On the other hand, I cannot create dynamic assertions for a modular approach, because Comment is just a comment and not a default/comment. A Comment is not restricted to the default module, it may also be used in the admin module.
With modular ACL I am trying to check for each page if a user is allowed to visit it, e.g.
if (!$acl->isAllowed($user, 'default/secrets', 'mysecrets')) {
$this->forward('auth', 'login');
$this->setDispatched(false);
}
And with dynamic assertions I am checking if somebody is allowed to edit a specific model object.
// $comment has a method getResourceId() returning 'comment'
if ($acl->isAllowed($user, $comment, 'delete')) {
// display a link for deletion
}
Of course it would be nice if the check for
deletion of a specific comment, and
accessing the /comment/delete/???? page
would be the same, but I guess this is not possible and I would have to create two rules:
$acl->allow('member', 'default/comment', 'delete');
$acl->allow('member', 'comment', 'delete', new Acl_Assert_CommentAuthor());
$acl->allow('admin', 'comment', 'delete');
Now, this seems not perfect to me as this can lead to duplicate work in some cases.
Is there some better method to approach this problem? Or is the only method to at least create a coherent naming scheme like: mvc:default/comment, model:comment
The way i did it, custom sql queries that restrict results, functions that check before insert/delete/modify sql
and then an ACL plugin i wrote that checks permission and uses these 5 tables
acl_groups
acl_permession
acl_permession_groups
acl_permession_risource
acl_risource
Code:
class Abc_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{
/**
* Return whether a given request (module-controller-action) exists
*
* #param Zend_Controller_Request_Abstract $request Request to check
* #return boolean Whether the action exists
*/
private function _actionExists(Zend_Controller_Request_Abstract $request)
{
$dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
// Check controller
if (!$dispatcher->isDispatchable($request)) {
return false;
}
$class = $dispatcher->loadClass($dispatcher->getControllerClass($request));
$method = $dispatcher->formatActionName($request->getActionName());
return is_callable(array($class, $method));
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// fetch the current user
$controller = $request->controller;
$action = $request->action;
$logger = Zend_Registry::get('log');
//prima controlla se sei autenticato e poi controlla se l'azione esiste, cosi non esponi il sito
$auth = Zend_Auth::getInstance(); /* #var $auth Zend_Auth */
if($auth->hasIdentity()) {
if(! $this->_actionExists($request))//l'azione non esiste?
{
$request->setControllerName('error');
$request->setActionName('pagenotfound');
$logger->notice( " IP: ". $_SERVER['REMOTE_ADDR']. " http://".$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]. " ?" .http_build_query($_REQUEST));
return ;
}
$allowed = Abc_Action_Helper_CheckPermission::checkPermission($controller, $action);
if ($allowed !== 1)
{
$request->setControllerName('error');
$request->setActionName('noauth');
}
//fine azione esiste
}else{
$request->setControllerName('user');
$request->setActionName('login');
return ;
}
}//fine preDispatch
}
You can then add your code(which i ommited for shortness) to remember the request and redirect you there after login.
I have 2 php websites in the same machine. The first site (a legacy system) has a basic auth: checks if is set $_SESSION['user_id']. I'm working in the second site (a Kohana 3.1 based) that will extends the funcionalities of the first one.
Both sites will link each other, so I need to share the session between those systems. Both sites use the same Database. Users will login in the first site.
In my site I have a code that detects the $_SESSION['user_id'] of the first one, but I'm having problems retaining the session with the Kohana-Auth module.
The first site (the legacy one) checks the session like this:
<?php
session_start();
if(empty($_SESSION['user_id']))header("Location: index.php?action=3");
... //more dark code
this is in all php files... a lot of files.
In my Kohana site I have a controller that before any action checks the session.
<?php
class My_Controller extends Controller_Template {
public function before() {
session_start();
$this->auth = Auth::instance();
if ($this->auth->logged_in()) {
//I have session in the second site... Do I have a session on the first one?
if (!isset($_SESSION['user_id']) || $_SESSION['user_id'] == "") {
//I have no session in the first site... I logout the user in my site
$controller = Request::current()->controller();
if ($controller != 'auth') {
Request::current()->redirect('auth/logout');
}
}
$this->user = ORM::factory('user', $this->auth->get_user()->id);
} else {
//I have no session in the second site... Do I have a session on the first one?
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
if (isset($user_id)) {
$user = Model_User::get_user($user_id);
if ($user->loaded()) {
//I have session in the first site... I login the user in my site
$this->auth->force_login($user);
$this->user = ORM::factory('user', $this->auth->get_user()->id);
}
}
if (!$this->auth->logged_in()) {
//I still have no session => redirect to login of the first site
//Request::current()->redirect(...);
echo Debug::vars("BUUUU");
}
}
}
}
This code is near to work: I can go from one site to the other and the user is detected... but I realised that when the user navegates between the differents actions inside my Kohana site, the "logins" couter of the Users table increases.
That means that before any action the "$this->auth->logged_in()" is FALSE... and this means that the Auth module do not retains my user between actions and do the force-login every time.
I don't know what can I do.
I want detect the session form the first site, but I don't want to login this user in every click.
I found the answer!!
In Kohana 3.1, the Kohana_Session class has a default value of the cookie.
/**
* #var string cookie name
*/
protected $_name = 'session';
That value didn't match with the default name of the PHP session: "PHPSESSID".
And that value is modified by creating a config file called "session.php" in the config directory. So I created a config/session.php like this:
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'native' => array(
'name' => 'PHPSESSID',
)
);
And my final controller was something like this:
<?php
class My_Controller extends Controller_Template {
public function before() {
$this->auth = Auth::instance();
if ($this->auth->logged_in()) {
//I have session in the second site... Do I have a session on the first one?
$user_id = Session::instance()->get('user_id');
if (!isset($user_id) || $user_id == "") {
//I have no session in the first site... I logout the user in my site
$controller = Request::current()->controller();
if ($controller != 'auth') {
Request::current()->redirect('auth/logout');
}
}
$this->user = ORM::factory('user', $this->auth->get_user()->id);
} else {
//I have no session in the second site... Do I have a session on the first one?
$user_id = Session::instance()->get('user_id');
if (isset($user_id) && $user_id != "") {
$user = Model_User::get_user($user_id);
if ($user->loaded()) {
//I have session in the first site... I login the user in my site
$this->auth->force_login($user);
$this->user = ORM::factory('user', $this->auth->get_user()->id);
}
}
if (!$this->auth->logged_in()) {
//I still have no session => redirect to login of the first site
//Request::current()->redirect(...);
echo Debug::vars("BUUUU");
}
}
}
}
that's all...
I'm using Zend_Auth with a project using doctrine.I believe every bootstrapping is done correctly and i can log in.
my adapter looks like this:
class Abra_Auth_Adapter_Doctrine implements Zend_Auth_Adapter_Interface {
protected $_resultArray;
private $username;
private $password;
public function __construct($username, $password) {
$this->username = $username;
$this->password = $password;
}
//based on feedbacks as response authenticate has changed to this
public function authenticate() {
$q = Doctrine_Query::create()
->from("Abra_Model_User u")
->leftJoin("u.Role r")
->where("u.username=? AND u.password=?", array($this->username,$this->password));
$result = $q->execute();
if (count($result) == 1) {
return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $result->get("Mylibrary_Model_User"), array());//autoloaderNamespaces[] = "Mylibrary_" in application.ini
} else {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, null, array("Authentication Unsuccessful"));
}
}
my Abra_Controller_Pluging_Acl looks like this
class Abra_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
parent::preDispatch($request);
$controller = $request->getControllerName();
$action = $request->getActionName();
$module = $request->getModuleName();
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()){
$identity = $auth->getIdentity();
$roles = $identity["Role"];
$role = $roles["name"];
$role = (empty ($role) || is_null($role))? "regular" : $role ;
} else {
$role = "guest";
}
}
now having Doctrine_Event Fatal error: spl_autoload() [function.spl-autoload]: Class Doctrine_Event could not be loaded. i've seen this post here and i'm wondering how that can affect my using of Zend_Session, and it's true that i have apc.dll enabled in my php.thanks a lot for reading this
How to get the role: In your adapter, on successful login, rather than returning only the username field, how about returning the whole user object? Then the whole thing will be available when you call Zend_Auth::getIdentity().
Question 1: If you treat controllers as resources and the ACL rules are going to be different per module, then the resource names should reflect the module, as well. This will address the issue of modules with identical controller names.
Question 2: I am not sure I am understanding correctly. Zend_Auth and its storage will take care of keeping the uer identity in its own session namespace. However, I have run into the issue of what to do when the user record in the db changes - say, the user modifies his full name in his profile during his logged-in session - and you are displaying that full name in your site template, pulled from Zend_Auth::getIdentity(). As a user, I would expect the change to be reflected in the visible interface, but the change has only occurred back in the db, not in the session.
What I have done in the past is to create an additional auth adapter that fetches the new user record and always returns success. When the user updates his profile, I call Zend_Auth::authenticate() using this trivial adapter. The session storage gets updated and all is well with the world.
[This approach is almost certainly a hack, so I'd be interested in hearing alternative approaches. I'm sure I can set a value in the session storage directly, but when I last tried it, I couldn't make it quite work. So resorted to the additional adapter workaround.]