CodeIgniter sess_destroy does not delete user_data session? - codeigniter

I am creating test case for my CodeIgniter app. However I just found something that I thought should not be happen :
in login.php controller :
public function logout()
{
$this->session->sess_destroy();
redirect('/');
}
So I just created a test to just make sure that session is really destroyed :
public function test_logout()
{
$this->CI = set_controller('login');
// make sure that all session is destroyed
$this->CI->session->set_userdata('test_session', 'some_value');
$this->CI->logout();
// userdata 'test_session' should be removed!
$this->assertTrue(($this->CI->session->userdata('test_session')==null || $this->CI->session->userdata('test_session')==''));
}
However I find that upon running the test case, my test case fails! Upon debug on the last line of test case, I found that the userdata is still exist with value = 'some_value'. I thought that sess_destroy should also delete all the set user data, as per what they described in their website documentation:
This function should be the last one called, and even flash variables will no longer be available. If you only want some items destroyed and not all, use unset_userdata().
I am using Kenji's CIUnit for unit testing.
Is this the correct behaviour or is there something that I missed?

Just found that CIUnit routes the Session to CIU_Session instead of original CodeIgniter's CI_Session. It miss a line that CI_Session does :
$this->userdata = array();
So turns out this is CIUnit's issue instead of CodeIgniter's. Create an issue in their bitbucket page.

Related

Cookie-less Laravel sessions

We have a small quiz type functionality built in Laravel to be embedded in a site via an iframe served from a separate domain (to work around CMS limitations).
It uses sessions to keep track of the user's progress in the quiz. This doesn't work in Safari (Mac/iOS), I believe because Apple disable cookies issued from within an iframe.
Assuming that limitation is one we're stuck with, has anyone had any success making Laravel sessions cookie-less? I found this code on Github, which looks promising but is old enough (and incompatible with current Laravel) that I can't tell if it's going to be a solution.
In case it helps someone else, or anyone can see any silly errors in my code, this is what I did (an adaption of the Github code, to work in Laravel 9).
I extended StartSession and SessionServiceProvider (to use my new StartSession). I created an override for handleStatefulRequest in Start Session, and where it adds a cookie to the reponse (it calls addCookieToResponse) did this:
if ($request->cookies->get($session->getName())) {
$this->addCookieToResponse($response, $session);
}
else {
// Add session ID to header
$this->addIdentifierToResponse($response, $session);
}
That new function looks like this:
protected function addIdentifierToResponse(Response $response, Session $session)
{
if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
$response->headers->set("X-Session-Token", $session->getId());
}
}
I also changed the getSession method to get the session ID from that newly set header (when no cookie found):
public function getSession(Request $request)
{
return tap($this->manager->driver(), function ($session) use ($request) {
if ($request->cookies->get($session->getName())) {
Log::debug('1. Set session ID from cookie');
$session->setId($request->cookies->get($session->getName()));
}
else if ($request->headers->get("X-Session-Token", $request->input("sess_id"))) {
$sessionToken = $request->headers->get("X-Session-Token", $request->input("sess_id"));
$session->setId($sessionToken);
}
});
}
I created a Github repo containing the whole thing.

Laravel Auth::User() performance

I have a question that whenever we call Auth::User() then its execute the query to fetch record or it have a saved instance?
Example 1
echo Auth::User()->name;
echo Auth::User()->email;
echo Auth::User()->phone;
Example 2
$userInfo=Auth::User();
echo $userInfo->name;
echo $userInfo->email;
echo $userInfo->phone;
Which one should be used performance wise?
Answer and example
Call to the database will be made only the first time you call Auth::user(), after that Laravel will store the user data and each call after that will get the stored instance rather then query the database again.
You can take a look at the vendor\laravel\framework\src\Illuminate\Auth\SessionGuard.php file under user() method. This is the code I copied from my current project which uses Laravel 7.x and this is the function called by Auth::user().
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if ($this->loggedOut) {
return;
}
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($this->user);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
$this->user = $this->userFromRecaller($recaller);
if ($this->user) {
$this->updateSession($this->user->getAuthIdentifier());
$this->fireLoginEvent($this->user, true);
}
}
return $this->user;
}
Debugbar options
Also as the comment before me pointed out, it is good to download Debugbar for Laravel https://github.com/barryvdh/laravel-debugbar. It will enable you to take a look into queries being executed, views being rendered, requests being sent, and much more.
Other option is Laravel's native solution https://laravel.com/docs/8.x/telescope. I have never personally used it and IMO the first one is simpler to use.
Notes on good practice
Although both examples will essentially do the same thing, I think it is much better to use the second example. Not because of the performance, but rather to make your code readable in future. If you define the user only one time and assign Auth::user() result to it, in future it will be more obvious what it is, and plus, you can easily change what are you assigning to the $userInfo variable (maybe you want to get user from another guard in future, etc.) without having to change it on N places throughout the code.

