Same User, multiple accounts with different roles - php

I'm creating an application in Symfony 3, that has the following structure:
class Account {
private $id;
private $name;
}
class User {
private $id;
private $email;
private $password;
}
class UserAccount {
private $id;
private $userId;
private $roles;
}
As we can see an user can belong to several accounts with different roles for each account, let's say that for Account 1 it has the role ROLE_ADMIN, and for the Account 2 it has the role ROLE_EDITOR.
The problem is that the user will have a select box where he can change the account, this means that the role needs to be loaded from the database based on a value on session ( since the account ID ) will be set on session.
This also means that when an user logins into the site, there will be no role, since the role is determined by the account selected.
I have tough about using events, but that doesn't seem to work from what I've read.
Does anyone has any thoughts/insights into this?
I have my own custom Authenticator, since I need to support both MD5 and bcrypt passwords.
This means that I have a class that extends SimpleFormAuthenticatorInterface of Symfony, this allows me to have the users login with MD5 and automatically upgrade them to bcrypt.
My User model ( which is an normal one ), and Custom Authenticator: Gist
To sumarize: I need a way in which I can change the roles of the user after he has logged in, without forcing the re login of the user.

So after two days of struggle, here is the solution.
Looking at the question, when the user logins it needs to take a role determined by what is in the UserAccount table, since an user can have several accounts associated to him, the only way to solve this was to first create a post login listener:
/**
* On login
*
* #param InteractiveLoginEvent $event The login event
*
* #return void
*/
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) {
$user = $this->tokenStorage->getToken()->getUser();
// get the user accounts of the current from the database
$accountsOfUser = $this->em->getRepository('AppBundle\Entity\AccountHasUser')->getAccountsOfUser($user->getId());
foreach ( $accountsOfUser as $accountOfUser ) {
$this->session->set('accountid', $accountOfUser['account_id']);
$user->resetAndAddRole('ROLE_' . $accountOfUser['const']);
break;
}
// We just need to set the new security token
$token = new UsernamePasswordToken(
$user,
null,
'main',
$user->getRoles()
);
// Update the current token to set the new role
$this->tokenStorage->setToken($token);
}
I known that I can only get one record from the database, but this was just for show ( don't blindly copy/paste this to your code ).
So basically I get the first account of the user, get it's role, put the account id on the session ( still got to read a bit more about bags on Symfony sessions ), and procede to generate a new UsernamePasswordToken and adding it to the tokenStorage.
The $user->resetAndAddRole, is a function in my User Model, that has only the following:
/**
* Resets current user roles and add's the new one
*
* #param string $role The role to add
*
* #return AppBundle\Entity\User
*/
public function resetAndAddRole($role) {
$this->roles = array($role);
return $this;
}
Now I also need to allow the user to change between accounts when is logged in, so in a controller:
public function changeAccountAction(Request $request, $id) {
$user = $this->get('security.token_storage')->getToken()->getUser();
$em = $this->getDoctrine()->getManager();
$newAccountRole = $em->getRepository('AppBundle\Entity\AccountHasUser')->getAccountModelByAccountIdANdUserId($id, $user->getId());
$user->resetAndAddRole('ROLE_' . $newAccountRole['const']);
$token = new UsernamePasswordToken(
$user,
null,
'main',
$user->getRoles()
);
// Update the current token to set the new role
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('accountid', $id);
// $em = $this->getDoctrine()->getManager();
// $accountsList = $em->getRepository('AppBundle\Entity\AccountHasUser')->getAccountsOfUser($user->getId());
// We need to change the users roles right here and how to we do it?
// We simply change the
return new RedirectResponse($this->generateUrl('dashboard'));
}
Basically, get the account that is passed by parameter, change the session value, so we can use this in our queries and every other code that requires the account id, and then create a new UsernamePasswordToken and voilá everything starts to work perfectly.
Later on I'm going to move the code on the controller to a service, which will also be passed to the post login listener, so that way I only have one play to make the changes to.
I really don't know if this is the right way to do this, but for now it seems to work.

Related

Return model with environment variables if no rows in Database

I have a table merchants in my project, and manager can move site to another merchant in admin panel. In one time, there can be only one active merchant (On activate merchant - I have code, which update all other merchants to is_active=0. But there can be situation, when someone delete all merchants, so I want to get it from .env file. Is my code elegant for this action? If not - how can I improve this?
/**
* #return self
*/
public static function getActiveMerchant()
{
$merchant = (new self())->where('is_active', 1)->first();
if (!$merchant) {
$merchant = new self();
$merchant->login_id = config('merchant.LOGIN_ID');
$merchant->public_client_key = config('merchant.PUBLIC_CLIENT_KEY');
LOG::warning('There is no one active merchant in the DB');
}
return $merchant;
}

How to search a pivot table for rows that are owned by two users

Sorry if this is a stupid question, but I'm new to Laravel.
I have two models and a pivot table:
User
id | name | password
public function conversations(): ?BelongsToMany
{
return $this->belongsToMany(Conversation::class)->withTimestamps();
}
Conversation
id
public function users(): ?BelongsToMany
{
return $this->belongsToMany(User::class)->withTimestamps();
}
conversation_user
id | conversation_id | user_id
I create a conversation and assign the users with sync like so:
$user->conversations()->syncWithoutDetaching($conversation);
$targetUser->conversations()->syncWithoutDetaching($conversation);
Users can have many conversations, and conversations can have multiple users. This is fine, but when I want to get a conversation with two specific users I don't know the best way to utilize the ORM to find the conversation they're both apart of.
I am currently using this next method, which works but it feels like there is a much better way of doing things utilizing the ORM:
/**
* Get a conversation by a target user id.
*
* #param int $targetUserId
* #return mixed
*/
public function getConversationByTargetUserId(int $targetUserId)
{
// Get the current user.
$user = Auth::guard()->user();
// Check the user exists.
if (!$user) {
throw new HttpException(500);
}
/**
* Get all pivot tables where the
* user ID is from the current user.
*/
$userConversationIdsArray = DB::table('conversation_user')->where('user_id', $user->id)->pluck('conversation_id');
/**
* Get all pivot tables where the user
* id is equal to the target id, and is
* also owned by the current user. Return
* the first instance that we come across.
*/
$targetConversation = DB::table('conversation_user')->where(['conversation_id' => $userConversationIdsArray, 'user_id' => $targetUserId])->first();
/**
* Return the conversation.
*/
return Conversation::find($targetConversation->conversation_id);
}
Thank you for your time :)
Is there a particular reason you are not utilising Eloquent? It might make it easier.
It could be done like this as you already have the user.
$user->conversations()->has('users.id', '=', $targetUserId)->first();
(I have not tested this solution so i am not sure this works 100%)
Also, there might be a typo in your first query. Might be a copy paste error might be a typo. Just making sure.
$userConversationIdsArray = DB::table('conversation_user')->where('user_id', $user->id)->pluck('id'); <---- 'id' shouldn't that be 'conversation_id'?
Thanks to #Fjarlaegur they put me on the right track. The following method works:
/**
* Get a conversation by a target user id.
*
* #param int $targetUserId
* #return mixed
*/
public function getConversationByTargetUserId(int $targetUserId)
{
// Get the current user.
$user = Auth::guard()->user();
// Check the user exists.
if (!$user) {
throw new HttpException(500);
}
return $user->conversations()->whereHas('users', function ($query) use ($targetUserId) {
$query->where('users.id', $targetUserId);
})->first();
}

How can i use idUser in a password_resets (laravel 5.2)

I want to use the table password_resets that I obtained with the comand php artisan make:auth, but this table use the "email" and this is not right because if my users change their email in their profile then is a problem to identify them, i want to use the "idUser" in the table "password_resets" instead of "email".
Where can i change this in the code????
When your user try to reset the email he must have to provide the email for resetting and if s/he change the email from profile the new email will be automatically updated in the user table or whatever you have,right? so the user will provide the updated email if s/he really intends to reset the password.so the latest email will be used for password resetting and the updating of the email should not be a problem at all. :) :)
thanks for your answer,
finally I solved doing this:
Dirección: ...vendor/laravel/....DatabaseTokenRepository.php
/**
* Create a new token record.
*
* #param \Illuminate\Contracts\Auth\CanResetPassword $user
* #return string
*/
public function create(CanResetPasswordContract $user)
{
$email = $user->getEmailForPasswordReset();
$idUser = $user->idUser; //Aquí he añadido el idUser
$this->deleteExisting($user);
// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken();
$this->getTable()->insert($this->getPayload($idUser, $email, $token)); //Aquí he añadido el idUser
return $token;
}
/**
* Delete all existing reset tokens from the database.
*
* #param \Illuminate\Contracts\Auth\CanResetPassword $user
* #return int
*/
protected function deleteExisting(CanResetPasswordContract $user)
{
return $this->getTable()->where('idUser', $user->idUser)->delete();
}

