I want to check if the user has permission to access to the URL (controller/method) combination. It should check before any method called in called controller and method belongs to it.
As far as I understand, the hook should be pre_controller for the logic above but when I use it, I think it clashes with post_controller_constructor shown below. If I use post_controller instead then it works but this time logic is compromised.
How can I solve this problem?
Thanks
CONFIG/HOOKS
//Used to authenticate user session to decide whether to authenticate site or not
$hook['post_controller_constructor'] =
array(
'class' => 'site_authentication',
'function' => 'authenticate',
'filename' => 'site_authentication.php',
'filepath' => 'hooks',
'params' => null
);
//Used to authenticate permitted controllers
$hook['pre_controller'] =
array(
'class' => 'permitted_controllers',
'function' => 'authenticate',
'filename' => 'permitted_controllers.php',
'filepath' => 'hooks',
'params' => null
);
APPLICATION/HOOKS
//This works fine
class site_authentication
{
private $CI;
public function __construct()
{
$this->CI =& get_instance();
}
public function authenticate()
{
if (! $this->CI->session->userdata('site'))
{
redirect('to error page');
}
$user_session = $this->CI->session->userdata('site');
//Some more stuff here
}
}
//This doesn't work with pre_controller
class permitted_controllers
{
private $CI;
public function __construct()
{
$this->CI =& get_instance();
}
public function authenticate()
{
$user_session = $this->CI->session->userdata('site');
//Url is set here, ignore syntax error below
$url = $this->CI->uri->segment(1) . 2 . 3;
if (! in_array($url, $user_session['controllers']))
{
redirect('to error page');
}
}
}
If I combine them two, they work fine under post_controller_constructor but they won't work separately?
$hook['post_controller_constructor'] [] =
array(
'class' => 'site_authentication',
'function' => 'authenticate',
'filename' => 'site_authentication.php',
'filepath' => 'hooks',
'params' => null
);
$hook['post_controller_constructor'] [] =
array(
'class' => 'permitted_controllers',
'function' => 'authenticate',
'filename' => 'permitted_controllers.php',
'filepath' => 'hooks',
'params' => null
);
pre_controller hook is run before the super object has been constructed, so it is not a viable option for hooking into CI's normal syntax (such as $this->db->query()).
I'd suggest creating a base controller (aka MY_Controller or some other name) and adding the permission check to its constructor. Then, each controller that should run the permissions check will then extend MY_Controller instead of CI_Controller. Here's Phil Sturgeon's classic article about base controllers.
Hooks are called on every page load. If you don't need to check permissions somewhere, you need to either add that logic to your hook, or add logic somewhere else to try and disable it. Not very extendable. Using a base controller, adding the permission check is as simple as extending a different class.
The pre_controller hook executes before the super object has been fully constructed read more here
Related
I'm using CodeIgniter hooks, I'm trying to trigger an action before a controller to be executed(pre_controller) and another action after the controller to be executed. But I'm facing a weird situation, when I enable the hook in $config['enable_hooks'] = TRUE; it trigger automatic logout, I mean it kills the user session. Could some one helps me on this? May be I made a wrong configuration for the hook.
Here is the hooks configuration:
hooks.php
$hook['pre_controller'] = array(
'class' => 'Hookcall',
'function' => 'get_post_params',
'filename' => 'hookcall.php',
'filepath' => 'hooks'
);
/*
hook for saving fraude logs
*/
$hook['post_controller'] = array(
'class' => 'Hookcall',
'function' => 'save_logs',
'filename' => 'hookcall.php',
'filepath' => 'hooks'
);
Hookcall should NOT extend MY_Controller. It should be a simple class instead
class Hookcall
{
public function save_logs()
{
}
public function get_post_params()
{
}
private function get_files_superior_5_percent($params)
{
}
}
I have the following hook config (in hooks.php)
$hook['post_controller_constructor'][] = array(
'class' => 'MY_DataCollection',
'function' => 'init',
'filename' => 'MY_DataCollection.php',
'filepath' => 'hooks'
);
$hook['post_controller'][] = array(
'class' => 'MY_DataCollection',
'function' => 'post_controller',
'filename' => 'MY_DataCollection.php',
'filepath' => 'hooks'
);
What I want to do is have the class instantiated in the post_controller_constructor and then have the init method run. Then for post_controller run the post_controller but using the very same instance. CodeIgniter is instantiating the class again however.
Next I tried something slightly clever I thought:
require_once APPPATH . 'hooks/MY_DataCollection.php';
$collection = new MY_DataCollection;
$hook['post_controller_constructor'][] = array(
'function' => array($collection, 'init'),
'filename' => 'MY_DataCollection.php',
'filepath' => 'hooks'
);
$hook['post_controller'][] = array(
'function' => array($collection, 'post_controller'),
'filename' => 'MY_DataCollection.php',
'filepath' => 'hooks'
);
That doesn't work, as the hook class in CI does a require and I get:
Fatal error: Cannot redeclare class MY_DataCollection in /var/www/application/hooks/MY_DataCollection.php on line 7
So I got rid of the file path info:
require_once APPPATH . 'hooks/MY_DataCollection.php';
$collection = new MY_DataCollection;
$hook['post_controller_constructor'][] = array(
'function' => array($collection, 'init')
);
$hook['post_controller'][] = array(
'function' => array($collection, 'post_controller')
);
Doesn't even attempt to run, as in the CI_Hooks class there is this check in _run_hook:
if ( ! isset($data['filepath']) OR ! isset($data['filename']))
{
return FALSE;
}
I think I'm going to have to override this function by extending this class, but I just can't believe that this situation hasn't been encountered by someone else before and that there's not any easier way of solving it.
Thanks
Hmmz, it seems you need to store the instance of the needed class somewhere.
Maybe you could do something like this:
class MyHooks
{
public static $object_you_need;
public function init()
{
$object = new MyObject();
$object->init();
self::$object_you_need = $object;
}
public function post_controller()
{
$object = self::$object_you_need;
// do stuff with that object
}
}
Doesn't feel pretty, but I think it works.
This issue is already solved on the CI3 here is how to fix it on CI2
Just create a MY_Hooks.php on your app/core directory
Copy the Hooks.php class on the CI 3 Repository and put it in your
MY_Hooks.php but the class name will be MY_Hooks Extends
CI_Hooks it is located on system/core/hooks.php
change the call_hook function to _call_hook (just add a underscore '_')
change the private _run_hook to public
next step, is lay back and drink beer
Here is to test if it is working
class Hook_hook{
private $init = 'function inits not called';
public function inits(){
$this->init = 'function inits called';
}
public function load(){
echo '<script>alert("'.$this->init.'")</script>';
}
}
Sample config on config/hooks.php
$hook['post_controller_constructor'][] = array(
'class' => 'Hook_hook',
'function' => 'inits',
'filename' => 'Hook_hook.php',
'filepath' => 'hooks'
);
$hook['post_controller'][] = array(
'class' => 'Hook_hook',
'function' => 'load',
'filename' => 'Hook_hook.php',
'filepath' => 'hooks'
);
CakePHP v.2.4...
I'm following this documentation trying to set up the Auth component to use my custom password hashing class:
App::uses('PHPassPasswordHasher', 'Controller/Component/Auth');
class AppController extends Controller {
// auth needed stuff
public $components = array(
'Session',
'Cookie',
'Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array('username'=>'email', 'password'=>'password'),
'passwordHasher' => 'PHPass'
)
),
Inside my UsersController::login() I debug the return from $this->Auth->login(); and it always returns false, even when I submit the correct email / password.
(NOTE: It looks strange to me that the login() takes no parameters, but the docs seem to imply that it looks into the the request data automatically. And this would make sense if my configurations aren't correctly causing it to check the User.email field instead username.)
The post data from the submitted login form looks like this:
array(
'User' => array(
'password' => '*****',
'email' => 'whatever#example.com'
)
)
What am I missing?
Update2
I'm starting to suspect that the default hashing algorithm is getting used instead of my custom class. I tried to match the examples in the docs but they're quite vague on how to do this.
Here's the contents of app/Controller/Component/Auth/PHPassPasswordHasher.php
<?php
App::import('Vendor', 'PHPass/class-phpass'); //<--this exists and defines PasswordHash class
class PHPassPasswordHasher extends AbstractPasswordHasher {
public function hash($password) {
$hasher = new new PasswordHash( 8, true );
return $hasher->HashPassword($password);
}
public function check($password, $hashedPassword) {
debug('PHPassHasher'); die('Using custom hasher'); //<--THIS NEVER HAPPENS!
$hasher = new new PasswordHash( 8, true );
return $hasher->CheckPassword($password, $hashedPassword);
}
}
AHA! The debug() never appears... so I'm pretty sure the problem is with my custom hasher configuration(s).
Update3
Additional clue: I experimented by setting various default hashing algorithms (Ex: "Simple", "Blowfish") and creating users. The hashes which show up in the DB are all the same which tells me that my config settings are getting ignored completely.
Update4
I debugged $this->settings inside the constructor of /lib/Cake/Controller/Component/Auth/BaseAuthenticate.php and my custom hasher settings are in there:
array(
'fields' => array(
'password' => 'password',
'username' => 'email'
),
'userModel' => 'User',
'scope' => array(),
'recursive' => (int) 0,
'contain' => null,
'passwordHasher' => 'PHPass'
)
You need to rename your password hasher class to have the suffix "PasswordHasher", and only provide the non-suffixed name in the 'className' argument.
eg:
<?php
App::import('Vendor', 'PHPass/class-phpass'); //<--this exists and defines PasswordHash class
class PHPassHasherPasswordHasher extends AbstractPasswordHasher {
// functions
}
The example from the docs sets the classname to 'Simple', which then loads 'SimplePasswordHasher'.
You might find that having a name of PHPassHasherPasswordHasher is a bit silly, it's up to you what you want to call it. Perhaps PHPassPasswordHasher might be a bit more appropriate (and then use the classname argument 'PHPass').
EDIT: It seems as if Cake has issues when multiple capital letters are used one after the other (eg. PHPass), so the right way to do it is to change the password hasher class to the following:
<?php
App::import('Vendor', 'PHPass/class-phpass'); //<--this exists and defines PasswordHash class
class PhpassPasswordHasher extends AbstractPasswordHasher {
// functions
}
... and make sure the filename matches the classname: PhpassPasswordHasher.php.
Thanks to SDP for the discussion, I learnt something today!
According to the docs:
To configure different fields for user in $components array:
// Pass settings in $components array
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array(
'username' => 'email',
'password' => 'password'
)
)
)
)
);
Source
I finally got this working. We were on the right track by renaming the file/class to comply with Cake conventions. I had to go one step further and change the capitalization as well:
PHPassPasswordHasher.php --> PhpassPasswordHasher.php
class PHPassPasswordHasher... --> class PhpassPasswordHasher...
Phew!
ps: Many many thanks to #Ben Hitchcock for support on this.
How can I configure Zend Framework 2 route to only to allow POST request type?
I've gone through the documentation but no avail.
EDIT
Here is the portion of my DI code
'cv-create' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/profile/cv/:type/create',
'defaults' => array(
'controller' => 'Application\Controller\ProfileController',
'action' => 'cv_create',
),
),
),
My actual question is, is there any way to inject the request control as a constraint to this route?
You could extend the Zend\Mvc\Router\Http\Segment class and create a slightly modified version of match(). Since match() has the $request parameter it should be as simple as follows:
public function match(Request $request, $pathOffset = null)
{
if (!$request->isPost())
{
return null;
}
return parent::match($request, $pathOffset);
}
Then use this modified class instead of the standard Segment in the routing configuration.
An alternative approach could be to use the AbstractRestfulController in this case.
http://framework.zend.com/apidoc/2.1/classes/Zend.Mvc.Controller.AbstractRestfulController.html
Only implement the create() method.
in class Zend_Controller_Request_Http
You can disable the other request type .
In module.config.php file, I have set value for 'password_has_type'. And in controller I want to access that. Here is my module.config.php file:
'auth' => array(
'password_hash_type' => 'sha512',
),
'di' => array(
'instance' => array(
'alias' => array(
'auth' => 'Auth\Controller\AuthController',
'auth_login_form' => 'Auth\Form\LoginForm',
),...
In controller, I have used
use Auth\Module
and in Action method I try to get access value by
echo Module::getOption('password_hash_type');
But I could not get any value?
So please can anybody help me to get that value ?
Please see my answer at Access to module config in Zend Framework 2.
But to make it more concrete to your question, you would do this:
$config = $this->getServiceLocator()->get('Config');
$pwht = $config['auth']['password_hash_type'];
I hope this helps!
You can do it with help of aliases and parameters. Put it into di->instance array:
'Auth\Controller\AuthController' => array(
'parameters' => array(
'passwordHashType' => 'sha512'
)
),
And it is your controller:
namespace Auth\Controller;
use Zend\Mvc\Controller\ActionController;
class AuthController extends ActionController
{
protected $passwordHashType;
public function indexAction()
{
echo $this->passwordHashType;
}
public function setPasswordHashType($passwordHashType)
{
$this->passwordHashType = $passwordHashType;
return $this;
}
}