auth_override_class_method_http not working with HMVC - codeigniter

Hi I have a config setting like below:
$config['auth_override_class_method_http']['admin']['auth']['post'] = 'none';
I am using this library for my HMVC: https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc
But when I checked with error handling on REST_Controller.php
it seems like /admin/auth is not recognised with REST
here is my REST code:
<?php
use Restserver\Libraries\REST_Controller;
defined('BASEPATH') OR exit('No direct script access allowed');
// This can be removed if you use __autoload() in config.php OR use Modular Extensions
/** #noinspection PhpIncludeInspection */
//To Solve File REST_Controller not found
require APPPATH . 'libraries/REST_Controller.php';
require APPPATH . 'libraries/Format.php';
class Auth extends REST_Controller {
function __construct()
{
// Construct the parent class
parent::__construct();
$this->load->model('Auth_model', 'Entity_model');
//$this->load->library('form_validation');
$this->load->library('validation');
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
}
public function index_get()
{
$return_message = [
'error' => 102,
'message' => "Entity Not Found"
];
$this->set_response($return_message, REST_Controller::HTTP_NOT_FOUND);
}
public function index_post()
{
/* Request, Response https://i.vgy.me/APy9PT.png */
$message = [
'status' => true,
'error_code' => 102,
'error_message' => 'success'
];
$this->set_response($message, REST_Controller::HTTP_CREATED);
// CREATED (201) being the HTTP response code
}
}
Although all of my GET/POST requests are working fine as it should be on the endpoint.
Could anyone suggest what I should do in order to get it working i.e.,
I want to use authentication on several API request but not on admin/auth [POST]

After days of debugging I realised that because I was using HMVC the router class in REST_Controller was not getting proper index.
So I edited my REST_controller to work with HMVC
Line 1498 : Add
if ( ! empty($auth_override_class_method_http[$this->router->module][$this->router->class][$this->request->method]))
{
// None auth override found, prepare nothing but send back a TRUE override flag
if ($auth_override_class_method_http[$this->router->module][$this->router->class][$this->request->method] === 'none')
{
return TRUE;
}
// Basic auth override found, prepare basic
if ($auth_override_class_method_http[$this->router->module][$this->router->class][$this->request->method] === 'basic')
{
$this->_prepare_basic_auth();
return TRUE;
}
// Digest auth override found, prepare digest
if ($auth_override_class_method_http[$this->router->module][$this->router->class][$this->request->method] === 'digest')
{
$this->_prepare_digest_auth();
return TRUE;
}
// Session auth override found, check session
if ($auth_override_class_method_http[$this->router->module][$this->router->class][$this->request->method] === 'session')
{
$this->_check_php_session();
return TRUE;
}
// Whitelist auth override found, check client's ip against config whitelist
if ($auth_override_class_method_http[$this->router->module][$this->router->class][$this->request->method] === 'whitelist')
{
$this->_check_whitelist_auth();
return TRUE;
}
}

Related

How to fetch session data in codeigniter?

