Get current user information in Apigility Resource - php

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();

Related

Returning JSON array in (old) PHP Symfony

Be gentle, I'm a hacker not a professional developer!
I think I'm close, but I am getting tripped up by the different ways that a Controller can retrieve data and return it as JSON (to a data table). I thought it would be simple enough to clone another feature that was working, but oh no!
This is an old (version 3.4) PHP Symfony. [In parallel I've been prep'ing a test site to start the upgrade work].
Current State of Play: Just a single row of many is returned. But it is in the right JSON format (and happily rendered by the data table).
The controller function is ...
/**
* #Route("/{slug}/feed", name="project_feed")
* #Method({"GET", "POST"})*
* #param Request $request
* #param Project $project
* #return JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function getProjectFeed(Request $request, Project $project)
{
$em = $this->getDoctrine()->getManager(); //Get feed for current project
$query = $em->createQuery('SELECT a FROM AppBundle:ProjectFeed a WHERE a.project = :project_id');
$this->logger->debug("ProjectController.getProjectFeed ... query == ", [$query]);
$query->setParameter('project_id', $project->getId());
$feed = $query->getResult(); // Retrieve feed if none exist return empty JSON response
// Doesn't populate Modifier
//$feed = $this->getDoctrine()->getRepository("AppBundle:Project")->getProjectFeed($project);
$serializer = $this->get('app.service.serializer');
return new Response($serializer->serializeEntity($feed, array('data_table')));
}
Previously: I have had all rows returned, but there is a relationship in the entity that doesn't get populated and is completely dropped from the JSON.
The repository function was ...
* #param Project $project
* #return array
*/
public function getProjectFeed(Project $project)
{
$em = $this->getEntityManager();
$sql = 'SELECT a FROM AppBundle:ProjectFeed a WHERE a.project = :project_id';
$query = $em->createQuery($sql);
$query->setParameter('project_id', $project->getId());
return $query->getResult(Query::HYDRATE_ARRAY);
}
Any pointers on the correct way to do this so I ended up with all rows returned and the correctly formatted JSON with the related entity data populated would be most welcome. Thanks.

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();
}

Am I doing eager loading correctly? (Eloquent)

