GraphQL relations leaking data, even with context.user resolver already set. How to prevent data exposure via relations? - graphql

How is everyone doing authentication across relations to prevent data from being traversed via relations?
For example we have a Shop which has Users.
// Returns error as i've set custom resolver to allow only context.user.is_shop_owner
{
shops {
name
users {
email
...
}
}
}
This query is normally blocked with a custom resolver like context.user.is_shop_owner, so you cannot execute this from root query.
However, if a malicious person traverses relations to reach the users object he is able to get the sensitive user data.
// Data exposed un-intendedly due to relation traversal. How to prevent this?
{
products {
name
price
shop {
users { ... } // boom, exposed
}
}
}
Is this a flaw in graphql? How are you guys working around this?
This is on a python-graphene stack btw.
Edit: Btw, i know we can do exclude_fields, but then i won't be able to access Users from the ShopNode, which is an important information to query for the ShopNode, so limiting fields are probably not a good idea. (edited)

This should probably be controlled within the Shop type, to return null when the user does not have the right permissions. Otherwise if a Shop were accessed from a second field, you'd have to duplicate the check.

I wound up setting custom resolvers for each node to block out the relations you want to limit access to based on the context.user. Refer to the code below in response to my question above.
class ProductNode(DjangoObjectType):
class Meta:
model = Product
interfaces = (graphene.relay.Node)
# Exclude nodes where you do not need any access at all
exclude_fields = ['users']
# For nodes where you need specific/limited access, define custom resolvers.
# This prevents the relation traversal loophole
def resolve_shop(self, args, context, info):
""" Allow access to nodes only for staff or shop owner and manager """
if get_is_staff_user(context.user, self):
return self.shop
Operations.raise_forbidden_access_error()

Related

Data normalization in GraphQL query