I am trying to create a login process using codeigniter framework. Form validation is working but there is a problem in session. I can't fetch username after "Welcome-".
controller : Main.php
<?php
class Main extends CI_Controller
{
public function login()
{
$this->load->view('login');
}
public function login_validation()
{
$this->form_validation->set_rules('username','Username','required');
$this->form_validation->set_rules('password','Password','required');
if ($this->form_validation->run())
{
$username = $this->input->post('username');
$password= $this->input->post('password');
//model
$this->load->model('myModel');
if ($this->myModel->can_login($username,$password))
{
$session_data = array('username' => $username);
$this->session->set_userdata('$session_data');
redirect(base_url().'main/enter');
}
else
{
$this->session->set_flashdata('error','Invalid Username Or Password');
redirect(base_url().'main/login');
}
}
else
{
$this->login();
}
}
function enter()
{
if ($this->session->userdata('username')!=' ')
{
echo '<h2> Welcome- '.$this->session->userdata('username').'</h2>';
echo 'Logout';
}
else
{
redirect(base_url().'main/login');
}
}
function logout()
{
$this->session->unset_userdata('username');
redirect(base_url().'main/login');
}
}
?>
Add session library in the constructor
<?php
class Main extends CI_Controller
{
public function __construct()
{
parent::__construct();
// Load form helper library
$this->load->helper('form');
// Load form validation library
$this->load->library('form_validation');
// Load session library
$this->load->library('session');
$username = $this->session->userdata('username');
if (empty($username)) {
redirect('main/logout');
}
}
}
Another method you can load the session library in autoload.php file
File location: application/config/autoload.php
$autoload['libraries'] = array('database', 'email', 'session');
I suggest a slight code rearrangement for enter() that provides a better test for the user name using a tiny bit less code.
function enter()
{
if(empty($this->session->userdata('username')))
{
//base_url() accepts URI segments as a string.
redirect(base_url('main/login'));
}
// The following code will never execute if `redirect()` is called
// because `redirect()` does not return, it calls `exit` instead.
// So, you do not need an `else` block
echo '<h2> Welcome- '.$this->session->userdata('username').'</h2>';
echo 'Logout';
}
empty() will be true for an empty string, NULL, False and a couple of other things. In this case, you are most interested in an empty string or NULL. (empty() documentation HERE.)
You might want to consider adding 'trim' to your validation rules because it strips empty whitespace from the input string. That will remove the possibility of someone trying to input a username using only space characters.
Otherwise, your code should work. If it does not then it's very likely you do not have CodeIgniter sessions configured properly. There are many session setup questions answered here on Stack Overflow that will help you get it running.

Laravel Passport consuming own API fail

I'm building a SPA with Vue. My front-end and my back-end (Laravel) are in the same codebase. I want to approach my API (that is in my back-end) via the Laravel Passport Middleware CreateFreshApiToken. I'm approaching my sign in method in my AuthController via web.php.
My problem:
As soon as I'm successfully signed in via my sign in method I would expect that at this time Passport created the laravel_token cookie. This is not the case. The cookie is created after a page refresh. But as I said I'm building a SPA and that's why I don't want to have page refreshes.
What I want:
I want to sign in via my sign in method then use the Passport CreateFreshApiToken middleware. After that I want to use the (just created in the middleware) laravel_token cookie so that I can correctly and safely speak to my own API in my signed-in section of the SPA.
More information:
Kernel.php
// Code...
protected $middlewareGroups = [
'web' => [
// other middlewares...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
];
// Code...
AuthController.php
// Code...
public function login()
{
if (Auth::attempt(['email' => Input::get('email'), 'password' => Input::get('password')], true)) {
return response()->json([
'user' => Auth::user(),
'authenticated' => auth()->check(),
]);
}
return response()->json(['authenticated' => false], 401);
}
// Code...
Login.vue
// Code...
methods: {
login: function (event) {
event.preventDefault();
this.$http.post(BASE_URL + '/login', {
email: this.email,
password: this.password,
})
.then(function (response) {
localStorage.user_id = response.body.user.id;
router.push({
name: 'home'
});
});
},
},
// Code...
What goes wrong? This:
CreateFreshApiToken.php
// Code...
public function handle($request, Closure $next, $guard = null)
{
$this->guard = $guard;
$response = $next($request);
// I'm signed in at this point
if ($this->shouldReceiveFreshToken($request, $response)) { // returns false unless you refresh the page. That's why it won't create the laravel_token cookie
$response->withCookie($this->cookieFactory->make(
$request->user($this->guard)->getKey(), $request->session()->token()
));
}
return $response;
}
protected function shouldReceiveFreshToken($request, $response)
{
// both methods below return false
return $this->requestShouldReceiveFreshToken($request) &&
$this->responseShouldReceiveFreshToken($response);
}
protected function requestShouldReceiveFreshToken($request)
{
// $request->isMethod('GET') - returns false because it's a POST request
// $request->user($this->guard) - returns true as expected
return $request->isMethod('GET') && $request->user($this->guard);
}
protected function responseShouldReceiveFreshToken($response)
{
// $response instanceof Response - returns false
// ! $this->alreadyContainsToken($response) - returns false as expected
return $response instanceof Response &&
! $this->alreadyContainsToken($response);
}
// Code...
I assume it is possible what I want to achieve right? If yes, how?
I had the same issue, decided to stick to client_secret way. I guess it's not relevant for you now, but I've found 2 ways of receiving the laravel token without refresh:
1) sending dummy get request with axios or $http, whatever you use - token will get attached to response;
2) changing requestShouldReceiveFreshToken method in CreateFreshApiToken.php - replace return $request->isMethod('GET') && $request->user($this->guard); with return ($request->isMethod('GET') || $request->isMethod('POST')) && $request->user($this->guard);
function consumeOwnApi($uri, $method = 'GET', $parameters = array())
{
$req = \Illuminate\Http\Request::create($uri, $method, $parameters, $_COOKIE);
$req->headers->set('X-CSRF-TOKEN', app('request')->session()->token());
return app()->handle($req)->getData();
}