I have a method that needs to pull in information from three related models. I have a solution that works but I'm afraid that I'm still running into the N+1 query problem (also looking for solutions on how I can check if I'm eager loading correctly).
The three models are Challenge, Entrant, User.
Challenge Model contains:
/**
* Retrieves the Entrants object associated to the Challenge
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function entrants()
{
return $this->hasMany('App\Entrant');
}
Entrant Model contains:
/**
* Retrieves the Challenge object associated to the Entrant
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function challenge()
{
return $this->belongsTo('App\Challenge', 'challenge_id');
}
/**
* Retrieves the User object associated to the Entrant
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
and User model contains:
/**
* Retrieves the Entrants object associated to the User
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function entrants()
{
return $this->hasMany('App\Entrant');
}
The method I am trying to use eager loading looks like this:
/**
* Returns an array of currently running challenges
* with associated entrants and associated users
* #return array
*/
public function liveChallenges()
{
$currentDate = Carbon::now();
$challenges = Challenge::where('end_date', '>', $currentDate)
->with('entrants.user')
->where('start_date', '<', $currentDate)
->where('active', '1')
->get();
$challengesObject = [];
foreach ($challenges as $challenge) {
$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all();
$entrantsObject = [];
foreach ($entrants as $entrant) {
$user = $entrant->user;
$entrantsObject[] = [
'entrant' => $entrant,
'user' => $user
];
}
$challengesObject[] = [
'challenge' => $challenge,
'entrants' => $entrantsObject
];
}
return $challengesObject;
}
I feel like I followed what the documentation recommended: https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
but not to sure how to check to make sure I'm not making N+1 queries opposed to just 2. Any tips or suggestions to the code are welcome, along with methods to check that eager loading is working correctly.
Use Laravel Debugbar to check queries your Laravel application is creating for each request.
Your Eloquent query should generate just 3 raw SQL queries and you need to make sure this line doesn't generate N additional queries:
$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all()
when you do ->with('entrants.user') it loads both the entrants and the user once you get to ->get(). When you do ->load('user') it runs another query to get the user. but you don't need to do this since you already pulled it when you ran ->with('entrants.user').
If you use ->loadMissing('user') instead of ->load('user') it should prevent the redundant call.
But, if you leverage Collection methods you can get away with just running the 1 query at the beginning where you declared $challenges:
foreach ($challenges as $challenge) {
// at this point, $challenge->entrants is a Collection because you already eager-loaded it
$entrants = $challenge->entrants->sortByDesc('current_total_amount');
// etc...
You don't need to use ->load('user') because $challenge->entrants is already populated with entrants and the related users. so you can just leverage the Collection method ->sortByDesc() to sort the list in php.
also, You don't need to run ->all() because that would convert it into an array of models (you can keep it as a collection of models and still foreach it).

Different type of object for admin and user

I am building an intranet application and i want to be able to have 2 different types of users a regular user and an admin user. I am trying to figure out what would be the best way to go about doing this. Either to have one object for admin type stuff and then one object for user type stuff. Or combine both of that into one object. But i keep getting stuck and not sure how to go about doing that, or if that is even the best way.
Lets say I have the following situations:
1. query the db to get all tasks for all projects that are active.
Admin Query
2. query the db to get all tasks for all projects that are due today and active.
Admin Query
3. Query the db to get all tasks for a specific project that are active.
Admin Query
User Query
4. Query the db to get all tasks for a specific project that are active and due today.
Admin Query
User Query
5. Query the db to get all tasks for a specific project.
Admin Query
User Query
6. Query the db to get all tasks for a specific project, with different status specified.
Admin Query
7. Any one of those queries has an optional parameter to either get the count or the data.
I started the following object but now im a little stuck as which route to go:
public function getTasks($status, $project, $type = "count", $duetoday = NULL)
{
try
{
if($duetoday != NULL){
$today = date("Y-m-d");
$stmt = $this->db->prepare("SELECT * FROM tasks WHERE status=:status
AND $project=:project AND duedate BETWEEN :duedate
AND :duedate");
$stmt->execute(array(':status'=>$status,':project'=>$project,':duedate'=>$today));
}else{
$stmt = $this->db->prepare("SELECT * FROM tasks WHERE status=:status
AND $project=:project");
$stmt->execute(array(':status'=>$status,':project'=>$project));
}
$tasks=$stmt->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() > 0)
{
if($type == "count"){
return $stmt->rowCount();
}else{
return $tasks;
}
}else{
return false;
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
I will start with some words about the single responsibility principle. Basically, this means that an object and it's behaviors should have one responsibility. Here, I think your getTasks method is a good opportunity to refactor some code into better object oriented code.
There are actually many things it is doing:
Generate sql
Execute a query
Control the flow of the program
The method generating sql should not have to worry about it's execution, and the method executing it should not have to worry about getting it. This, as a side effect, will also reduce the nesting in a single method.
There is a lot of code to write, which I'll let you do, but if you create classes that implements those interfaces and a controller to use them, you should be able to get through this and write easier to maintain / refactor code:
interface SqlGenerating {
/**
* #param array $params
* #return string
*/
public function makeSql(array $params);
/**
* #param array $params
* #return array
*/
public function makeValues(array $params);
}
interface DBAccessing {
public function __construct(\PDO $pdo);
/**
* #param string $sql
* #param array $values
* #return PDOStatement
*/
public function getStmt($sql, array $values = []);
}
class Controller {
public function __construct(SqlGenerating $sqlGenerator, DBAccessing $dbAccess) {
// associate to private properties
}
public function getTasks($status, $project, $type = "count", $duetoday = null) {
// this function will use the sqlGenerator and the dbAccess to query the db
// this function knows to return the count or the actual rows
}
}
If you haven't already, this is a good time to learn about type-hinting in functions. This requires your function to be passed an object (or an array) to be assured of the behavior of the function. Also, you will notice that I type-hinted the interfaces into the controller. This is to actually be able to switch classes if ever you need a different one to manage sql and db access.

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