Access Control Filtering of Query before its sent to Prisma server/DB - prisma

Right now I am using accesscontrol to manage the ACL and it is working great. It looks something like this:
const methods = {
async update(parent, { data }, ctx, info) {
const acUpdate = ac.can('role').updateOwn('model')
if (! acUpdate.granted) throw new ACError()
const filtered = acUpdate.filter({ ...data })
return await ctx.db.mutation.updateOrganization({
data: filtered,
where: { id }
}, info)
}
}
However, on a Query method from GraphQL I don't know how to filter the requests to the DB. For example, on a nested query it may look like this:
{
model {
id
name
user {
id
name
pictures {
id
name
}
}
}
}
So on the resolver it would check if they have access to Model, then it would send the request to the Prisma server without filtering the GQL schema. In this scenario let's say that the user has access to read model but not user. Ideally I'd like to do a permission.filter(...) on the actual request schema (info?) before sending it to Prisma. Have any of you solved this? Of course its possible to filter the request after it has resolved, but that level of computation is not necessary and can cause issues if abused.

I found out this is the topic I was addressing in one of my issue responses because I thought it was asked there. I recognize now that I must have confused it with this one being open in one of the tabs in the back.
https://github.com/maticzav/graphql-shield/issues/113#issuecomment-423766569
I think that the second part of my response concerns you the most. I hope you find it helpful! 🙂

I was having the exact same problem and i am now solving it by using prisma client for making the requests to prisma. Prisma client only queries one level deep each time so you get full control of the resolvers also in nested queries.
See https://stackoverflow.com/a/53703140/1391050

Related

Azure Remote Tables And Entity Framework: Join two tables

I read through some answeres here, but they dont seem to apply my usecase.
I have a a few tables running on Azure Mobile Apps Backend, I can query them just fine with entity framework.
I know that I could do two queries and just join my results on client side, but Im sure there is a better way.
My issue is however, that I have a table inside my middleware, and then a dataservice with a remote model class that I use to query data from my service. This is how I would query a User by Email:
public async Task<IEnumerable<UserItem>?> GetItemByEmail(string email)
{
await InitializeAsync();
try
{
return await _table.GetAsyncItems().Where(x => x.Email == email).ToListAsync();
}
catch (Exception e)
{
return null;
}
}
The issue however is, that this only works on the one table (UserItemTable) which is referencend in _table.
Now there is no reference to a second table with which I wanna join.
Maybe this isnt the right place to do this inside the RemoteTable Controllers?
Any help would be appreciated.

Is there a way to setup a field-level authorisation on FaunaDB + GraphQL?

I'm having troubles finding a way to hide user emails from everyone, except the owner (user has access to only his email). Is there a way to hide a certain document field, for a certain roles?
Here is an example I found that creates a role with dynamic access to the whole User collection:
CreateRole({
name: "tier1_role",
membership: {
resource: Collection("User"),
predicate: Query(
Lambda("userRef",
// User attribute based rule:
// It grants access only if the User has TIER1 role.
// If so, further rules specified in the privileges
// section are applied next.
Equals(Select(["data", "role"], Get(Var("userRef"))), "TIER1")
)
)
},
privileges: [
{
// Note: 'allUsers' Index is used to retrieve the
// documents from the File collection. Therefore,
// read access to the Index is required here as well.
resource: Index("allUsers"),
actions: { read: true }
}
]
})
I tried to change it a bit, but I wasn't able to set up field-level access.
Let's say I'd set up FaunaDB with GraphQL schema below.
enum UserRole {
TIER1
}
type User {
email: String! #unique
username: String! #unique
role: UserRole!
}
type Query {
allUsers: [User!]
}
type Mutation {
addUsers(new_users: [UserInput]): [User]
#resolver(name: "add_users", paginated: false)
}
How do create a FaunaDB role in such a way that all of the users (except the current one) in resulting array from allUsers query, will not have email field?
I could break User collection into two: one is public, the other is accessible to a document owner, but this sounds wrong.
I'm new to the noSQL concept, so maybe I'm looking at this problem from the wrong perspective?
it's a request that came up a few times. You probably want to do this straight in FaunaDB's ABAC role system but although it provides row-level security, hiding a specific field is currently not provided yet. The feedback has been logged though, we will look into it.
The current way to do this is to split out Users from Accounts and fetch Users instead of Accounts. It would be useful to have something like hidden fields though in the future.
If you think of it, in this case, it does make sense to split authentication information from User information. You never know that you might offer another way to authentication in the future. I still recall from the Phoenix Framework book that they do it there was well and considered it a good practice.
You could also make a thin wrapper using Apollo in a serverless function and filter out these fields when you pass through the results. There is a guide that explains how to build such a thin Apollo middleware that just delegates to FaunaDB https://www.gatlin.io/blog/post/social-login-with-faunadb-and-auth0

