Restler+OAuth2 - Identifying the user correctly - php

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)

Related

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

symfony/doctrine: cannot use object without refreshing

It's the first time I run into this problem. I want to create a doctrine object and pass it along without having to flush it.
Right after it's creation, I can display some value in the object, but I can't access nested object:
$em->persist($filter);
print_r($filter->getDescription() . "\n");
print_r(count($filter->getAssetClasses()));
die;
I get:
filter description -- 0
(I should have 19 assetClass)
If I flush $filter, i still have the same issue (why oh why !)
The solution is to refresh it:
$em->persist($filter);
$em->flush();
$em->refresh($filter);
print_r($filter->getDescription() . " -- ");
print_r(count($filter->getAssetClasses()));
die;
I get:
filter description -- 19
unfortunately, you can't refresh without flushing.
On my entities, I've got the following:
in class Filter:
public function __construct()
{
$this->filterAssetClasses = new ArrayCollection();
$this->assetClasses = new ArrayCollection();
}
/**
* #var Collection
*
* #ORM\OneToMany(targetEntity="FilterAssetClass", mappedBy="filterAssetClasses", cascade={"persist"})
*/
private $filterAssetClasses;
public function addFilterAssetClass(\App\CoreBundle\Entity\FilterAssetClass $filterAssetClass)
{
$this->filterAssetClasses[] = $filterAssetClass;
$filterAssetClass->setFilter($this);
return $this;
}
in class FilterAssetClass:
/**
* #var Filter
*
* #ORM\ManyToOne(targetEntity="App\CoreBundle\Entity\Filter", inversedBy="filterAssetClasses")
*/
private $filter;
/**
* #var Filter
*
* #ORM\ManyToOne(targetEntity="AssetClass")
*/
private $assetClass;
public function setFilter(\App\CoreBundle\Entity\Filter $filter)
{
$this->filter = $filter;
return $this;
}
Someone else did write the code for the entities, and i'm a bit lost. I'm not a Doctrine expert, so if someone could point me in the good direction, that would be awesome.
Julien
but I can't access nested object
Did you set those assetClasses in the first place?
When you work with objects in memory (before persist), you can add and set all nested objects, and use those while still in memory.
My guess is that you believe that you need to store objects to database in order for them to get their IDs assigned.
IMHO, that is a bad practice and often causes problems. You can use ramsey/uuid library instead, and set IDs in Entity constructor:
public function __construct() {
$this->id = Uuid::uuid4();
}
A database should be used only as a means for storing data. No business logic should be there.
I would recommend this video on Doctrine good practices, and about the above mentioned stuff.
Your problem is not related to doctrine nor the persist/flush/refresh sequence; the problem you describe is only a symptom of bad code. As others have suggested, you should not be relying on the database to get at your data model. You should be able to get what you are after entirely without using the database; the database only stores the data when you are done with it.
Your Filter class should include some code that manages this:
// Filter
public function __contsruct()
{
$this->filterAssetClasses = new ArrayCollection();
}
/**
* #ORM\OneToMany(targetEntity="FilterAssetClass", mappedBy="filterAssetClasses", cascade={"persist"})
*/
private $filterAssetClasses;
public function addFilterAssetClass(FilterAssetClass $class)
{
// assuming you don't want duplicates...
if ($this->filterAssetClasses->contains($class) {
return;
}
$this->filterAssetClasses[] = $class;
// you also need to set the owning side of this relationship
// for later persistence in the db
// Of course you'll need to create the referenced function in your
// FilterAssetClass entity
$class->addFilter($this);
}
You may have all of this already, but you didn't show enough of your code to know. Note that you should probably not have a function setFilterAssetClass() in your Filter entity.

TYPO3: Keep forms filled after form submit

I created an extension which allows the user to sign up via the frontend. I couldn't use working ones because the client requested special tasks.
This is the code which detects taken usernames.
public function createAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX)
{
$uname = $newFeUserX->getUsername();
$select_query = '*';
$from_table = 'fe_users';
$where_clause = 'username="'.$uname.'"';
$test = $GLOBALS['TYPO3_DB']->exec_SELECTquery($select_query, $from_table, $where_clause);
if ($GLOBALS['TYPO3_DB']->sql_num_rows($test)) {
$this->addFlashMessage('Username is already taken.', '', \TYPO3\CMS\Core\Messaging\AbstractMessage::ERROR);
$this->redirect('new');
} else {
// do stuff when the username isn't taken yet
}
}
But unfortunately and obivously, when redirecting back to the new action, the fields are empty again.
Is there a way to pass the arguments back to the new action and fill the forms?
Yes, and extbase has a standardized way to do this. It works as follows:
If an action is called, its parameters are validated, except if validation is switched off in the doc comments. If validation fails, the previous action (the one whose view contained the submitted form) is called again, with the same parameters.
You can use this as follows:
/**
* #param \Vendor\Feregister\Domain\Model\FeUserX $newFeUserX
* #ignorevalidation $newFeUserX
*/
public function newAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX = null)
{
$this->view->assign('user', $newFeUserX);
// View renders form with name="newFeUserX" and object="{user}",
// action="create", fields use the property-attribute to fill
// in values and field names.
}
/**
* #param \Vendor\Feregister\Domain\Model\FeUserX $newFeUserX
* #validate $newFeUserX \Vendor\Feregister\Validator\UsernameDoesNotExistValidator
*/
public function createAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX)
{
// Do something with the user - you can be sure the username
// is not yet taken
}
The class \Vendor\Feregister\Validator\UsernameDoesNotExistValidator is a custom validator that implements the ValidatorInterface, or extends AbstractValidator. It should basically do the validation you are doing in your createAction (maybe using a repository instead of $GLOBALS['TYPO3_DB']). A validator returns errors in a standard way, making it easier to show nice error messages and localize them.
If the validation fails, extbase will try to forward to the action that rendered the form, in this case the new-action. In this case, it will work, because of the #ignorevalidation annotation on the new-action.
In addition, information about validation errors are available in the view, you can render them using the ViewHelper f:form.validationResults.

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

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.

Categories