How to retrieve associations together with authenticated user data? - authentication

I have a Users table and a UsersProfiles table - the two are obviously related and the user table stores basic user_id, username, password while the users_profiles table stores firstname, lastname, job_title etc.
In CakePHP 3, the call to Authentication Component on login returns the basic user table row. I would like to modify the same to also return the corresponding profile row. How can I do this?
I found a way to do it - but am not sure if there is a more elegant or simpler way.
public function login() {
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
// load profile and associate with user object
$profile = $this->Users->UsersProfiles->get($user['id']);
$user['users_profile'] = $profile;
$this->Auth->setUser($user);
return $this->redirect($this->Auth->config('loginRedirect'));
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}

The contain option
Before CakePHP 3.1, use the contain option
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'contain' => ['UsersProfiles']
]
]
]);
A custom finder
As of 3.1 you can use the finder option to define the finder to use for building the query that fetches the user data
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth'
]
]
]);
In your table class
public function findAuth(\Cake\ORM\Query $query, array $options)
{
return $query->contain(['UsersProfiles']);
}
Both solutions
will ensure that the data returned by AuthComponent::identify() will contain the associated UsersProfiles.
See also
Cookbook > ... Components > Authentication > Configuring Authentication Handlers
Cookbook > ... Components > Authentication > Customizing find query

Related

Laravel array of object update failing for unique email

I am using Laravel 6.I have an API that sends an array of staff, some are existing objects that need to be updated and some are new objects that need to be created. The creation part works but update fails due to "DUPLICATE ENTRY for UNIQUE EMAIL".I am using a function in controller
class AdminController extends Controller
{
public function userTransfer()
{
$caremanagementUserData = Admin::on('validated')->get();
$caremanagementUserData->each(function($userData)
{
if(User::where('email', '=', $userData['email'])->exists()) {
User::on('mysql')->where('id', '=',$userData['id'])->update([
'username' => $userData['username'],
'email' => $userData['email'],
'password' => $userData['password']
]);
} else {
User::on('mysql')->insert([
'username' => $userData['username'],
'email' => $userData['email'],
'password' => $userData['password']
]);
}
});
}
}
Help me about how to send email in the update array to solve the unique email error.
I found a way to get around the problem. Because email is unique and if email existence is being checked by
if(User::where('email', '=', $userData['email'])->exists())
{}
email update should be omitted from array of object, and if a new email is send it will be inserted as new data. It works for me !

How can is possible to attach different roles to users in the exact moment of registration?

before starting I premise and humbly apologize: I am a neophyte regarding the use of Laravel framework. I Searched eveywhere even the documentation but without results.
My question is if it is possible to assign a role at registration time using the Laratrust library. We want for example that the first 4/5 users who register are administrators, from the fifth onwards are normal users with restricted permissions.
This is the code regarding the Controller for registration.
RegisterController.php
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \App\Models\User
*/
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
if(Auth::id() < 4 ){
$user->attachRole('administrator');
}
else{
$user->attachRole('user');
}
return $user;
}
This is the method that aims to register the user from the form, for some reason when I go to take the current id of the user, the condition in the if statement is bypassed, in fact in the database in the table roles i always have users that are administrators. I am thinking that maybe the real problem is Auth Class beacuse if i understood correctly this class is used when a user is already logged.
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
$checklatestid = User::latest()->pluck('id')->first();
if($checklatestid < 4 ){
$user->attachRole('administrator');
//Ruolo amministratore impianto scii
}
else{
$user->attachRole('user');
//Ruolo cliente impianto scii
}
return $user;
}
If understand you correctly

How to create a trigger to populate a table in database based on saved data in another table from laravel controller

