How can I set session expiration time dynamically in codeigniter?
For example, if a user logs in and has the role of admin, the expiration time should be longer than if a user logs in who does not have an admin role.
Thanks.
You can update your session expiration time by increasing this variable in config file:
$config['sess_expiration'] = 'somevalue'.
Set $config['sess_expiration'] = 0, if you want it to never expire.
Here's a good discussion on CI forums:
Dynamically set configuration on session expire doesn’t work
$data = array(
'username' => $this->input->post('username'),
'ADMIN_is_logged_in' => true
);
$this->session->sess_expiration = '14400';// expires in 4 hours
$this->session->set_userdata($data);// set session
None of these solutions address doing this dynamically or require another variable to be added to the session. The solution I came up with for CI 3.0.4 is to extend Session.php.
Create file application/libraries/Session/MY_Session.php
Put the following into the file and modify for your logic of setting the $expiration variable. In my case I am pulling the value from a database. NOTE: If you have different expiration values per user type; there is a chance they could get garbage collected and expire unexpectedly due to different expirations with the same session. In this case I do NOT recommend this approach.
<?php
class MY_Session extends CI_Session
{
public function __construct(array $params = array())
{
parent::__construct($params);
}
/**
* Configuration
*
* Handle input parameters and configuration defaults
*
* #param array &$params Input parameters
* #return void
*/
protected function _configure(&$params)
{
$CI =& get_instance();
$phppos_session_expiration = NULL;
$CI->db->from('app_config');
$CI->db->where("key", "phppos_session_expiration");
$row = $CI->db->get()->row_array();
if (!empty($row))
{
if (is_numeric($row['value']))
{
$phppos_session_expiration = (int)$row['value'];
}
}
$expiration = $phppos_session_expiration !== NULL ? $phppos_session_expiration : config_item('sess_expiration');
if (isset($params['cookie_lifetime']))
{
$params['cookie_lifetime'] = (int) $params['cookie_lifetime'];
}
else
{
$params['cookie_lifetime'] = ( ! isset($expiration) && config_item('sess_expire_on_close'))
? 0 : (int) $expiration;
}
isset($params['cookie_name']) OR $params['cookie_name'] = config_item('sess_cookie_name');
if (empty($params['cookie_name']))
{
$params['cookie_name'] = ini_get('session.name');
}
else
{
ini_set('session.name', $params['cookie_name']);
}
isset($params['cookie_path']) OR $params['cookie_path'] = config_item('cookie_path');
isset($params['cookie_domain']) OR $params['cookie_domain'] = config_item('cookie_domain');
isset($params['cookie_secure']) OR $params['cookie_secure'] = (bool) config_item('cookie_secure');
session_set_cookie_params(
$params['cookie_lifetime'],
$params['cookie_path'],
$params['cookie_domain'],
$params['cookie_secure'],
TRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons
);
if (empty($expiration))
{
$params['expiration'] = (int) ini_get('session.gc_maxlifetime');
}
else
{
$params['expiration'] = (int) $expiration;
ini_set('session.gc_maxlifetime', $expiration);
}
$params['match_ip'] = (bool) (isset($params['match_ip']) ? $params['match_ip'] : config_item('sess_match_ip'));
isset($params['save_path']) OR $params['save_path'] = config_item('sess_save_path');
$this->_config = $params;
// Security is king
ini_set('session.use_trans_sid', 0);
ini_set('session.use_strict_mode', 1);
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.hash_function', 1);
ini_set('session.hash_bits_per_character', 4);
}
}
You can handle this with a custom controller. When a user logs in, set a session variable with the time of login. Create custom controller that contains a function in the constructor to check if the user is not admin user and if the timeout has expired. If it has, call $this->session->destroy(); Now, make all your controllers extend that controller instead of the CI base controller.
In Codeigniter 4
Go to the file App=>Config=>App.php
Find the var $sessionExpiration
The default value for this var is 7200
Change it to the value as you want your session to be alive.
The complete config for the session is given below:
public $sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler';
public $sessionCookieName = 'ci_session';
public $sessionExpiration = 7200;
public $sessionSavePath = WRITEPATH . 'session';
public $sessionMatchIP = false;
public $sessionTimeToUpdate = 300;
public $sessionRegenerateDestroy = false;
Session will expire in 10 seconds
$config['sess_expiration']= 10;
Session will not expire
$config['sess_expiration']= 0;
In Codeigniter 4, I do it the other way.
Set the session expiration time to maximal value (for example month for everybody) then in your controller or libraries, if the user is not admin, check the last active time, if time is more than what you need, destroy session and require log in.
use something like this:
$user_type = $this->input->post('user_type');
if ($user_type == 'admin')
{
//set session to non-expiring
$this->session->sess_expiration = '32140800'; //~ one year
$this->session->sess_expire_on_close = 'false';
}
else
{
//set session expire time, after that user should login again
$this->session->sess_expiration = '1800'; //30 Minutes
$this->session->sess_expire_on_close = 'true';
}
//set session and go to Dashboard or Admin Page
$this->session->set_userdata(array(
'id' => $result[0]['id'],
'username' => $result[0]['username']
));
At codeigniter go to applications/config.php and find the below configuration.
$config['sess_expiration'] = 14400; //in seconds
In your login functionality just after user credentials have been verified you can check if user is admin and set different sessions accordingly. Something along these lines
<?php
/*
*Assuming user is successfully veriefied and you've verified id user is admin*/
if($isAdmin==true){
$this->session->sess_expiration = 14400; // 4 Hours
}else{
// For ordinary users
$this->session->sess_expiration = 1800; // 30 minutes
}
$this->session->sess_expire_on_close = FALSE;
I think the most better chooice is using session temp_data and always you can change is dynamically and it is not depended your 'sess_expiration' in config file:
$this->session->set_tempdata('admin_session', true, 72000);
$this->session->set_tempdata('user_session', true, 14400);
where you check admin or user login state, like 'ADMIN_is_logged_in?'
check the remained 'tempdata' lifetime by:
if($this->session->tempdata('admin_session')){
//do something }
else{
//session timeout and is time to destroy all sessions
session_destroy();
}
You can solve the session issue by replacing this:
$config['sess_use_database'] = TRUE;
$config['sess_encrypt_cookie'] = TRUE;
with this:
$config['sess_use_database'] = FALSE;
$config['sess_encrypt_cookie'] = FALSE;
Related
After upgrade from 2.4.6 to 2.5 users get logged out every hour or two, though ttl is not that small. Switching back fixes the problem.
framework:
session:
name: SESSID
handler_id: session.handler.pdo
cookie_lifetime: 259200
Login is done with ajax.
$token = new UsernamePasswordToken($user, $user->getPassword()/* null */,
'main', $user->getRoles());
$this->get('security.context')->setToken($token);
$event = new InteractiveLoginEvent($this->getRequest(), $token);
$this->get('event_dispatcher')->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $event);
Does anyone know about changes to symfony which could cause this? Or better and still simple way to log in?
I encountered this problem and I solved it by setting a value to gc_maxlifetime in config.yml. For that, I had a look to PdoSessionHandler.php and in the read method:
public function read($sessionId)
{
$this->beginTransaction();
try {
$this->lockSession($sessionId);
// We need to make sure we do not return session data that is already considered garbage according
// to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes.
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
$sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id AND $this->timeCol > :time";
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
$stmt->execute();
// We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed
$sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
return base64_decode($sessionRows[0][0]);
}
return '';
} catch (\PDOException $e) {
$this->rollback();
throw $e;
}
}
I looked especially these two lines:
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
and
$stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
So, I have defined in config.yml:
session:
handler_id: session.handler.pdo
cookie_lifetime: 259200
gc_maxlifetime: 259200
I think that default php.ini value is taken for gc_maxlifetime if it is not defined (here $maxlifetime). So when user is inactive more than gc_maxlifetime, he is disconnected. cookie_lifetime will force user to be disconected after cookie_lifetime seconds.
PS: In Symfony 2.4 there was this code where gc_maxlifetime was not used:
public function read($sessionId)
{
$sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->execute();
// We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed
$sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
return base64_decode($sessionRows[0][0]);
}
return '';
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
}
}
I've been analyzing joomla session and I came across a few problems. One of the problems is the session name.
See this code:(seesion.php#894)
protected function _setOptions(array $options)
{
// Set name
if (isset($options['name']))
{
session_name(md5($options['name']));
}
// Set id
if (isset($options['id']))
{
session_id($options['id']);
}
// Set expire time
if (isset($options['expire']))
{
$this->_expire = $options['expire'];
}
// Get security options
if (isset($options['security']))
{
$this->_security = explode(',', $options['security']);
}
if (isset($options['force_ssl']))
{
$this->_force_ssl = (bool) $options['force_ssl'];
}
// Sync the session maxlifetime
ini_set('session.gc_maxlifetime', $this->_expire);
return true;
}
My problem is with the $options array , I add this to the script:
print_r($options);
the result was: Array ( [name] => 266e79f0eac297f66eaf7926636f03fa [expire] => 900 )
where did this element come from ? I mean is there a value that will allow me to do this:
md5('value');
and get the same [name] element
Hey i found the answer for this.
The session value is calculated as following.
$hash = md5(md5($secret.'site'));
where $secret is defined in the configuration file.
use following code to get the session value.
include_once(dirname(dirname(FILE)).DIRECTORY_SEPARATOR.'configuration.php');
$config = new JConfig;
$secret = $config->secret;
$hash = md5(md5($secret.'site'));
So for you the value of name variable comes from md5($secret.'site') which is session value. and if you take md5 again it will give you the session cookie value.
Problem scenario
If a user A logs into the application then the user id set in session. After doing some tasks user A closes his browser and leaves the computer. Short time later, user B came and open browser and see the application was in logged in state. User B can also open an internal url, which directly redirects him into the application without any authentication by using the previous session.
My Configuration
$config['sess_cookie_name'] = 'cisession';
$config['sess_expiration'] = 7200;
$config['sess_expire_on_close'] = TRUE;
$config['sess_encrypt_cookie'] = FALSE;
$config['sess_use_database'] = FALSE;
$config['sess_table_name'] = 'ci_sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_match_useragent'] = TRUE;
$config['sess_time_to_update'] = 300;
You can override or Set a Config Item dynamically. If you simply look at $config['sess_expire_on_close'] = TRUE;
Whether to make the session to expire automatically when the browser window is closed.
Set it to true if user did not check the remember me checkbox. And the session will expire after user close the browser.
And if he checks the remember me checkbox, set $config['sess_expire_on_close'] to FALSE like
if($this->input->post('remember')) $this->config->set_item('sess_expire_on_close', '0'); //'remember' is checkbox name.
now session will not expire after browser is closed.
note: this solution is also tested on Opera, Mozilla, Chrome and ie9
Try this, may be it help you
/ **
* Escape String
*
* #param string
* #param bool whether or not the string will be used in a LIKE condition
* #return string
*/
public function escape_str($str, $like = FALSE)
{
if (is_array($str))
{
foreach ($str as $key => $val)
{
$str[$key] = $this->escape_str($val, $like);
}
return $str;
}
$str = is_resource($this->conn_id) ? mysql_real_escape_string($str, $this->conn_id) : addslashes($str);
// escape LIKE condition wildcards
if ($like === TRUE)
{
return str_replace(array($this->_like_escape_chr, '%', '_'),
array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
$str);
}
return $str;
}
// --------------------------------------------------------------------
Set in application/config/config.php:
$config['sess_expiration'] = 0;
$config['sess_expire_on_close'] = TRUE;
This should be OK.
Why don't you use the CI session function to do that
http://www.codeigniter.com/userguide2/libraries/sessions.html
I have problems with a session variable, users log into the app and then it sets a session variable but when it redirects to the next controller it isn't there.
At the moment I am not using the auth component, I think it is not correct, but I don't know how to apply it to my logic. This is because I dont log in users with username and password, they come authenticated from other website that gives me a ticket and a key to know who they are.
Here is my code of the UsersController where the app starts:
class UsuariosController extends AppController {
public $components = array('Session');
function beforeFilter() {
}
function login() {
$isLogged = false;
if(!empty($_POST['Ffirma']) ) {
$this->loginByTicket();
}
else if(!empty($this->data)) { //When users log by email it works perfectly
$this->loginByEmail();
}
}
private function loginByEmail() {
//Se busca el usuario en la base de datos
$u = new Usuario();
$dbuser = $u->findByEmail($this->data['Usuario']['email']);
//if doesn't exist user in db
if(empty($dbuser) ) {
$this->Session->setFlash('El usuario no existe en el sistema, consulte con el administrador.');
$this->redirect(array('controller' => 'usuarios', 'action' => 'login'));
exit();
}
$this->userIsCorrectlyLogged($dbuser);
}
function loginByTicket() {
$Fip = $_POST['Fip'];
$Frol = $_POST['Frol'];
$FidPersona = $_POST['Fidpersona'];
$Fticket = $_POST['Fticket'];
$Ffirma = $_POST['Ffirma'];
//Check sing
$f = $this->gen_firma($Frol, $FidPersona, $Fticket);
if( strcmp($f, $Ffirma) != 0 ) {
$this->Session->setFlash('Firma no válida.');
return;
}
//Check if ticket is valid
//1º Check if it exists on the db
$t = split('-',$Fticket);
$ticket = new Ticket();
$dbticket = $ticket->findById($t[0]);
if( strcmp($dbticket['Ticket']['valor'], $t[1]) != 0) {
$this->Session->setFlash('Ticket no válido.');
return;
}
//2º if Ip ok
if($Fip != $dbticket['Ticket']['ip']) {
$this->Session->setFlash('IP no válida.'.' '.$dbticket['Ticket']['ip'].' '.$Fip);
return;
}
$u = new Usuario();
$dbuser = $u->findById($dbticket['Ticket']['idPersona']);
$this->userIsCorrectlyLogged($dbuser);
}
private function userIsCorrectlyLogged($dbuser) {
$user = array('Usuario' => array(
'last_login' => date("Y-m-d H:i:s"),
'rol_app' => 1,
'nombre' => $dbuser['Usuario']['nombre'],
'email' => $dbuser['Usuario']['email'],
'apellidos' => $dbuser['Usuario']['apellidos'],
'id' => $dbuser['Usuario']['id']
) );
//Some stuff to determine rol privileges
$this->Session->destroy();
$this->Session->write('Usuario', $user);
$this->redirect(array('controller' => 'mains', 'action' => 'index'),null, true);
exit();
}
As you can see I make some controls before know that the user is correctly logged, and in user correctly logged I just save the session.
In my AppController I check if the user has logged in, but the session variable has already gone:
class AppController extends Controller {
public $components = array('Session');
function beforeFilter() {
//Configure::write('Security.level', 'medium'); //I've tried this that i saw somewhere
pr($this->Session->read()) // Session is empty
if($this->checkAdminSession()) {
$user = $this->Session->read('Usuario');
$email = $user['Usuario']['email'];
$usuario = new Usuario();
$dbuser = $usuario->findByEmail($email);
$respons = $usuario->getAccionesResponsable($dbuser['Usuario']['id']);
$this->set("hayacciones", true);
if( empty($respons) ) $this->set("hayacciones", false);
}
else {
$this->Session->setFlash('Necesitas identificarte para acceder al sistema.');
$this->redirect('/usuarios/login/');
exit();
}
}
function checkAdminSession() {
return $this->Session->check('Usuario');
}
}
I'm desperate, I've read a lot of documentation but I don't know how to solve this problem, could you give me any clue?
Thanks you very much, and sorry for my English!.
Note: I have discovered that if the security level is low it works:
Configure::write('Security.level', 'low');
But I dont like this solution...
You are overriding the beforeFilter() method. So, instead of using this:
<?php
class UsuariosController extends AppController {
function beforeFilter() {
}
you should do this:
<?php
class UsuariosController extends AppController {
function beforeFilter() {
parent::beforeFilter();
}
I was losing session information after a login call too and after searching for a while I found many different ways to fix my issue. I only regret that I don't fully understand what is causing the issue, but I imagine it has to do with php's session configuration.
As you mentioned, changing Security.level to low fixed the issue for me
Configure::write('Security.level', 'low');
Changing the session save configuration to php fixed the issue for me too:
Configure::write('Session', array(
'defaults'=>'cake',
));
And finally adding a timeout worked too (which I ended up using):
Configure::write('Session', array(
'defaults'=>'php',
'cookieTimeout'=> 10000
));
All these found in /app/Config/core.php
I post this hoping someone is able to make sense of what is going on underneath. I feel understanding the root of the issue would make a better job of answering your question.
I have the same problem. I tried all the suggestion. My Cache engine is Apc.
$this->__saveData($t);
debug($this->Session->read());// >>>>>> GOOD
$this->redirect(array('controller'=>'users','action'=>'main'));
}
}
}
function logout() {
$this->Session->destroy();
$this->Session->delete('User');
$this->redirect(array('controller'=>'logins','action'=>'login'));
}
function forgot() {
$this->layout = 'login';
}
private function __saveData($t)
{
$this->Session->write('User',$t['User']['name']);
$this->Session->write('User_name',$t['User']['firstname']);
$this->Session->write('User_id',$t['User']['id']);
$this->Session->write("User_Group",$t['Group']['name']);
$g = $this->Myauth->getPerm('User_Group'); // This is the array of permission w.r.t to the menu (key)
$this->Session->write("Permissions",$g);
debug($this->Session->read());
}
function main()
{
// Check permissions
$this->Myauth->check('users','login');
$username = $this->Session->read('User');
debug($this->Session->read( ));die(); <<<<< NOTHING
}
The funny thing is that yesterday it worked.
My php.ini has a simple extension=apc.so.
My core.php
Configure::write('Session.defaults', 'php');
Nothing change if I change the Security level. I will appreciate any direction.
EDIT
First solution: in my php.ini I had a bad value for session.referer_check (It was = 0 while it should be '').
But now, on the same server, one site is ok. Another one fires the error
Error: Call to undefined function apc_cache_info()
The two sites are separated and do not share any cakelib.
[SOLUTION FOUND]
For Cake > 2.2 and Chrome 24 I found this solution (I tried all the others found on the web). In your core.php:
Configure::write('Security.cookie', 'cakephpfdebackend');
Configure::write('Session.cookieTimeout', 0);
Configure::write('Session.checkAgent', false);
Configure::write('Session.cookie_secure',false);
Configure::write('Session.referer_check' ,false);
Configure::write('Session.defaults', 'php');
Actually, only the Session.cookieTimeout is required. The other settings are optional to solve the problem.
I had some issue with session on some pages . Can you check whether any space comes at the bottom of page after the php ending tag. When i faced this problem, i found session is missing due to a white space character in controller after the php ending tag . Please check this and let me know .
A possible reason for this problem is that the server clock is not synced with the client's clock and thus the cookie timeouts.
Can I change the database config per method in a controller?
$db['default']['db_debug'] = TRUE;
The default is TRUE, while I need to make it false in a certain method to catch the error and do something else (for example show 404 page).
When I tried $this->config->load('database') it fails.
Another question :
Can I check an incorrect query and catch it to some variables rather than displaying it to users other than setting the db_debug config to FALSE?
I checked the code of system/database/DB_Driver and found that:
$this->db->db_debug = FALSE;
will work in my controller to enable/disable the debug thing on the fly.
Expanding on the answer by comenk, you can extend the database class and implement various methods by which to achieve your goal.
First, you'll need to extend the core Loader class by creating a MY_Loader.php file
class MY_Loader extends CI_Loader
{
function __construct()
{
parent::__construct();
}
/**
* Load the Standard and/or Extended Database function & Driver class
*
* #access public
* #return string
*/
function database( $params = '', $return = FALSE, $active_record = NULL )
{
$ci =& get_instance();
if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($ci->db) AND is_object($ci->db))
{
return FALSE;
}
$my_db = config_item('subclass_prefix').'DB';
$my_db_file = APPPATH.'core/'.$my_db.EXT;
if(file_exists($my_db_file))
{
require_once($my_db_file);
}
else
{
require_once(BASEPATH.'database/DB'.EXT);
}
// Load the DB class
$db =& DB($params, $active_record);
$my_driver = config_item('subclass_prefix').'DB_'.$db->dbdriver.'_driver';
$my_driver_file = APPPATH.'core/'.$my_driver.EXT;
if(file_exists($my_driver_file))
{
require_once($my_driver_file);
$db = new $my_driver(get_object_vars($db));
}
if ($return === TRUE)
{
return $db;
}
// Initialize the db variable. Needed to prevent
// reference errors with some configurations
$ci->db = '';
$ci->db = $db;
}
}
By implementing the above this will allow you to create a MY_DB_mysqli_driver.php whereby mysqli is replaced by whatever driver you're using in your CI database.php config.
At this point you'd add comenk's answer to MY_DB_mysqli_driver.php
function debug_on() {
return $this->db_debug = TRUE;
}
function debug_off() {
return $this->db_debug = FALSE;
}
function in_error() {
return (bool) $this->_error_number();
}
Then in your model/controller,
$this->db->debug_off();
$this->db->query('SELECT * FROM `table`');
if( $this->db->in_error() ) {
show_404();
}
$this->db->debug_on();
you must add function on system/database/DB_driver.php
function debug_on()
{
$this->db_debug = TRUE;
return TRUE;
}
function debug_off()
{
$this->db_debug = FALSE;
return FALSE;
}
after that you can simply do this command to changes at run-time
$this->db->debug_off();
$this->db->reconnect();
$this->db->db_debug = 0; // 0: off, 1: on
That worx for me...
You can look at the $GLOBALS variable to locate this generic setting.
To hide bad SQL (and other errors) from users, you need to set the php error reporting level. CodeIgniter ships in basically development mode.
Go to index.php and replace this
error_reporting(E_ALL);
with this
error_reporting(0);
This is the quick way to do it. You can also implement this using a hook, so you don't have to touch CI files. You can also add logic to that hook so that it only sets it on the production server.
For debugging SQL, you can create a class that inherits from CI_Model, then create all your model classes to extend that class. In that class, you can add code for running queries that writes the queries to the log so that you can debug them easier. This won't help if the query itself is bad, but you should be able to figure that out before you get to that point.