API REST don't work routes

I'm using codeigniter, for make an api rest, with the library that provide the oficial web site.
The problem is: the file routes.php doesn't redirect well. When i put localhost/API/1 into my browser apear the 404 error.
Here my controller "Apicontroller":
public function __construct() { //constructor //no tocar
parent::__construct();
$this -> load -> model("Modelocontrolador");
}
public function index_get() { //get all the info
$datos_devueltos = $this->Modelocontrolador->getPrueba(NULL, "Usuarios");
if(!is_null($datos_devueltos)){
$this->response(array("response" => $datos_devueltos), 200);
}else{
$this->response(array("response" => "No date"), 200);
}
}
public function find_get($id){ //select where
$datos_devueltos = $this->Modelocontrolador->getPrueba($id, "Usuarios");
if($id != NULL){
if(!is_null($datos_devueltos)){
$this->response(array("response" => $datos_devueltos), 200);
}else{
$this->response(array("response" => "No date"), 200);
}
}else{
$this->response(array("response" => "No dates for search"), 200);
}
}
public function index_post() { //insert in la table
if(! $this -> post("dato")){
$this->response(array("response" => "No enought info"), 200);
}else{
$datoID = $this -> Modelocontrolador -> save($this -> post("dato"),"UsuariosJJ");
if(!is_null($datoID)){
$this->response(array("response" => $datoID), 200);
}else{
$this->response(array("response" => "No found it"), 200);
}
}
}
public function index_put($id) { //"update"
if(! $this -> post("dato") || ! $id){
$this->response(array("response" => "No ha mandado informacion correcta para el update"), 200);
}else{
$datoID = $this -> Modelocontrolador -> update("Uid",$id,$this -> post("dato"),"UsuariosJJ");
if(!is_null($datoID)){
$this->response(array("response" => "Dato actualizado"), 200);
}else{
$this->response(array("response" => "Error modify"), 200);
}
}
}
public function index_delete($id) {
if(! $id){
$this->response(array("response" => "Not enought info"), 200);
}else{
$delete = $this-> Modelocontrolador -> delete("Uid",$id,"UsuariosJJ");
}
if(!is_null($delete)){
$this->response(array("response" => "Date delete"), 200);
}else{
$this->response(array("response" => "Error delete"), 200);
}
}}
And my routes file:
$route['default_controller'] = 'Apicontroller';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;
/*sub-rutas*/
/*---------*/
$route["Apicontroller"]["get"] = "Apicontroller/index"; //basico
$route["Apicontroller/(:num)"]["get"] = "Apicontroller/find"; //select
$route["Apicontroller"]["post"] = "Apicontroller/index"; //insert
$route["Apicontroller/(:num)"]["put"] = "Apicontroller/index/$1"; //update
$route["Apicontroller/(:num)"]["delete"] = "Apicontroller/index/$1"; //delete
If the browser request literally uses /API then routing needs to 'see' exactly that. Also, the route rules must be explicit with the method to be called. (Hopefully the code shown reflects the mapping you had in mind.)
/*sub-rutas*/
/*---------*/
$route["API"]["get"] = "Apicontroller/index_get"; //basico
$route["API/(:num)"]["get"] = "Apicontroller/find_get/$1"; //select
$route["API"]["post"] = "Apicontroller/index_post"; //insert
$route["API/(:num)"]["put"] = "Apicontroller/index_put/$1"; //update
$route["API/(:num)"]["delete"] = "Apicontroller/index_delete/$1"; //delete
Using the above routes I created some test code. Here are those files.
The much simplified Apicontroller.
class Apicontroller extends CI_Controller
{
function __construct()
{
parent::__construct();
}
function index_get()
{
echo "API index";
}
public function find_get($id)
{ //select where
echo "API find_get $id";
}
public function index_post()
{
echo 'API index_post';
}
public function index_put($id)
{ //"update"
echo "API put $id";
}
}
I don't believe that because your Apicontroller is extending a different Class the results would change. That may be a drastic assumption.
In order to test POST calls I used these two files.
First a Testpost.php controller
class Testpost extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->helper('form');
}
public function index()
{
$this->load->view("test");
}
}
The very simple view (test.php) loaded by the above.
<?php
echo form_open("API");
echo form_submit('mysubmit', 'Submit Post!');
echo form_close();
Directing the browser to localhost/testpost shows a page with a single submit button. Pressing the button results in a screen with the text "API index_post".
Sending the browser to localhost/API/3 produces a screen with the text "API find_get 3".
localhost/API produces "API index".
Now the interesting thing (not related to your problem, but interesting).
Given the default
$route['default_controller'] = 'Apicontroller';
and the route
$route["API"]["get"] = "Apicontroller/index_get";
I expected that directing the browser to the home page localhost would produce "API index". But it doesn't. It results in a 404. Due to that behavior it might be wise to be more explicit with default_controller
$route['default_controller'] = 'Apicontroller/index_get';
Or add an index() function to Apicontroller that calls $this->index_get().
I did not test PUT or DELETE as my server isn't setup to handle them. But as GET and POST seem to function, in a righteous world, they will work.
seems like you are using PHil's REST_Controller library with CI 2.x, correct ?
If so, I would recommend you to use what I like to call an "index gateway" because you can't do per-Method routing with CI2:
class Apicontroller extends REST_Controller
{
function index_gateway_get($id){
$this->get_get($id);
}
function index_gateway_put($id){
$this->put_put($id);
}
// This is not a "gateway" method because POST doesn't require an ID
function index_post(){
$this->post_post();
}
function get_get($id = null){
if(!isset($id)){
// Get all rows
}else{
// Get specific row
}
}
function put_put($id = null){
if(!isset($id)){
// a PUT withtout an ID is a POST
$this->post_post();
}else{
// PUT method
}
}
function post_post(){
// POST method
}
}
The routing to make this work is really easy:
$route["API/(:num)"] = "Apicontroller/index_gateway/$1";
That's all you need. Phil's REST Library will redirect to the correct index_gateway_HTTPMETHOD depending on which method is used.
Each index_gateway_HTTPMETHOD will then redirect to the correct method.
As far as I know, this trick is the only way to have CI2 use a single /API/ entry point that works for all HTTP Methods.

