Skip to content

Commit

Permalink
Add support for middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
ngekoding committed May 24, 2021
1 parent 88fbda2 commit 03c8145
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 4 deletions.
11 changes: 9 additions & 2 deletions README.md
Expand Up @@ -24,12 +24,19 @@ If you loves CodeIgniter 3 & Vue.js, you must try this one to make your life eas

I try to keep this project as simple as possible, so you can making a changes to suit your needs. No need to install a bunch of libraries for making something simple.

### Restful API support: response helper, ajax request validation ✅
### Restful API support: response helper, ajax request validation ✅
- application/config/routes.php
- application/core/MY_Controller.php
- application/controllers/api/*

### Middlewares
- Not implemented yet!
- application/core/MY_Controller.php
- application/middlewares/*
- application/helpers/auth_helper.php
- application/config/config.php
- application/config/routes.php
- application/config/autoload.php
- application/controllers/api/v1/Auth.php
- application/controllers/api/v1/User.php

Powered by [ngekoding.github.io](https://ngekoding.github.io)
2 changes: 1 addition & 1 deletion application/config/autoload.php
Expand Up @@ -58,7 +58,7 @@
|
| $autoload['libraries'] = array('user_agent' => 'ua');
*/
$autoload['libraries'] = array();
$autoload['libraries'] = array('session');

/*
| -------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion application/config/config.php
Expand Up @@ -380,7 +380,7 @@
$config['sess_driver'] = 'files';
$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = NULL;
$config['sess_save_path'] = sys_get_temp_dir();
$config['sess_match_ip'] = FALSE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;
Expand Down
1 change: 1 addition & 0 deletions application/config/routes.php
Expand Up @@ -65,4 +65,5 @@
*/
$route['api/v1/users']['GET'] = 'api/v1/user';
$route['api/v1/users/(:num)']['GET'] = 'api/v1/user/show/$1';
$route['api/v1/test-login/(:any)']['POST'] = 'api/v1/auth/test_login/$1';
$route['api/(.*)'] = '404';
33 changes: 33 additions & 0 deletions application/controllers/api/v1/Auth.php
@@ -0,0 +1,33 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Auth extends MY_Controller {

public function __construct()
{
parent::__construct();

$this->load->helper('auth');
}

public function test_login($role)
{
/**
* Make userdata object as you want
* For this example, we need a role that will used
* for role middleware
*/
$userdata = (object) [
'id' => 1,
'name' => 'Nur Muhammad',
'role' => $role
];

set_userdata($userdata);

return send_response([
'success' => TRUE,
'data' => $userdata
]);
}
}
23 changes: 23 additions & 0 deletions application/controllers/api/v1/User.php
Expand Up @@ -6,6 +6,29 @@ class User extends MY_Controller {
// All method must called by ajax request
protected $ajax_request_only = TRUE;

/**
* Limit method access with middlewares
* For this example:
* - index can only accessed by authenticated user (whatever it's role)
* - show can only accessed by authenticated user with role 'admin'
*
* You can use auth controller to make a login test with spesific role
* example.com/index.php/api/v1/auth/test-login/admin
*/
protected $middlewares = [
['name' => 'auth'],
[
'name' => 'role',
'behavior' => [
'type' => 'only',
'methods' => ['show']
],
'extras' => [
'roles' => ['admin']
]
]
];

// Just example data
// In the real case, this data from the DB
private $users = [
Expand Down
64 changes: 64 additions & 0 deletions application/core/MY_Controller.php
Expand Up @@ -11,13 +11,28 @@ class MY_Controller extends CI_Controller {
*/
protected $ajax_request_only = FALSE;

/**
* Middlewares to manage access
*
* Formatted as an array that contains these keys:
* - name Middleware name (required)
* - behavior An array of validation behavior. (optional)
* Format: ['type' => except or only, 'methods' => [...]]
* If empty or not defined, will applied to all methods
* except: applied to all methods except the given
* only: applied only to the given methods
* - extras Extra paramaters to passing to the run method. (optional)
*/
protected $middlewares = [];

public function __construct()
{
parent::__construct();

$this->load->helper('api');

$this->ajax_request_validator();
$this->run_middlewares();
}

/**
Expand Down Expand Up @@ -48,4 +63,53 @@ private function ajax_request_validator()

if ($error) send_bad_request('Ajax request only!');
}

/**
* Run middlewares validation
*/
private function run_middlewares()
{
foreach ($this->middlewares as $middleware) {
$name = $middleware['name'];
$behavior = isset($middleware['behavior'])
? $middleware['behavior']
: NULL;
$extras = isset($middleware['extras'])
? $middleware['extras']
: [];

$run = TRUE;
if (!empty($behavior)) {
$type = $behavior['type'];
$methods = $behavior['methods'];

// Get current requested method
$requested_method = $this->router->fetch_method();

if ($type == 'except' && in_array($requested_method, $methods)) {
$run = FALSE;
} elseif ($type == 'only' && !in_array($requested_method, $methods)) {
$run = FALSE;
}
}

if ($run) {
$class_name = ucfirst(strtolower($name)) . '_middleware';
$file_name = $class_name . '.php';
$file_path = APPPATH . 'middlewares/' . $file_name;
if (file_exists($file_path)) {
require $file_path;
$ci =& get_instance();
$obj = new $class_name($ci, $this, $extras);
$obj->run();
} else {
if (ENVIRONMENT == 'development') {
throw new Exception('Unable to find middleware: ' . $file_name);
} else {
throw new Exception('Sorry something went wrong.');
}
}
}
}
}
}
21 changes: 21 additions & 0 deletions application/helpers/auth_helper.php
@@ -0,0 +1,21 @@
<?php

define('AUTH_SESS_NAME', 'app_logged_in');

function set_userdata($data) {
$_ci =& get_instance();
$_ci->session->set_userdata(AUTH_SESS_NAME, $data);
}

function userdata() {
$_ci =& get_instance();

$userdata = $_ci->session->userdata(AUTH_SESS_NAME);

return $userdata;
}

function clear_userdata() {
$_ci =& get_instance();
$_ci->session->unset_userdata(AUTH_SESS_NAME);
}
33 changes: 33 additions & 0 deletions application/middlewares/Auth_middleware.php
@@ -0,0 +1,33 @@
<?php

class Auth_middleware {

private $ci;
private $controller;
private $extras;

/**
* Accepting codeigniter instance, current controller & extras
*/
public function __construct($ci, $controller, ...$extras)
{
$this->ci = $ci;
$this->controller = $controller;
$this->extras = $extras;
}

public function run()
{
$this->ci->load->helper([
'api',
'auth'
]);

if (empty(userdata())) {
return send_response([
'success' => FALSE,
'error' => 'Login required!'
], HTTP_UNAUTHORIZED);
}
}
}
38 changes: 38 additions & 0 deletions application/middlewares/Role_middleware.php
@@ -0,0 +1,38 @@
<?php

class Role_middleware {

private $ci;
private $controller;
private $extras;

/**
* Accepting codeigniter instance & controller
*/
public function __construct($ci, $controller, $extras)
{
$this->ci = $ci;
$this->controller = $controller;
$this->extras = $extras;
}

public function run()
{
$this->ci->load->helper([
'api',
'auth'
]);

$allowed_roles = $this->extras['roles'];

$userdata = userdata();
$role = $userdata->role ?? NULL;

if (!in_array($role, $allowed_roles)) {
return send_response([
'success' => FALSE,
'error' => 'Sorry, you don\'t have access to this resource.'
], HTTP_FORBIDDEN);
}
}
}

0 comments on commit 03c8145

Please sign in to comment.