how to create user role wise access control in user and role table joining in cakephp 3? - cakephp

user table
role table
I just want to allow access control to role table set like: ctrl_view = 1 means this role can view any controller view.
How can I set different action in different role?

Follow conventions, user_role_id should be named "role_id", role_id only "id" and user_name should be "username" or inside your Auth configuration change the default fields name use for your connection form.
public function initialize()
{
//...
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Pages',
'action' => 'welcome',
'prefix' => 'admin'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login',
'prefix' => false
],
'authError' => 'Unauthorized access...',
'authenticate' => [
'Form' => [
'fields' => ['username' => 'user_name', 'password' => 'password']
]
],
'authorize' => 'Controller',
'unauthorizedRedirect' => [
'controller' => 'Pages',
'action' => 'unauthorized'
],
]);
// ...
}
and inside your Appcontroller make somtehing like this
public function isAuthorized($user)
{
if(!is_null($this->Auth->user())): // if user is logged
$action = $this->request->getParam('action'); // get name action
$this->loadModel('Roles'); // load your model Roles
$query = $this->Authorizations->find() // find inside Roles
->where([
'Roles.role_id IN' => $user['user_role_id'], // where role_id is like user_role_id of current user
'Roles.ctl_'.$action => 1 // and where ctl_[action] is set to 1
])->toArray();
if (!empty($query)): // if we find an occurence, we allow the action
return true;
else: // else we don't authorize
return false,
endif;
/* previous lines can be change with this ----> return (!empty($query)); */
else: // if user is not connected we don't allow action
return false
endif;
}
and to finish, i think it's better to use "prefix", with prefix u can simplify your authorisation process (will no prefix i allow, with prefix i check my role table), for this you have to simply add these line in the beginin of your isAuthorized function:
if (!$this->request->getParam('prefix')) {
return true;
}
Hope it helps

Related

cakedc/users autologin after registration

I'm using the cakephp 4.2.6 with the cakedc/users plugin. I'd like to automatically login the user after registration without the need to first activate the account by clicking on the link in e-mail.
My idea is to creating an Event-Listener and listening to the Users.Global.afterRegister Event, but then I don't know how to login the user with the new Authentication.
this is what I ended up with and it seems to work so far:
<?php
namespace App\Event;
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ServerRequestInterface;
use Cake\Event\EventListenerInterface;
use Cake\Datasource\FactoryLocator;
use Cake\Log\Log;
class UserEventListener implements EventListenerInterface
{
public function implementedEvents(): array
{
return [
'Users.Global.afterRegister' => 'autoLogin',
];
}
public function autoLogin($event)
{
$user = $usersTable->get($event->getData('user')->id);
$request = $event->getSubject()->getRequest();
$response = $event->getSubject()->getResponse();
$authenticationService = $this->getAuthenticationService($request);
$authenticationService->persistIdentity($request, $response, $user);
}
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$authenticationService = new AuthenticationService([
'unauthenticatedRedirect' => \Cake\Routing\Router::url([
'controller' => 'Users',
'action' => 'login',
'plugin' => null,
'prefix' => null
]),
'queryParam' => 'redirect',
]);
// Load identifiers, ensure we check email and password fields
$authenticationService->loadIdentifier('Authentication.Password', [
'fields' => [
'username' => 'email',
'password' => 'password',
]
]);
// Load the authenticators, you want session first
$authenticationService->loadAuthenticator('Authentication.Session');
// Configure form data check to pick email and password
$authenticationService->loadAuthenticator('Authentication.Form', [
'fields' => [
'username' => 'email',
'password' => 'password',
],
'loginUrl' => \Cake\Routing\Router::url([
'controller' => 'Users',
'action' => 'login',
'plugin' => null,
'prefix' => null
]),
]);
return $authenticationService;
}
}
There is a similar use case covered in the plugin documentation https://github.com/CakeDC/users/blob/master/Docs/Documentation/Events.md
Your approach looks correct to me, using the afterRegister event to create the listener in a controller where you have access to the Authenticatication component,
// note this example is handling auto-login after the user validated the email sent (click the validation token link sent to his email address)
EventManager::instance()->on(
\CakeDC\Users\Plugin::EVENT_AFTER_EMAIL_TOKEN_VALIDATION,
function($event){
$users = $this->getTableLocator()->get('Users');
$user = $users->get($event->getData('user')->id);
$this->Authentication->setIdentity($user);
}
);
or using the request to retrieve the authentication service and set the identity like it's done here https://github.com/cakephp/authentication/blob/master/src/Controller/Component/AuthenticationComponent.php#L273

common method to check status before all controllers in CakePHP

I have an application in cakephp where a logged in user must have a status of active otherwise it should redirect to a controller where user must have submit his application.
Actually i want to implement on all the controllers whenever a user tried to access any controller action it should check the status automatically and if status is not active it should redirect to the user application. How can i implement it in the Appcontroller.
My Appcontroller contents are :
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler', [
'enableBeforeRedirect' => false,
]);
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authError' => 'You have been logged out due to period of inactivity.',
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'loginRedirect' => [
'controller' => 'Users',
'action' => 'dashboard'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'logout'
],
]);
You can use beforeFilter Method in your AppController to perform such a check.
Suppose the name of your Controller where you want to Redirect is YourController, then your method should look like.
public function beforeFilter( $event ) {
if ($this->Auth->user()) { //If User is logged in.
if ( $this->request->controller != 'YourController' ) { //If Request Controller is other than YourController
$status = $this->Auth->user('status'); //Get the Status
if( $status != 'active' ) { //If Status is not active
//Redirect Here
return $this->redirect(
['controller' => 'YourController', 'action' => 'index'];
);
}
}
}
}

Redirect to authorize page on session time out