Get current user information in Apigility Resource

I just started with Apigility and oAuth2, and I was wondering if it is possible to get the currently authenticated "loggedin" user when fetching information from a database.
I currently have the following code:
/**
* Fetch all or a subset of resources
*
* #param array $params
* #return mixed
*/
public function fetchAll($params = array())
{
var_dump($params);
// Using Zend\Db's SQL abstraction
$sql = new \Zend\Db\Sql\Sql($this->db);
//I would like to get the currently logged in user here... but how?
$select = $sql->select('projects')->where(array('userid' => 1));;
// This provides paginated results for the given Select instance
$paged = new \Zend\Paginator\Adapter\DbSelect($select, $this->db);
// which we then pass to our collection
return new ProjectsCollection($paged);
}
I did a lot of searching already but I have no clue how to access the user information or the access token, do I need to parse the request header for this?
I was also looking for it. I didn't found any documentation about that. But the answer is quite simple:
Resource classes inherits ZF\Rest\AbstractResourceListener which already has a method getIdentity.
/**
* Fetch all or a subset of resources
*
* #param array $params
* #return mixed
*/
public function fetchAll($params = array())
{
// if user isn't authenticated return nothing
if(!$this->getIdentity() instanceof ZF\MvcAuth\Identity\AuthenticatedIdentity) {
return [];
}
// this array returyour query here using $userIdns the authentication info
// in this case we need the 'user_id'
$identityArray= $this->getIdentity()->getAuthenticationIdentity();
// note, by default user_id is the email (username column in oauth_users table)
$userId = $identityArray['user_id'];
// fetch all using $userId
}
You can also use getIdentity in RPC services.
I'm using the latest version of apigility.
I found in the end a shorter way to get the userid, just adding it as answer for the sake of completeness.
You can get the identity object like #ViníciusFagundes mentioned $this->getIdentity() and this identity object has the function getRoleId() which returns the identifier of the user.
$user_id = $this->getIdentity()->getRoleId();