Joining entities with #ManyToMany relationship

I have these entities:
User
Role
Permission
A user has many roles and a role has many permissions.
What is the best way to retrieve a set of permission a user has?
I need a method to check if a User has a particular Permission.
This is what I have so far:
public boolean hasPermission(String permissionString) {
if (!authenticated) return false;
for (Role role : user.getRoles()) {
for (Permission permission : role.getPermissions()) {
if (permission.getName().equals(permissionString)) {
return true;
}
}
}
return false;
}
A second, but related question -- where should I put the code that checks if a user has a particular permission?
In the User entity?
In the UserBean EJB?
In the Authentication JSF Managed Bean?
It depends on your mappings, the number of objects in the list, if the lists have already been fetched, your database connections, the database tuning etc.
You would have to try with production data to determine what ways are best.
For instance, If your collections have been prefetched with a join query, then traversing them in Java is trivial. If they haven't, each access in the for loop would cause a query to populate the objects. If it is the last one all the time, it means your java code causes you to traverse your object graph in the worst way possible and it would have been better to fetch it upfront. So you would be losing any benefit of lazy access, and would be better of hitting the database once to query for the permission linked to this user with the permissionString name: "Select p from u User join u.roles r join r.permissions p where p.name = :permissionName".
Only testing on production data will give you the best answer for your situation, and numerous other decisions in the application and mappings change the outcome.

Laravel with mongodb belongsToMany sync

Im working with Laravel 4 and mongodb 2.0.4 module
I have User and Role class and Im trying to use belongsToMany relation with attach, detach and sync methods
User class
public function roles()
{
return $this->belongsToMany('Role', null, 'user_ids', 'role_ids');
}
Role class
public function users()
{
return $this->belongsToMany('User', null, 'role_ids', 'user_ids'
}
When I run attach method
$user = User::find($id);
$user->roles()->attach(array($role_id));
mongodb generate one of the query wrong (or not?)
user.update({"_id":{"$id":"54f8d7802228d5e42b000036"}},{"$addToSet":{"role_ds":{"$each":["54f8d7b02228d5e42b000037"]}}},{"multiple":true})
role.update({"_id":["54f8d7b02228d5e42b000037"]},{"$addToSet":{"user_ids":{"$each":["54f8d7802228d5e42b000036"]}}},{"multiple":true})
user collection is updated but role collection stay intact.
It should generate query like this?
role.update({"_id":{"$id":"54f8d7b02228d5e42b000037"}},{"$addToSet":{"user_ids":{"$each":["54f8d7802228d5e42b000036"]}}},{"multiple":true})
This problem is present with both attach and detach methods. Only sync runs correctly. But only if there is one element. If you run sync on multiple elements one of the collections stays always intact because of wrong query.
Am I missing something or there is really a problem with this relation?
Any help would be great. Thank you
Replace
$user->roles()->attach(array($role_id));
With
$user->roles()->attach($role_id);
If your parameter is not array, you have to use attach method. sync method only accept array as parameter. Here is a good explanation about them. Hope it will be useful for you.

How to hide properties on Azure Mobile Service responses?

I have a Mobile Service with Model classes and DTO classes. I mapped these using Fluent API and got it to work to perform CRUD operations, but I have a problem with the JSON responses returned in some instances.
Take for example a User entity with user name and password. To register a user I have my PostUserDTO method like this:
// POST tables/UserDTO
public async Task<IHttpActionResult> PostUserDTO(UserDTO item)
{
string hashString = PasswordHash.CreateHash(item.Password);
item.Password = hashString;
UserDTO current = await InsertAsync(item);
current.Password = "";
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
On the method I read the password property sent from the client and then proceed to hash it with a salt, replace the value in the object sent and save it to the database, then I proceed to empty the password so the hashed result isn't returned.
In this case the best practice would be to omit the password property from the response, this also should happen when retrieving all of the users in the table, I only want to return specific information, some information generated by my server should stay out from the response.
How can I select or alter the information from the responses? Do I have to create other DTOs for every kind of response I desire?