Hasura: Allow users to not vote for their own post - graphql

I have three models User, Post, Vote
I tried to create a role-based authorization where the author (the user who creates a post/blog) can't vote for their own post/blog. To identify users, I used Hasura session variables X-Hasura-User-Id. Configuring (Row insert) Permission Rules for Vote table by,
Error:
{
"errors": [
{
"extensions": {
"path": "$.selectionSet.insert_Vote_one.args.object",
"code": "permission-error"
},
"message": "Check constraint violation. insert check constraint failed"
}
]
}
But which given constraint violation for the author and the other users when they try to vote a post/blog. How to solve that issue for the latter case using Permission Rules?
Update
Auth SetUp
I use one of my auth server(express) to create user and access_token which contain the user.id as Hasura session variables X-Hasura-User-Id.
Then I use this access_token to maintain role-based authorization:

It seems that you are to make the following rule: X-Hasura-User-Id != Vote.blog.User_id (assuming a hasura relationship called blog for FK Vote.Blog_id).
What you are doing instead is making sure that Vote.User_id != X-Hasura-User-Id. I am assuming the user who submits the vote will always have the same id as themselves. This will always result in a permissions constraint violation.
Unless I misunderstood something...
Let us know if that helps.

Related

Way to limit number of records a user can create in Amplify GraphQL API

I have an app where Auth is implemented using Cognito User Pools and API is a GraphQL API implemented using Amplify. In the Schema definitions, is there an easy way to limit the number of records a user can create. For example in the following schema...
type Product #model #auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
description: String
}
I would like to limit the users to a maximum of 100 Products.
One way is via my front-end. When I detect that a user has reached 100 limit, I can just make the UI stop giving them the ability to add more. But if someone were to bypass the UI, they could create more than 100. Hence, I prefer to enforce this limit in the backend.
Is there a way to do this in the Schema definition, or elsewhere in AWS / DynamoDB ?
Thanks!
There isn't a straightforward way to do this that I'm aware of.
Below is how I would solve this.
Create a #key on Product on the owner property, so that you can query by owner.
Overwrite the CreateProduct mutation. In your custom resolver, before creating a new Product, query the Product table byOwner, using the owner id passed in, to count how many already exist.
Here is the documentation: https://docs.amplify.aws/cli/graphql-transformer/resolvers#add-a-custom-geolocation-search-resolver-that-targets-an-elasticsearch-domain-created-by-searchable
I think the easiest solution would be processing the API request in a lambda function that validates the request (product count < 100) before having the script write to the DB. Then you can null out the built-in mutations for the model to prevent unintended access.
Example Schema:
type Mutation {
addProduct(input: ProductAddInput): ProductAddOutput #function(name: "productLambda-${env}")
}
type Product
#model(queries: null, mutations: null, subscriptions: null) /* update these to what you need */
#auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
description: String
}
In Lambda you can pull the username from the event.identity property and that should correlate to the owner field in the db. Since the AWS package is automatically loaded you should be looking at very fast script execution as long as your db indexes are set properly.
For the user product count, I see a couple of options:
A secondary index set up on the owner field so you don't do a ton of
scans
If you have a user table, you could add a field that counts
the products for each user and just update that table any time you
update the product table.

is there a way to link the created_by field that gets auto generated to users (create a relation) rather than it being related to the admin Users?

I'm new to Strapi tried creating a new collection and updating a value using postman for my endpoint.
The problem I'm having is that the "created_by" field seems to get auto-generated and will not allow me to update it using a created user credentials/id but it always picks the admin id.
I'm lost on this how can you relate the "created_by" field to your defined users rather than the admin table?
You shouldn't use "created_by" field.
You need a relation field between users and collection
Such as :
Relation with User (from: users-permissions)
Please check this "many to one relation" example.
So projectroot\api\bird\models\bird.settings.json will have the following lines:
"user": {
"via": "birds",
"plugin": "users-permissions",
"model": "user"
},

Unable to get gender from Google People API

I've got a problem obtaining gender information from People API.
I'm making a request to https://people.googleapis.com/v1/people/account_id which is not returning gender field. If I add genders to personFields it is giving me Requested entity was not found error.
It looks like obtaining this information is forbidden. Is there any chance to get this field?
There are two ways to use the Google People api.
The first assumes that you have used Oauth2 to authenticate your user.
GET https://people.googleapis.com/v1/people/me
returns the info about the current authenticated user.
The second is a public call to the api. you can use an API key or Oauth2.
GET https://people.googleapis.com/v1/people/117200475532672775346
This will return the info about a specific user {117200475532672775346} but it will depend upon what that user has set to public. The above number is my personal g+ account, the following is the gender response.
"genders": [
{
"metadata": {
"primary": true,
"source": {
"type": "PROFILE",
"id": "117200475532672775346"
}
},
"value": "female",
"formattedValue": "Female"
}
I have no idea where you are getting your account id this is a users google id. The information must be filled out on Google plus i suggest you check the users google+ account to see what they have set to public https://plus.google.com/u/0/117200475532672775346. Note: It doesn't matter if this is the current authenticated user if they dont have the info set public you cant see it in your application.
Tip: assuming you only want to see genders you can use the fields parameter to request just that
?fields=genders

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.

How to do an upsert or check results of a sub query before inserting using graphql

I need to do what is essentially an upsert. What's the efficient way of checking for an existing user and doing one mutation vs another. In my case I need to do a signin if the user exists, otherwise I should call create before signing in. I am using the graph.cool graphql service. I can do it as 2 seperate calls, but can is there a way I can write it in graphql so that it's done in one call and wouldn't require a 2nd roundtrip? Note that I don't have control of the backend and can only use the functions that exist.
https://docs.graph.cool/reference/simple-api/user-authentication
mutation {
// Only create a user if they do not exist already
// is there a way to do a conditional statement in graphql here?
createUser(authProvider: { auth0: { idToken: "<idToken>" }}) {
id
}
// always try signing the user in with the token we already got from auth0
signinUser(input: { auth0: { idToken: "<idToken>" }}) {
id
token
}
}
I think your question boils down to "how can I return either a token or an id depending on what the back end decided to do?"
The answer is: a union type.
union authResult = id | token
mutation {
authenticateUser(authProvider: { auth0: { idToken: "<idToken>" }}) {
authResult
}
}
Now you have delated the decision of "signin or create?" to the back end, and you get to do it in one trip and find out the result.
Note that because you want to do it in one trip, you must provide all the necessary information in one go - IE you have to provide enough info for the createUser step even if the back end doesn't do the create because the user already exists.

Resources