Laravel 5: Sessions not working the way they should

On top of every controller and routes.php I used:
use Illuminate\Support\Facades\Session;
In routes.php I set the session using:
Session::put('key', 'value');
In a controller I want to call the session value of key using:
echo Session::get('key');
But once I set a new value to key in routes.php and call it in a controller, I still get the first value and not the new one. If I echo the the session using Session::all() in routes.php after setting it, I see the new value, but in a controller it flips back to the first value. I even tried using below in routes.php before setting the new value, but without success.
Session::forget('key');
Am I forgetting something here?
Using regular PHP $_SESSION my routes.php looks like this:
$slug = $_SERVER['REQUEST_URI'];
$slug = explode('/', $slug[0]);
if(in_array($slug[1], Language::all()->lists('iso'))) {
$_SESSION['language'] = $slug[1];
if(!$slug[2]) {
$_SESSION['slug'] = 'home';
Route::any('/{slug}', ['as' => 'pages.page', 'uses' => 'PagesController#page']);
} else {
if($slug[2] != 'dashboard' && $slug[2] != 'migrate' && $slug[2] != 'form-send') {
if (in_array($slug[2], ElementValue::where('element_field_id', 2)->lists('value_char')) && !isset($slug[3])) {
$_SESSION['slug'] = $slug[2];
Route::any('/{slug}', ['as' => 'pages.page', 'uses' => 'PagesController#page']);
} else {
$_SESSION['slug'] = 'home';
Route::any('/{slug}', ['as' => 'pages.page', 'uses' => 'PagesController#page']);
}
}
}
}
Where in routes.php are you setting the session value? It sounds like you're doing something like this:
Session::put('key', 'value');
Route::get('my-route', 'MyController#doSomething');
and then doing this:
class MyController {
public function doSomething()
{
Session::get('key');
}
}
Is that correct? If so, read on...
I'm no expert on the Laravel request lifecycle (for more, see the documentation), but it doesn't surprise me that this doesn't work. The way I think about it is this: the routes.php file is loaded and executed early in the life cycle - probably first - since it tells the application what code to execute next (ie. what do when a particular request is received). And when I say "early in the life cycle", I mean early - like before sessions are initialized. I believe that the Session::put call is simply being ignored, since at the time when you're setting the value, the session does not exist.
You may want expand your question with a little more detail about what you're trying to accomplish - there has got to be a better way to do it.
EDIT - in response to the comments below...
I am not saying you should touch the $_SESSION superglobal - that's a bad idea because I'm not even sure that Laravel uses the native PHP session facility and you have no guarantee that whatever you do will continue to work in the future.
It's not clear what you're trying to do, but to me this sounds like a value that does not belong in the session.
By placing the Session::put in the routes.php file, it sounds like you have some value that's important and should be set for every session and every request
If that's the case, and it's a static value, then it's not a session value, it's a configuration value.
If, instead, it's a dynamic value and/or it changes depending on which user is associated with a session, then you can set it in one of several places:
if you're using controller-based routing, you could set this in the controller constructor, although I wouldn't recommend it, because you will probably have to do it for several controllers, leading to code duplication
if you're using closures in your routes, set it there. E.g.
Route::get('some/route', function () {
Session::put('key', 'value');
// this works, because the closure isn't executed until after
// the application is initialized
});
you could also do it in middleware
or in a service provider (although I'm not certain that sessions would be available when the service providers are executed).
The best option is probably middleware - this would allow you to set (or calculate) the session value in one place in your code and also associate it with particular routes, if you don't need it for all routes.
Don't use $_SESSION in laravel. Uses the laravel Session class. See the following post How to access the globals $_SESSION and $_COOKIE from a laravel app?
Also, all your if logic should not be living in routes.php. You should add that to middleware to filter your routes.
Also, you are really making this hard for yourself. Laravel provides most of what you need in convenient helper classes e.g. Request::url(), Request::getHost(), Request::getLocale(). Have a read through the docs and get familiar with "The Laravel Way" it will be much easier and things will then work as you expect.
I moved the logic to the controller and now my routes are this simple:
Route::pattern('slug', '[a-zA-Z0-9\-_\/]+');
$slug = Request::path();
if(isset($slug)) {
Route::any('/{slug}', 'PagesController#index')->where('slug', '[a-zA-Z0-9\-_\/]+');
}
The session is stored in the PagesController and used further in the application. Thanks for your help guys.

redirect is not working in codeigniter _construct function

I am facing a problem using redirect in _construct function,
In timesheet controller I wrote the following code and I am getting an error in the browser "
This problem can sometimes be caused by disabling or refusing to
accept
cookies.
Here is my code
class Timesheet extends MY_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('timesheet_model');
//$this->load->library('auth');
$username=$this->session->userdata('logged_in');
//$this->load->model('login_model');
if($username['fullname']!=""){
redirect('timesheet');
}
else{
redirect('login');
}
}
Please help me to find a way to get rid of this problem.
Thanks.
It looks to me like you're going around in a loop.
You check to see if the fullname element in your $username array is empty and, if it is, you redirect back to the same controller. I'm willing to bet it goes around in a circle like that for a while before the webserver throws up the error you mention.
If I'm reading what you're trying to do correctly, wouldn't you call another function within your Timesheet constructor if the fullname element is present to show whatever information it is that you're trying to display?
I'd suggest changing your logic to do the following:
if($username['fullname'] == ""){
redirect('login');
}
else{
//go to another controller method here
}
OK. I got it. The error is just because of I am redirecting to same controller where I have written the code. Everytime when it redirects to timesheet it enters into the construct function and it redirects again to timesheet. And the same thing is working like an endless loop.
So the fault was redirecting to the same controller.
I
use redirect like this : redirect('timesheet', 'refresh');