Brief Introduction about the web app:
I'm developing a web app on CakePHP 3.2 where user authentication is a two step process.
Every user has username, password, PIN, along with other fields.
Step 1: users/login - Enter username and password.
Step 2: users/pin_authorize - If step 1 is successful, then enter pin.
What I need:
Every time the user is inactive for about 30 mins, I want the user to be redirected to the pin_authorize page and not the main login page. The user would be able to access the other pages only if he inserts the correct PIN.
What I've tried so far:
// AppController
public function initialize() {
parent::initialize();
$this->loadComponent('Auth',[
'loginAction' => [
'plugin' => 'Admin',
'controller' => 'Users',
'action' => 'login'
],
'loginRedirect' => [
'plugin' => 'Admin',
'controller' => 'Users',
'action' => 'pinAuthorization'
],
'logoutRedirect' => [
'plugin' => 'Admin',
...
]
]);
}
// UsersController
public function pinAuthorization() {
if (!$this->Auth->user('id')) {
return $this->redirect([
'plugin' => 'Admin',
'controller' => 'Users',
'action' => 'login'
]);
}
if ($this->request->is('post')) {
if ($this->Users->pinAuthorize($this->request->data['pin'])) {
$this->request->session()->write("PinAuthStatus", 1);
return $this->redirect([
'plugin' => 'Admin',
'controller' => 'Users',
'action' => 'dashboard'
]);
}
$this->Flash->error(__('Invalid PIN.'));
}
$this->viewBuilder()->layout(false);
}
Any help would be highly appreciated. Thanks!
I would recommend creating a cookie after the user has successfully logged in that expires in 30 minutes and is updated every time isAuthorized is validated. You can then do a redirect to the pin authorization page within the isAuthorized if the cookie has expired and the session still exists.

CakePHP 3 - Login Error - Call to a member function identify on boolean

Hello I'm using CakePHP 3 to simple setup a site which some pages of it need user to login first.
It was fine when I put the loadComponent('Auth', blablabla) code in initialize() of AppController.php.
src\Controller\AdminController.php
...
public function login() {
if ($this->request->is('post')) {
$admin = $this->Auth->identify();
if ($admin) {
$this->Auth->setUser($admin);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error('Your username or password is incorrect.');
}
}
...
src\Controller\AppController.php
...
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'Authenticate' => [
'Form' => [
'userModel' => 'Admin',
'Fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Admin',
'action' => 'login',
]
]);
$this->Auth->allow(['display']);
}
...
At this point, I needed to login in order to view all other pages of the site.
But I tried to put this same authentication setup in another controller called JustController, and after I logged in, a fetal error stated
Call to a member function identify() on boolean
has been shown.
It should be possible to setup authentication in other controllers so that the site can have more than 1 set of login system instead of covering whole site by setting up in AppController, doesn't it?
Thank you.

Include a subset of fields in the Auth User session

My Users table has a whole bunch of fields, most of which I don't need/want stored in the Auth User session. How do you restrict which fields are stored in the session for the logged in user?
I know you can choose fields of associated models with the 'contain' key, but normally to select fields of the top-level model, you'd use the 'fields' key. But in the case of Auth, the 'fields' key is used to choose which fields to authenticate the user by, not which fields to include in the session.
To give some context, here's my code so far... what would I do to make it so that only the email and firstname fields are stored in the Auth session, as opposed to all fields in the Users table.
$this->Auth->authenticate = array(
'Blowfish' => array(
'fields' => array(
'username' => 'email',
'password' => 'password',
)
)
);
I've upvoted the answers which were useful, albeit work-around solutions - thanks.
I think the "correct" answer is that there's no way to do this with CakePHP Auth component out of the box, and you have to hack it (eg, using one of the solutions below). I took a look at the _findUser method in BaseAuthenticate.php and it confirms this.
In case a CakePHP core dev is reading (DeEuroMarK?), this is probably a pretty common requirement, and I think it's a feature worth having built in.
Suggested implementation: just include the fields you want as extra fields in the 'fields' array - and just assume that every key other than 'username' and 'password' is an extra field that should be included in the auth session. That way it's consistent with other Model find syntax.
Example:
$this->Auth->authenticate = array(
'Blowfish' => array(
'fields' => array(
'username' => 'email',
'password' => 'password',
'another_field',
'yet_another_field'
)
)
);
in the beforeFilter of my UsersController I have something similar as your login.
Then I set a afterLogin function as the redirect
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'afterLogin');
$this->Auth->loginRedirectTrue = array('controller' => 'users', 'action' => 'index');
$this->Auth->logoutRedirect = array('controller' => 'pages', 'action' => 'display');
the login function dus some checks and afterwards redirects to
if ($this->Auth->login()){
// code here
$this->redirect($this->Auth->redirect());
}
and afterLogin function like this
function afterLogin(){
$session = $this->Session->read('Auth');
$user_id = $session['User']['id'];
// change this to find only the fields you need and then override the Auth.User...
$user = $this->User->findById($user_id);
if (!empty($user)){
$this->Session->write('Auth.UserProfile', $user['UserProfile']);
}
$this->redirect($this->Auth->loginRedirectTrue);
}
You should change the findById to suit your needs and then override the Auth.User fields in the session.
Good Luck!
I think the simplest is to add something like this:
add a contain to your Auth-Component configuration
$this->loadComponent('Auth', [
'authorize' => 'Controller',
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
],
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email'],
'contain' => ['Groups']
]
],
'unauthorizedRedirect' => $this->referer()
]);
...
And in you login-action save the user in session:
$foundUser = $this->Auth->identify();
if ($foundUser) {
$this->Auth->setUser($foundUser);
}
...
this will add the containing groups to the Auth.User
The is for CakePhp3 - in older versions it may be different.

Resources