Restler+OAuth2 - Identifying the user correctly

I'm working with Restler and the OAuth2 module written by Brent Shaffer. What I want to do is determine the user from the token they send, inside my app classes, not just the OAuth2Server classes.
There are two methods that I can see of doing this. Hopefully this explains what I am trying to do.
Method 1: I don't particularly like this method, but it works.
POST /v1/token
Returns my token including the user_id, for example
{
"access_token":"282090609b3407d981c2bea633a39739595ba426",
"expires_in":3600,
"token_type":"Bearer",
"scope":"basic",
"refresh_token":"b60a4e5f759168df857342380f3550bc120b6f9d",
"user_id": 5
}
Now that the client knows the user_id, it is sent with my request:
GET /v1/dashboard?id=5
My __isAllowed method takes care of checking that the user hasn't altered the id, requesting info that isn't theirs.
public function __isAllowed() {
$token = static::$server->getAccessTokenData(Request::createFromGlobals());
return (($token['user_id'] > 0) && ($token['user_id'] === $_GET['id']) && ($token['group_id'] == self::$group_id));
}
Dashboard class looks like this:
/*
* #version 1
* #access protected
*/
class Dashboard {
/**
* #param int $id Customer ID {#from query}
* #return type
*/
public function index($id) {
$s = Dao\ViewCustomerDaoObject::findId($id);
return array_merge($s->toJSON(), $widgets);
}
}
This is how I would prefer to be calling the API:
GET /v1/dashboard
When I request the above, join the oauth2_token table to my dashboard table. I think this might be a bit of a hack and I don't want this to cause problems down the road.
The info is already available in the OAuth2Server instance, as the OAuth2Server class does determine if the correct token is used and what their user_id is.
Can someone please guide me in the right direction for handling this situation, particularly with Restler?
I actually figured this out myself.
In the OAuth2Server->__isAllowed method, you must set the UserId in the static User class.
public function __isAllowed() {
$token = static::$server->getAccessTokenData(Request::createFromGlobals());
// If the user_id is valid, set static user class.
// *** This is not production code, add more checks here if you use this!
if ($token['user_id'] > 0) {
\Luracast\Restler\User::init();
\Luracast\Restler\User::setUniqueIdentifier($token['user_id']);
return true;
}
return false;
}
Now you can get the currently authenticated user in your class by calling:
\Luracast\Restler\User::getUniqueIdentifier(true)

Categories