Anyway to redirect to previous URL after registration in Joomla?

I am developing a component that required login at some level, then if user is not logged in, I placed a login link, that take user to login page with following in query string.
return=<?php echo base64_encode($_SERVER['REQUEST_URI']);?>
After login, it comes back to that page, but is there some way to tackle this if user is not registered and user starts registering? Is there some way to do this without changing some thing in Joomla it self? like by just setting some thing in cookie e.t.c. Or I will need to change some thing in Joomla Registration component or module. Or is there some plugin for that?
Any response will be appreciated, please tell what ever way you know so that it may give me some better clue.
In your component you could try to store the referrer in the Joomla! session - I don't believe the session changes or is replaced during login. I haven't had time to try this but it should work.
To Save:
$session = JFactory::getSession();
$session->set('theReferrer', $_SERVER['HTTP_REFERER'], 'mycomponentname');
To Retrieve:
$session = JFactory::getSession();
$redirectTo = $session->get('theReferrer', '', 'mycomponentname');
Then you can just use a setRedirect before you return.
$this->setRedirect($redirectTo);
You can achieve this with a plugin (at least in Joomla 3.x - not sure how far back this will work off-hand). Key here is the onUserAfterSave event, which tells you whether the user is new or existing.
I wrote the code below some time ago, so can't recall the exact reason the redirect could not be done from within the onUserAfterSave event handler, but I think the redirect is subsequently overridden elsewhere in the core Joomla user management code if you try to do it from there, hence saving a flag in the session and checking it in a later event handler.
class PlgUserSignupRedirect extends JPlugin
{
public function onUserAfterSave($user, $isnew, $success, $msg)
{
$app = JFactory::getApplication();
// If the user isn't new we don't act
if (!$isnew) {
return false;
}
$session = JFactory::getSession();
$session->set('signupRedirect', 1);
return true;
}
function onAfterRender() {
$session = JFactory::getSession();
if ($session->get('signupRedirect')) {
JFactory::getApplication()->redirect($_SERVER['HTTP_REFERER']);
$session->clear('signupRedirect');
}
}
}

Resources