Change the options of a field according to the roles of the admin (Sonata admin) - fosuserbundle

I have two different roles for my dashboard admin : ROLE_SONATA_ADMIN and ROLE_SONATA_SUPER_ADMIN .
Both roles have almost the same functionality but there is a difference when adding a new user : If the admin has the role 'ROLE_SONATA_SUPER_ADMIN ' It can add other admin ,if not It only can add simple users .
For the moment I displayed the field 'type 'To choose the type of user to add :
->add('type',ChoiceType::class,array('label' => 'Type' ,
'choices' => array('ADMIN' => 'admin',
'CLIENT' => 'client',
)))
How I can change the field type options according to the admin role .?
Thanks

protected function configureFormFields(FormMapper $formMapper)
{
$user=$this->getConfigurationPool()->getContainer()->get('security.token_storage')->getToken()->getUser();
if($user->hasRole("ROLE_SONATA_SUPER_ADMIN ")){
$cases = ['ADMIN' => 'admin', 'CANDIDAT' => 'candidat',
];
}else{
$cases = [ 'CANDIDAT' => 'candidat',
'CLIENT' => 'client',
];
}
And :
$formMapper->add('type',ChoiceType::class,array('label' => 'Type' ,
'choices' => $cases))

Related

Cakephp authentication plugin how can I implement login in admin prefix?

I'm trying to implement cakephp authentication plugin in admin prefix
Route I have written for admin prefix
$routes->prefix('admin', function (RouteBuilder $routes) {
$routes->connect('/',['controller'=>'AdminUsers','action'=>'login']);
$routes->fallbacks(DashedRoute::class);
});
In application.php , I have followed everything That mentioned in authentication documentation
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$authenticationService = new AuthenticationService([
'unauthenticatedRedirect' => Router::url('/admin'),
'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',
],
'userModel' => 'AdminUsers',
'loginUrl' => Router::url('/admin'),
]);
return $authenticationService;
}
I have changed users model to AdminUsers for database table admin_users
Now in admin/appController.php
I have loadComponent in initialize method
$this->loadComponent('Authentication.Authentication');
In before filter method I have added
$this->Authentication->allowUnauthenticated(['login']);
Now after submit login form I am getting error
Table class for alias Users could not be found.
In getAuthenticationService method I have changed model Users to AdminUsers. Why it's going for Users model rather then AdminUsers model ?
My Table/AdminUsersTable.php table class look likes
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class AdminUsersTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('admin_users');
--------
After ndm comment I am able to change default model in application.php , But how can I change it in Admin/AppController.php ? Code that I have tried.
public function beforeFilter(EventInterface $event)
{
$service = new AuthenticationService();
$service->loadIdentifier('Authentication.Password', [
'resolver' => [
'className' => 'Authentication.Orm',
'userModel' => 'AdminUsers',
],
]);
$this->Authentication->allowUnauthenticated(['login','signup']);
$this->viewBuilder()->setLayout('admin');
}

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

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

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

CakePHP - Add custom values to User Object

I have added 4 more columns to my CakePHP Users table and I am trying to figure out how I can include these columns in the $this->Auth->user() object.
I've added the column information to my Users Model and Entity but still no joy. Currently my user object looks like this;
[
'id' => (int) 1,
'username' => 'admin',
'name' => 'Web Admin',
'email' => 'webteam#',
'role' => 'admin',
'created' => object(Cake\I18n\Time) {
'time' => '2016-02-09T16:04:46+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\Time) {
'time' => '2016-02-12T08:53:16+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
}
]
Where is this object created and is there a way I can add my custom values to it, without editing core CakePHP files?
Thanks for your help .
By default the built-in authenticators will fetch all fields in the tables schema.
You most probably just forgot to clear your cache (tmp/cache/models), which you should do whenever you make changes to your schemas.
In case one would want to specify what fields are being fetched, a custom finder would be needed.
See Cookbook > Controllers > Components > Authentication > Customizing Find Query
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth'
]
],
]);
In your UsersTable class
public function findAuth(\Cake\ORM\Query $query, array $options)
{
return $query
->select(['id', 'username', 'password', 'column_x', 'column_y']);
}
It should be noted that the fields required for authentication must always be included, ie like username and password!

Querying database for existing username with Zend and doctrine

I am querying database records to check if a username already exists this way:
$q = Doctrine_Query::create()
->from('Tcc_Model_User u')
->where('u.Username = ?', $input['Username']);
$object = $q->fetchOne();
if(is_object($object)) {
// not sure what to do here
} else {
// ok to record
What I cannot make it work is how to show a message that username is already taken next to the Username field of the submitted form. Could please anyone help me? Really appreciated any help. Thank you
EDITED:
Thanks to Drew010 I solved the above problem by adding:
$form->getElement('Username')->addError('Sorry, that username is already taken!');
in the first of the conditional statement. Thank again drew010.
Since you are using Zend_Form, there is a validator that can do this for you.
See Zend_Validate_DbNoRecordExists
They give an example for validating that a given username does not exist in the database.
Here is an example from one of my applications, the third validator checks that the username does not already exist:
$this->addElement('text', 'username', array(
'label' => 'User Name:',
'required' => true,
'validators' => array('NotEmpty'),
'decorators' => $this->elementDecorators,
'validators' => array(
array('StringLength', true, array('min' => 4,
'messages' => array(
Zend_Validate_StringLength::TOO_SHORT =>
'Account username must be at least %min% characters'
)
)),
array('Regex', true, array('pattern' => '/^\w(?:[\w\d\.\-_]+)(?:\w|\d)$/',
'messages' => array(
Zend_Validate_Regex::NOT_MATCH =>
"The username contained invalid characters"
)
)),
array('Db_NoRecordExists', true, array(
'table' => 'accounts', 'field' => 'username',
'messages' => array(
Zend_Validate_Db_NoRecordExists::ERROR_RECORD_FOUND =>
"An account with the username '%value%' is already registered"
)
))
)
));
Also, for future reference, if you do extended validation you can't do with a validator, you can add an error to a specific form element like this:
if ($something_was_wrong) {
$form->getElement('username')
->addError('Username already exists!');
}
I would create a separate Doctrine-based validator modeled on the Zend_Validate_Db_NoRecordExists validator and add that validator directly to the element
The validator:
class My_Validate_UsernameAvailable extends Zend_Validate_Abstract
implements Zend_Validate_Interface
{
const NOT_AVAILABLE = 'usernameAvailableNot';
protected $_messageTemplates = array(
self::NOT_AVAILABLE => "Username is not available",
);
public function isValid($value)
{
$q = Doctrine_Query::create()
->from('Tcc_Model_User u')
->where('u.Username = ?', $value);
if ($q->fetchOne()){
$this->_error(self::NOT_AVAILABLE);
return false;
}
return true;
}
}
Then in the form, attached to the username element:
'validators' => array(
// your other valiadators
array(new My_Validate_UsernameAvailable(), true),
),
Then there is no extra handling required in the controller.

Resources