I'm using GraphQL to query a database that has two data types: User and Group.
Groups have a field users which is an array of User objects which are in that group. I have one field at root named groups which returns an array of all of my groups.
A typical query might look something like this:
{
groups {
id,
name,
users {
id,
name,
address,
email,
phone,
attitude,
job,
favoriteQuote,
favoriteColor,
birthday
}
}
}
The problem is that a lot of those users can belong to multiple groups and, seeing as User has a lot of fields, this can make responses quite large.
Is there any way to get one set of fields for the first instance of an object, and a different set for every other instance in the response?
I only need name, job, email etc etc once per user in the response, and just the id thereafter (I can do my own normalization afterwards).
alternatively
Is there any way to only get id fields for all users in groups and return a separate array of all unique User objects that have been referenced in the query (which is not all User objects)?
Is there any way to get one set of fields for the first instance of an object, and a different set for every other instance in the response?
No. The same set of fields will be returned for each item in a list unless the type of the individual item is different, since a separate selection set can be specified for each type returned at runtime.
Is there any way to only get id fields for all users in groups and return a separate array of all unique User objects that have been referenced in the query (which is not all User objects)?
You could design your schema to accommodate this. Something like
{
groups {
nodes {
id
name
users {
id
}
}
uniqueUsers {
id
# other fields
}
}
}
Your groups resolver would need to handle all the normalization and return the data in the appropriate shape. However, a simpler solution might be to just invert your relationship:
{
users {
id
name
address
email
phone
attitude
job
favoriteQuote
favoriteColor
birthday
groups {
id
name
}
}
}
Generally - usually
... normalization ... of course ... f.e. using apollo and it's normalized cache.
All records returned from API has to be the same shape.
You can get data and render some <MembersList/> component using query for ids and names only (full/paginated).
Later you can render details in some <UserProfile/> component with own query (hook useQuery inside) to fetch additional data from cache/api (controllable).
Your specific requirements - possible
1st option:
Usually response is of one common shape (as requested), but you can decide on resolver level what to return. This requires query structure changes that allows (API, backend) to null-ify some properties. F.e.
group {
id
name
users {
id
name
profile {
photo
email
address
With profile custom json type ... you can construct users resolver to return full data only for 1st record and null for all following users.
2nd option:
You can use 2 slightly different queries in one request. Use aliases (see docs), in short:
groupWithFullMember: group ( groupId:xxx, limitUsers:1 ) {
id
name
users {
id
name
address
email
...
}
}
groupMembers: group ( groupId:xxx ) {
id
name // not required
users {
id
name
}
}
Group resolver can return it's child users ... or users resolver can access limitUsers param to limit response/modify db query.

Cache nested results

In my app, an user might request which friends did a specific action:
query($id: Int) {
post(id: $id) {
likes {
id
name
photo
}
}
}
But that means re-fetching the id, name and photo data for the same objects for different posts (since the server doesn't know what I already have cached). What strategy I could use to try leverage the cache here?
Only thing I can think of is in the query above I only request the id field and have separate queries for the name and photo and try to batch those.
There is no built-in way in GraphQL to share objects in the same result object graph returned. You have actually answered your own question. By normalizing the resultant graph of objects using IDs, it does reduce the amount of JSON you'd be transmitting back to the client. However, it requires the clients to request the data in a different way. For example,
query($id: Int) {
post(id: $id) {
likes {
id
}
}
likesByPostId(postId: $id) {
id
name
photo
}
}
In GraphQL, we let the clients make the decision on how to optimize the data fetching. The server simply returns the data in the shape the client requests. So you can't really decide for the clients.

Laravel roles and permissions

I am new with Laravel and i am trying to build an application based on roles, but in my case the user can have only one role (there is no a pivot table between users and roles) and we can create new role us we like(assign many permissions to one role). Any help? and thanks a lot
So here is one way I would do it.
You need 2 tables :
One table I would call "ranks", inside it you can have everything about the rank itself :
Its id of course, but also :
Is it an admin rank ? (all permissions)
What's it name ? ...
One table I would call "ranks_abilities", inside it you can have everything about what a rank can do
Therefore, it would have three columns : an id, a rank_id, and the name of the ability
And you need to put the rank_id inside the users table.
Note : if you wanna do it really nicely, you can have a table "abilities" containing all the possible abilities, and you'd reference their ids instead of the name
So how would it work ?
You would therefore have three models :
User
Rank
RanksAbility
Inside of your User model, you'd have a belongs_to relationship to the Rank model (call it rank)
Inside of the Rank model, you'd have a has_many relationship to the RanksAbility model (call it ranks_abilities)
I guess we are now fine with the database structure, let's get to the point of allowing to do something.
So, of course, where a login is required, you have the auth middleware that does it perfectly
Then, to handle the permissions itself, there are several ways to do it, here is one I would recommend.
Step 1 :
Create a policy for some action for example if you have posts you can create a PostPolicy (https://laravel.com/docs/5.4/authorization#creating-policies)
If, you want, for example, a permission so that a user can edit all posts, you'd have an update method in that PostPolicy
public function update(User $user, Post $post)
{
return $user->hasPermission('post.update'); // You can also add other permissions for example if the post belongs to your user I'd add || $post->user_id == $user->id
}
I would do something like that.
Then, you'd have to make the hasPermission method.
So, in your User model, you could put something like this :
public function hasPermission($permission){
if(!$this->relationLoaded('rank')){
$this->load('rank', 'rank.ranks_abilities');
}
if(!$this->rank){
return false;// If the user has no rank, return false
}
foreach($this->rank->rank_abilities as $ability){
if($permission === $ability->name){
return true;// If the user has the permission
}
}
return false;
}
And here the method is done.
Last step, you can use all the methods listed here https://laravel.com/docs/5.4/authorization#authorizing-actions-using-policies to avoid an user from doing something he can't.
Personally, I would do a FormRequest, and handle the authorization with it (https://laravel.com/docs/5.4/validation#authorizing-form-requests).
I hope to be clear, if you have any more questions go ahead
For setting up customized user roles and permissions, you may give a try to https://thewebtier.com/laravel/understanding-roles-permissions-laravel/.
It's a complete guide on how to setup roles and permissions in your Laravel project.

Does Eloquent handle caching of related entities?

I'm exploring Laravel's Eloquent as a drop-in replacement for my project's current, home-grown active record data layer. Currently, I have a class User that supports a many-to-many relationship with another class, Group. My current implementation looks something like:
class User {
protected $_groups; // An array of Group objects to which this User belongs
public function __construct($properties = []){
...
}
public function groups() {
if (isset($_groups))
return $_groups;
else
return $_groups = fetchGroups();
}
private function fetchGroups() {
// Lazily load the associated groups based on the `group_user` table
...
}
public function addGroup($group_id) {
// Check that the group exists and that this User isn't already a member of the group. If so, insert $group_id to $_groups.
...
}
public function removeGroup($group_id) {
// Check that the User is already a member of the group. If so, remove $group_id from $_groups.
...
}
public function fresh() {
// Reload user and group membership from the database into this object.
...
}
public function store() {
// Insert/update the user record in the `user` table, and insert/update/delete records in `group_user` based on the contents of `$_group_user`.
...
}
public function delete() {
// If it exists, delete the user record from the `user` table, and delete all associated records in `group_user`.
...
}
}
As you can see, my class:
Performs lazy loading of related groups, caching after the first time they're queried;
Maintains an internal representation of the User's relationship with their Groups, updating in the database only when store is called;
Performs sanity checks when establishing relationships, making sure that a Group exists and is not already related to the User before creating a new association.
Which, if any of these things, will Eloquent automatically take care of for me? Or, is my design flawed in some way that Eloquent can solve?
You can assume that I will re-implement User as User extends Illuminate\Database\Eloquent\Model and use Eloquent's belongsToMany as a replacement for my current fetchGroups method.
Eloquent caches the results of relationships internally, yes. You can see that in action in the Model::getRelationValue() method.
Eloquent also provides you with methods to help you manage the many-to-many relationship. You could implement this functionality inside your existing API. However, here are some things to look out for:
When using attach(), detach(), etc., the queries are performed immediately. Calling the parent User::save() method would only save the User's details, not the many-to-many relationship information. You could work around this by storing the IDs passed to your API temporarily, and then act upon them when you call User::store().
No sanity checks are performed when using attach/detach/etc. It would be good to apply these in your API if they're needed.
Adding or removing an ID to/from a many-to-many relationship will not affect the cached results of the initial relationship query. You would have to add in logic to insert or remove the related models into the collection.
For example, let's say a User has two Groups. When I load the user, I can access those groups using $user->groups. I now have a Collection of Groups cached inside the User model. If I call for $user->groups again, it will returned this cached Collection.
If I remove one Group using $user->detach($groupId), a query will be performed to update the join table, but the cached Collection will not change. Same goes for adding a group.

Yii2 authentication based on record level

Within yii2 I need some RBAC access control also on record level. As a yii2 beginner I'm searching for the best point to enter the logic but struggle through the documents.
Example:
A table Children mentions a child, besides lots of other children.
A child usually has two parents in table Parents.
Besides other access control with yii2-admin/user these two parents can view and manipulate the record of their own child/ren but not others.
the logged in user is a parent.
Example table Children:
|id|name|age|
|1|Max|10|
|2|Moritz|11|
|3|Lena|8|
...
Example table Parents:
|id|relation|name|
|1|mother|Anna|
|2|father|Paul|
|3|mother|Lisa|
...
Example table Xref (Relation to Children and Parents):
|child_id|parent_id|
|1|1|
|1|2|
|2|3|
|3|1|
|3|2|
...
I think the activeRecord class Children would be the the right place for a behaviour like that, right?
Does someone have an example code to point me to the right direction for an efficient code,
where Paul and Anna could modify the record for 'Max' but not for 'Moritz'?
I hardly ever use relational DBs with Yii2 but I will illustrate what I meant as best as possible:
I'm going to illustrate the Parent Class as the child will be roughly similar (appart from the relationship direction):
class ParentModel extends ActiveRecord
{
/**
* #return string the name of the table associated with this ActiveRecord class.
*/
public static function tableName()
{
return 'Parents';
}
public function getChildren()
{
// ParentModel has_many ChildModel via Xref.child_id -> id
return $this->hasMany(ChildModel::className(), ['id' => 'child_id'])
->viaTable('Xref', ['parent_id' => 'id']);
}
}
With this you can get all children by using (for example):
$user = ParentModel::findOne($userID);
$children = $user->children;
You can then use all RBAC functionalities to make sure you users can't access an edit form action/view. Or use RBAC rules to make sure only the appropriate content is being handled.
Or you could write your own checks to make sure (for example) that the children being handled really belong to the Parent accessing the controller action. (by say... comparing the user's children to the ones being handled, though depending on your structure this could be handled by RBAC Rules)
But regardless of the actions, that logic should remain on the controller layer. On some very rare occasions you might have to put access right logic in your models but the models should have that logic separated and not related to the RBAC system. I've personally had this issue with graph traversal logic which is on an ActiveQuery level. But that's another issue in it's own right.

Resources