I am creating an API with Laravel where I am trying to populate a table in a database based on saved data in another table in the database. So after table 1 data is saved, it should trigger for table 2 data to be also saved.
Here is the code for Controller of table 1
public function create(Request $request)
{
//
$this->validate($request, [
'name' => 'required|unique:users',
'code' => 'required'
]);
Auth::user();
$user = new User();
$user->inputed_code = $request->inputed_code;
$user->code = $request->code;
$user->save();
return response([
'success' => true,
'message' => 'Data has been saved!!',
'data' => $user,
], Response::HTTP_OK);
}
Here is the Controller for table 2 which I want to populate based on result from table 1 through a trigger effect.
public function reward(Request $request)
{
//
$reward = new Reward();
$reward->user_id = DB::table('users')->get(id)
$reward->points = $request->referral_reward;
$reward->save();
$user = $request->only('user_id');
$reward = Reward::where('user_id', '=', $user)->update([
'referral_reward' => 5
]);
return response()->json([
'success' => true,
'message' => 'You have been awarded 5 points',
'data' => $reward,
], Response::HTTP_CREATED);
}
Here are the API routes
Route::post('applycode', 'UserController#create');
Route::post('reward', 'RewardController#reward');
Please what am I doing wrong and is there is better way to approach this is give the same results.
Thank you
I understand that based on a saved User model you want to save something in your rewards table.
You could use Eloquent events. Register an event in your User model like this:
class User extends Model {
// ... other stuff
protected $dispatchesEvents = [
'saved' => UserSaved::class,
];
}
Then you need to create your events file by running php artisan make:event UserSaved, which creates a file app/Events/UserSaved.php. In this files __construct method you receive the affected model as parameter.
public function __construct(User $user)
{
// ... do sth
}
Then you need event listeners, see the docs, to consume the event
After defining and mapping your Eloquent events, you may use event listeners to handle the events.
Then inside the listener you can do all the logic you want to apply to your Rewards model.

Standalone rbac setup for module yii2

