Codeigniter session authentification still allows restricted page to load - php

I am trying to restrict access from a page if the user is not logged in. The weird thing is that the webpage, when tried to be accessed by an unauthorised user, shows the page for the restricted access (with the "unauthorised, please login" message and, on the same page, it loads the members only page).
For the site controller:
class Site extends CI_Controller {
function __construct(){
parent::__construct();
$isLogged=$this->session->userdata('logged');
if($isLogged!='logged' || !isset($isLogged)){
$data['content']='denied';
$this->load->view('include/template', $data);
}
}
function members(){
$data['content']='memberArea';
$this->load->view('include/template', $data);
}
The login form :
class Login extends CI_Controller {
function index () {
$data['content']='loginForm';
$this->load->view('include/template', $data);
}
function validateMember(){
// load the model for the login authentification
$this->load->model('loginModel');
//query should also contain the user details
$query = $this->loginModel->authenticate();
//if it returns something:
if($query){
foreach ($query as $row){
//set user data to be passed
$details=array(
'username'=>$row->user_name,
'id'=>$row->id,
'logged'=>'logged',);
}
//set session
$this->session->set_userdata($details);
redirect('site/members');
}
else{
$this->index();
}
}
The model for the login is:
class LoginModel extends CI_Model {
function authenticate(){
//select active fields
$this->db->select('id, user_name, password');
// query to select from table where conditions
$query = $this->db->get_where('login', array(
'user_name'=>$this->input->post('username'),
'password'=>$this->input->post('password'),),
1);
//if it finds something...must ask if i need to manually close the database with $this->db->close();
if($query->num_rows()==1){
foreach($query->result() as $row){
$data[]=$row;
}
return $data;
}
else {return false;}
}
}
My tests showed that site continues to call the other functions even if the construct function fails. the session does contain the data. If i use die() or exit() the webpages loads blank. many thanks in advance!
PS: the views only have <p> in them, nothing fancy.

I can see two solutions to this problem.
Redirect to another page that doesn't check for authentication. You can use redirect(<url>); from the URL helper.
Use exit() in the __construct() with the buffered output flushed. When you call $this->load->view() the data is sent to a buffer called output in CodeIgniter. You can write that buffer by doing :
if($isLogged!='logged' || !isset($isLogged)){
$data['content']='denied';
$this->load->view('include/template', $data);
// Write the output.
echo $this->output->get_output();
// Stop the execution of the script.
exit();
}
or you can by-pass the output buffer with:
if($isLogged!='logged' || !isset($isLogged)){
$data['content']='denied';
// Writes the content instead of sending it to the buffer.
echo $this->load->view('include/template', $data, true);
// Stop the execution of the script.
exit();
}
Pick which ever you want.

The constructor method does not "fail", the if statement is simply executed (because the condition is true) and there is no reason why this would prevent other methods from being executed.
Inside the constructor method, at the end of the if block, you may insert an exit statement, this way the page would probably behave as you are expecting.
See here http://php.net/manual/en/function.exit.php

You're testing if there is a query, not if the query actually return anything. Check the num rows and make sure they equal 1, doing greater than 0 is bad practice on login checks.
if($query->num_rows==1){
PS. Post your model code as well, it's possible the error is there and it's returning a result even when it shouldn't.

Related

Laravel allow route to be accessible only from another route

I have /signup/select-plan which lets the user select a plan, and /signup/tos which displays the terms of services. I want /signup/tos to be only accessible from /signup/select-plan. So if I try to go directly to /signup/tos without selecting a plan, I want it to not allow it. How do I go about this?
In the constructor, or the route (if you are not using contructors), you can check for the previous URL using the global helper url().
public function tos() {
if ( !request()->is('signup/tos') && url()->previous() != url('signup/select-plan') ) {
return redirect()->to('/'); //Send them somewhere else
}
}
In the controller of /signup/tos which returns the tos view just add the following code:
$referer = Request::referer();
// or
// $referer = Request::server('HTTP_REFERER');
if (strpos($referer,'signup/select-plan') !== false) {
//SHOW THE PAGE
}
else
{
dd("YOU ARE NOT ALLOWED")
}
What we are doing here is checking the HTTP referrer and allowing the page access only if user comes from select-plan
You are need of sessions in laravel. You can see the following docs to get more info: Laravel Sessions
First of all you need to configure till how much time you want to have the session variable so you can go to your directory config/sessions.php and you can edit the fields 'lifetime' => 120, also you can set expire_on_close by default it is being set to false.
Now you can have following routes:
Route::get('signup/select-plan', 'SignupController#selectPlan');
Route::post('signup/select-token', 'SignupController#selectToken');
Route::get('signup/tos', 'SignupController#tos');
Route::get('registered', 'SignupController#registered');
Now in your Signupcontroller you can have something like this:
public function selectPlan()
{
// return your views/form...
}
public function selectToken(Request $request)
{
$request->session()->put('select_plan_token', 'value');
return redirect('/signup/tos');
}
Now in signupController tos function you can always check the session value and manipulate the data accordingly
public function tos()
{
$value = $request->session()->get('select_plan_token');
// to your manipulation or show the view.
}
Now if the user is registered and you don't need the session value you can delete by following:
public function registered()
{
$request->session()->forget('select_plan_token');
// Return welcome screen or dashboard..
}
This method will delete the data from session. You can manipulate this. You won't be able to use in tos function as you are refreshing the page and you want data to persist. So its better to have it removed when the final step or the nextstep is carried out. Hope this helps.
Note: This is just the reference please go through the docs for more information and implement accordingly.

Laravel - return a redirectResponse selectively generated in a function

Part of my application is a multi-stage checkout process; during the latter pages of this I first run a sanity check on each request to verify the user actually has some items in their basket: if not they're sent back to the beginning.
I have a controller function like this which is called from multiple routes for DRY purposes.
private function checkBasketFull($request)
{
if (self::isBasketEmpty($request)) {
return redirect('/')->with('status', config('app.empty_basket_message'));
}
}
When I call it, I can't just do:
self::checkBasketFull($request);
because without a return the redirect doesn't fire, only the session data is sent.
And I can't do:
return self::checkBasketFull($request);
because that will give an error if there's no redirect or abort the method if checkBasketFull returns anything else.
My current (working) code is:
$check = self::checkBasketFull($request);
if ($check) {
return $check;
}
Is there an alternative way of writing this on a single line, or modifying the checkBasketFull function, so the redirect will occur if the basket is empty but execution will continue as normal if it isn't?
Either use this:
if ($redirect = self::checkBasketFull($request)) return $redirect;
Or throw an error and catch it in the global error handler.
However, instead of returning and checking that for a redirect like that, I'd much rather keep it as two completely separate methods:
public function someRoute(Request $request)
{
if ($this->isBasketEmpty($request)) return $this->redirectBasketEmpty();
// Continue processing this request...
}
protected function isBasketEmpty(request)
{
// run your login here...
}
protected function redirectBasketEmpty()
{
return redirect('/')->with('status', config('app.empty_basket_message'));
}
Feels cleaner to me.

PHP session being lost

I'm trying to implement and authentication system with jQuery and PHP. All the php work is made in the controller and datahandler class. There is no php code inside the .html files, all the values in .html files are rendered via jQuery that request the data from php server. So what I'm trying to do is:
When user clicks the login button, the jQuery makes a call to the authenticate() method in my controller class, it checks if the user is correct and stuff, and if it is, start the session and set the user_id on the session so I can access it later, and returns the userId to the jQuery client again.
After that, if everything is fine, in jQuery I redirect it to the html file. On the html file I call a jQuery from the <script> tag that will handle other permissions. But this jQuery will access the method getPermissionString (from the same class of authenticate() method mentioned before), and it will need to get the session value set in authenticate method.
The Problem:
When I try to get the session value inside getPermissionString() it says:
Notice: Undefined variable: _SESSION
I've tried to check if the session is registered in the second method, but looks like it's not. Here is my PHP code.
Any idea? Thanks.
public function authenticate($login, $password)
{
$result = $this->userDataHandler->authenticateUser($login, $password);
if(is_numeric($result) && $result != 0)
{
session_start();
$_SESSION["uid"] = $result;
if(isset($_SESSION["uid"]))
{
echo "registered";
$userId = $_SESSION["uid"];
}
else
{
echo "not registered";
}
echo $result;
}
else
{
echo 0;
}
}
public function getPermissionString()
{
if(isset($_SESSION["uid"]))
{
echo "registered";
$userId = $_SESSION["uid"];
}
else
{
echo "not registered";
}
}
Before you can access $_SESSION in the second function you need to ensure that the program has called session_start() beforehand. The global variable is only populated when the session has been activated. If you never remember to start a session before using it then you can change the php.ini variable below:
[session]
session.auto_start = 1
Further, you said that you're using a class for your code. In this case you can also autos tart your session each time the class in created by using magic methods:
class auth {
function __construct() {
session_start();
}
function yourfunction() {
...
}
function yoursecondfunction(){
...
}
}
If you don't have session.auto_start enabled, and authenticate and getPermissionString are called on two different requests, you need to call session_start() in each function.
If you need more information on how the session ID is passed, just read Passing the Session ID
You should not use that function if session is not started. So throw an exception:
public function getPermissionString()
{
if (session_status() !== PHP_SESSION_ACTIVE)
{
throw new Exception('No active session found.');
}
if(isset($_SESSION["uid"]))
{
echo "registered";
$userId = $_SESSION["uid"];
}
else
{
echo "not registered";
}
}
This ensures the pre-conditions of your functions are checked inside the function so you don't need to check it each time before calling the function.
You will now see an exception if you wrongly use that function and it will give you a backtrace so you can more easily analyze your code.
For php sessions to work you have to call session_start() every time you script is requested by the browser.

codeiginter no direct access to functions

I'm having this problem about direct access to functions: for example I have this code:
controller users
function index(){
//this is my users index view, user can add,edit,delete cars
}
function details($id){
//a function where 1 car can be viewed in detail..
function add(){
//function to add car
}
Now if I go to address bar and type. localhost/myapp/users/detail it will go to the url and echo an error since $id is null. What I want is only the index is directly accessible if a user would type in the address bar. I don't want the users to go directly to myapp/users/add, etc..
CI Controller functions always must be able to handle user input (i.e. url segments), which means anyone can type in whatever they wish and make a request. You can't stop that. The best practice is to either:
Always provide default arguments
Use the URI class to get your parameters, or func_get_args()
Always validate the presence of and integrity of arguments passed to the controller, as you would with any other user input
Since it's much more common, accepted, and easier to read - just make sure to always provide defaults and validate them.
An example with your controller:
function index() {
//this is my users index view
//user can add,edit,delete cars
}
function details($id = NULL) {
if ( ! $id) {
// No ID present, maybe redirect without message
redirect('users');
}
$user = $this->user_model->get($id);
if ( ! $user) {
// ID present but no user found, redirect with error message
$this->session->set_flashdata('error_message', 'User not found');
redirect('users');
}
// We found a user, load view here etc.
}
function add() {
// Check for the presence of a $_POST value
// You could also use the Form_validation lib here
if ( ! $this->input->post('add_car')
{
$this->session->set_flashdata('error_message', 'Invalid request');
redirect('users');
}
// Try to add the car here and always redirect from here
}
The only other way is to make the method private or use CI's _underscore() naming as suggested (making it inaccessible from the url). You can still call the function in other methods if you wish, as in:
function index() {
if ($this->input->post('add_car')
{
// Call the private "_add" method
$this->_add();
}
// Load index view
}
So to make a long story short: You can't stop the requests from being made, you can only decide what to do when the request is invalid.
Add an underscore before the names of functions you want to hide:
function _details($id){
//a function where 1 car can be viewed in detail..
}
function add(){
//function to add car
}

How to replace "Login" button with user name in CodeIgniter

I'm trying to create a universal header for a website built on CodeIgniter, and I'm having trouble figuring out the code that will switch the 'Login' link for the user's name (with a link to the profile page) after the user logs in.
In the controller functions, I've tried the following code:
if(!$this->session->userdata($userSessionVar))
{
$data['header_output'] = "<li><a href='" . base_url() . "index.php/main/login'>Login</a></li>";
} else
{
$data['header_output'] = $this->session->data('userFirstName');
}
(I realize this is incomplete, based on my designs, but it's just to test.) $userSessionVar holds the value "logged in" once logged in. Probably not the best way to do that. And that doesn't seem to work (and I pass the $data to the view). I've also tried making a custom function:
function check_login()
{
$CI =& get_instance();
$userSessionVar = 'logged_in';
if( ! $CI->session->userdata($userSessionVar))
{
return false;
} return true;
}
And then use the true/false return to structure the $header_output variable. None of these seem to work. I'm new to CodeIgniter and have some intermediate level of PHP/HTML/CSS, etc. I'm sure I'm missing something obvious and would appreciate any help, as well as a heads-up on how to avoid including the code in every controller function.
The variable $userSessionVar is only available within the function check_login(), so when you try to use it outside of the function, it will be blank (and therefore useless).
I recommend that you simply use $this->session->userdata('logged_in') and $CI->session->userdata('logged_in') rather than using the variable $userSessionVar to store what appears to be a constant value.
Also, you have an error in your code. You need to replace $this->session->data('userFirstName') with $this->session->userdata('userFirstName')
Here's how I typically deal with user data. First, add auth.php to the models folder:
<?php
class Auth extends Model {
private $user_data = false;
function Auth() {
parent::Model();
if ($this->input->post('action') == 'login') $this->login();
else if ($auth_id = $this->session->userdata('auth_id')) {
$user = // load user data from the database into the variable $user
if ($user) {
$this->user_data = $user;
} else $this->session->unset_userdata('auth_id');
}
}
function login() {
// process POST, check with database, and then store user_id using
// $this->session->set_userdata('auth_id', $user_id_here)
}
function me() {
return $this->user_data? (object)$this->user_data : false;
}
}
?>
Then, auto-load the model. To do this, edit config/autoload.php like so:
$autoload['model'] = array('auth');
Now your IF statement could look like this:
if ($me = $this->me()) $data['header_output'] = $me->userFirstName;
else $data['header_output'] = '<li>Login</li>';
in your model auth.php you've got the statements
class Auth extends Model
and
parent::Model();
With CodeIgniter, should these not be "CI_Model"...?

Categories