Route resources set before auth not working in Laravel 4

I added this in routes.php, expected it will check the authentication session for the page, however it is not working.
Route::resource('ticket', 'TicketController', array('before' => 'auth') );
Then I go to the controller, work in another way. It's work.
class TicketController extends BaseController {
public function __construct()
{
$this->beforeFilter('auth');
}
May I know where can get more documentation regarding the Route::resource(), what type of argument it able to accept?
OK... I found the answer.
in
\vendor\laravel\framework\src\Illuminate\Routing\Router.php
public function resource($resource, $controller, array $options = array())
{
// If the resource name contains a slash, we will assume the developer wishes to
// register these resource routes with a prefix so we will set that up out of
// the box so they don't have to mess with it. Otherwise, we will continue.
if (str_contains($resource, '/'))
{
$this->prefixedResource($resource, $controller, $options);
return;
}
// We need to extract the base resource from the resource name. Nested resources
// are supported in the framework, but we need to know what name to use for a
// place-holder on the route wildcards, which should be the base resources.
$base = $this->getBaseResource($resource);
$defaults = $this->resourceDefaults;
foreach ($this->getResourceMethods($defaults, $options) as $method)
{
$this->{'addResource'.ucfirst($method)}($resource, $base, $controller);
}
}
protected function getResourceMethods($defaults, $options)
{
if (isset($options['only']))
{
return array_intersect($defaults, $options['only']);
}
elseif (isset($options['except']))
{
return array_diff($defaults, $options['except']);
}
return $defaults;
}
as you can see, it only accept only and except arguement only.
If you want to archive the same result in route.php, it can be done as below
Route::group(array('before'=>'auth'), function() {
Route::resource('ticket', 'TicketController');
});

CodeIgniter: loading multiple models in the same controller

I searched the whole Internet and either there is no one mentioning my problem, or I'm stupid, or maybe it's just a bad day for coding.
What's the situation:
controller "source"
model "source"
model "login"
The "login" model is loaded from autoload.php, then in each controller's constructor I have $this->login->check(), which is checking if the user is logged in (obviously). Then in some of the methods I'm using the "source" model to connect to the database.
I tried loading both of the models from the autoload array, I also tried to load them in the way described here, but it's obviously for an old CI version (the thread is from 2008) and I tried all the possible ways I had in my mind.
Anyway, the result is this:
A PHP Error was encountered
Severity: Notice
Message: Undefined property: Source::$login
Filename: controllers/source.php
Line Number: 10
Fatal error: Call to a member function check() on a non-object in ...\application\controllers\source.php on line 10
Any ideas what I'm missing or how to fix it...? I'm stuck for hours and I don't have any ideas what I could do...
Edit 1: here is the code from the "source" controller:
class Source extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->model('login');
$this->login->check();
}
function index() {
// Pagination config, getting records from DB
$this->load->view('templates/layout', $data);
}
function add() {
$this->load->model('source', '', true);
$btn = $this->input->post('btn');
if(isset($btn)) {
// More form validation
if($this->form_validation->run() == TRUE) {
if($btn == "Add") {
// here I am supposed to use the source model...
}
}
}
$data['page'] = 'source_add';
$this->load->view('templates/layout', $data);
}
}
?>
Edit 2: login.php:
<?php
class Login extends CI_Model {
function __construct() {
parent::__construct();
}
function authenticate($username, $password) {
// the login script comes here
}
function logged() {
if($this->session->userdata('logged') == true) {
return true;
} else return false;
}
function check() {
if(!$this->logged()) {
redirect('/authentication');
}
}
}
?>
Conventionally, the classname of Models should end with _model, so it not collides with controllers with the same name, so try changing
class Login extends CI_Model {
to
class Login_model extends CI_Model {
I resolved this issue by utilizing the hooks and turned the login process into a controller, thereby being able to access user information and setting access levels.
First I added the following to the hooks.php file in the config folder
$hook['post_controller_constructor'][] = array('function' => 'check_login','filename' => 'authority.php','filepath' => 'hooks');
Then I have the following functions in a hook file called authority.php
[EDIT]Having reviewed this I am going to change it to a pre_controller_constructor and see if I can remove what seems to be a double page flash on initial construct.[/EDIT]
function check_login(){
$CI =& get_instance();
$is_logged_in = $CI->session->userdata('is_logged_in');
if(!$is_logged_in){
$unauth_pages = array(your unauthorized pages go here);
if(!in_array($CI->router->class,$unauth_pages)){
$CI->session->set_userdata('before_login_url',current_url());
redirect('login');
}
}
}
function check_authority(){
$CI =& get_instance();
if($CI->session->userdata('usergroupID') == 'SUPADMIN'){return;}
$page = $CI->router->class ;
$method = $CI->router->method;
$method = ($method=='index')?'':$method;
$unauth_pages = array(your unauthorized pages go here);
if(in_array($page,$unauth_pages))return;
$user_group = $CI->session->userdata('usergroupID');
$CI->load->model('user_model');
if($user_group == 'ADMIN' || $user_group == 'USER'){
if($CI->session->userdata('timezone') == ''){
date_default_timezone_set('Canada/Pacific');
} else {
date_default_timezone_set($CI->session->userdata('timezone'));
}
}
if( !$CI->user_model->authorized_content($CI->session->userdata('usergroupID'),$page, $method)){
redirect('unauthorized');
}
}
With the above I dont have to worry about checking on each page but instead utilize the ci framework to do the checking for me.. if its not in the unauth page array then it is a page that requires authorization checking.
Hope this works for you.

Resources