I`ve got two modules in my yii2-basic application. For common users I have "user" table and for superadmin users I have the second "superadmin" table. There are two users with different sessions in my app - users and superadmins. I need to use different, standalone rbac for superadmin users table. Is it possible to setup two different rbacs in two modules? Now it is setup in web.php file globally. Maybe it is possible somehow to setup authManager in module separately, not in global web.php file?
Thanks in advance!
I figured it out! In your module.php file specify authManager with different table names, you are supposed to create them before. You also can specify rbac submodule of mdm\admin if you want to use it as well. Specify there another usertable - SuperAdmin in the code below.
public function init()
{
parent::init();
$this->modules = [
'rbac' => [
'class' => 'mdm\admin\Module',
'controllerMap' => [
'assignment' => [
'class' => 'mdm\admin\controllers\AssignmentController',
'userClassName' => 'app\models\SuperAdmin',
'idField' => 'id',
'usernameField' => 'username',
],
],
'layout' => 'left-menu',
'mainLayout' => '#app/views/layouts/rbac.php',
],
];
$config = [
'components' => [
'authManager' => [
'class' => 'yii\rbac\DbManager',
'itemTable' => 'superadmin_auth_item',
'assignmentTable' => 'superadmin_auth_assignment',
'itemChildTable' => 'superadmin_auth_item_child',
'ruleTable' => 'superadmin_auth_rule',
],
],
'as access' => [
'class' => 'app\components\SuperAdminAccessControl',//your overridden AccessControl class
'allowActions' => [
'admin/rbac/*',
'admin/default/login',
'admin/default/logout',
]
],
];
\Yii::$app->authManager->itemTable = 'superadmin_auth_item';
\Yii::$app->authManager->assignmentTable = 'superadmin_auth_assignment';
\Yii::$app->authManager->itemChildTable = 'superadmin_auth_item_child';
\Yii::$app->authManager->ruleTable = 'superadmin_auth_rule';
\Yii::configure(\Yii::$app, $config);
}
Overridden AccessControl class:
namespace app\components;
class SuperAdminAccessControl extends \mdm\admin\components\AccessControl
{
function __construct() {
$this->setUser('superadmin');//table name
}
}

ZF2 / doctrine ORM authentication different Entity

in my application (ZF2 / ORM) i have 3 Entities (with Single Table inheritance)
User
Owner extends User
Agent extends User
i want to make one single Authentication (Login) for the 3 Entity using
doctrine.authenticationservice.orm_default
module.config.php
//other doctrine config
'authentication' => array(
'orm_default' => array(
'object_manager' => 'Doctrine\ORM\EntityManager',
'identity_class' => 'Application\Entity\User',
'identity_property' => 'email',
'credential_property' => 'password',
'credential_callable' => function(User $user, $passwordGiven) {
return $user->getPassword() == md5($passwordGiven);
},
),
),
and the process of login
//LoginController.php
// ..data validation
$this->authService = $this->getServiceLocator()->get('doctrine.authenticationservice.orm_default');
$AuthAdapter = $this->authService->getAdapter();
$AuthAdapter->setIdentity($this->request->getPost('email'));
$AuthAdapter->setCredential(md5($this->request->getPost('password')));
$result = $this->authService->authenticate();
if($result->isValid()){
$identity = $result->getIdentity();
//continue
}
how can i do this process without caring about object type,
when i try to login with email of an Agent, i get this error
Catchable fatal error: Argument 1 passed to Application\Module::{closure}() must be an instance of User, instance of Application\Entity\Owner given
The error you mention is due to the type hint on:
function(User $user) {
Which leads me to believe that you have a missing namespace declaration in your config file; in which case you can either add it or use the FQCN.
function(\Application\Entity\User $user) {
Nevertheless, I don't think it's actually the problem. You can only define one 'identity_class' with doctrine authentication (which the adapter will use to load the entity from the entity manager). If you have multiple entity classes there is no way to have each of these tested with one adapter.
However, the configuration is really just creating a new authentication adapter, specifically DoctrineModule\Authentication\Adapter\ObjectRepository. One solution would be to create multiple ObjectRepository adapters, each with the correct configuration for the different entities and then loop through each of them while calling authenticate() on the Zend\Authentication\AuthenticationService.
For example :
public function methodUsedToAutheticate($username, $password)
{
// Assume we have an array of configured adapters in an array
foreach($adapters as $adapter) {
$adapter->setIdentity($username);
$adapter->setCredential($password);
// Authenticate using the new adapter
$result = $authService->authenticate($adapter);
if ($result->isValid()) {
// auth success
break;
}
}
return $result; // auth failed
}
As previously mentioned is the doctrine config will not allow for more than one adapter, so you would need to create them manually and remove your current configuration.
Another example
public function getServiceConfig()
{
return [
'factories' => [
'MyServiceThatDoesTheAuthetication' => function($sm) {
$service = new MyServiceThatDoesTheAuthetication();
// Assume some kind of api to add multiple adapters
$service->addAuthAdapter($sm->get('AuthAdapterUser'));
$service->addAuthAdapter($sm->get('AuthAdapterOwner'));
$service->addAuthAdapter($sm->get('AuthAdapterAgent'));
return $service;
},
'AuthAdapterAgent' => function($sm) {
return new DoctrineModule\Authentication\Adapter\ObjectRepository(array(
'object_manager' => $sm->get('ObjectManager'),
'identity_class' => 'Application\Entity\Agent',
'identity_property' => 'email',
'credential_property' => 'password'
));
},
'AuthAdapterOwner' => function($sm) {
return new DoctrineModule\Authentication\Adapter\ObjectRepository(array(
'object_manager' => $sm->get('ObjectManager'),
'identity_class' => 'Application\Entity\Owner',
'identity_property' => 'email',
'credential_property' => 'password'
));
},
// etc...
],
];
}
Hopefully this gives you some ideas as to what is required.
Lastly, if you would consider other modules, ZfcUser already has a 'chainable adapter' which actually does the above (but uses the event manager) so It might be worth taking a look at even if you don't use it.