I have a graphql model in Amplify as follows:
type Blog #model #auth(rules: [
{ allow: owner },
{ allow: groups, groups: ["admins"] },
{ allow: public, provider: apiKey, operations: [read] }
]){
id: ID!
title: String!
content: String!
author: String
}
I would like all field to have read access via an API key except the 'author' field.It should only be readable by owner and the 'Admin' group.
I have tried all sorts of combinations of top level and field level #auth directives but it doesn't work.
Does anyone knows how to solve this.
Thank you
have you tried using the Field Level Authorization?
type User #model {
id: ID!
username: String
ssn: String #auth(rules: [{ allow: owner, ownerField: "username" }])
}
Update: My mistake, in your question it does say you tried this out.
Related
I am a bit confused about how authentication works using AWS Amplify (Cognito and AppSync bundle).
I have setup my authentication method to Cognito and I designed a datamodel using GraphQL API schema.
It looks almost like this (I skipped some elements has they are not relevant to my question):
type Grower #model
#auth(rules: [
{ allow: owner, ownerField: "admins" },
{ allow: owner, ownerField: "members", operations: [read, update, create]},
{ allow: owner, ownerField: "guests", operations: [read]}
]) {
code: String!
name: String!
admins: [String]
members: [String]
guests: [String]
workers: [Workers] #hasMany
orchards: [Orchard] #hasMany
}
type Orchard #model
#auth(rules: [
{ allow: owner }
]){
name: String!
location: String
grower: Grower! #belongsTo
harvests: [Harvest] #hasMany
visits: [Visit] #hasMany
}
Given this schema, will a user declared in the Grower.admins field be able to access an Orchard and its fields if it belongs to the Grower?
And, the opposite, will a user not declared as a member of this Grower (neither guests, member or admins) be able to see the Orchard?
Basically, I would like to understand if my authentication rules 'propagate' throught nested models.
Any comment about this datastructure is welcomed.
Using AWS AppSync GraphQL, I want to be able to have the #auth ownership of one type of document to be based on another. For example, let's say my schema looked like this:
type Course
#model
#auth(rules: [{ allow: owner, ownerField: "contributors", operations: [read, create, update, delete] }])
{
id: ID!
title: String!
contributors: [String!]!
questions: [Question!]! #hasMany
}
type Question
#model
#auth(rules: [{ allow: private, operations: [read, create] }, { allow: owner, operations: [read, create, update, delete] }])
{
id: ID!
title: String!
owner: String!
course: Course! #belongsTo
}
Notice in the above schema, a Course has many Questions, and a Question belongs to a Course. I want to allow the "contributors" of a Course to also have update/delete access on its child Questions, but in the above schema only the creator of the Question (which is any random signed-in user) will have update/delete access.
Is there a way that the Question type could set it's update/delete #auth access to allow not just the owner of the Question, but also of its parent Course "contributors"?
I have the following schema for an amplify project using GraphQL API:
type SomeItem
#model
#auth(
rules: [
{ allow: owner, operations: [create, delete, update] }
]
){
id: ID!
owner: String
version: Int!
text: String
read: Boolean #auth(rules: [{ allow: owner, operations: [create, delete] }])
createdAt: AWSDateTime
updatedAt: AWSDateTime
}
What I want to be able to do is for only the Owner to create/update/delete every field.
I want to allow anybody to update the read field only. I have tried the above to make the read field less restrictive but it's not working. How can I make one field in the model less restrictive than the other fields? Do I need to set an #auth for each field individually?
Imagine I have the following AppSync GraphQL schema with a User type, and a Post type with an editors field set to an array of Users:
type User
#model
#auth(rules: [
{ allow: owner }
])
{
id: ID!
owner: String!
username: String!
}
type Post
#model
#auth(rules: [
{ allow: owner },
# Can I do this?
# { allow: owner, ownerField: "editors.owner", operations: [update] }
])
{
id: ID!
owner: String!
title: String!
content: String
editors: [User]
}
How do I create an #auth rule to give update permissions to the Users in the editors array?
If you're using the amazon Cognito user pool you should set the editor type inside Post to be an array of String and set the values to the Cognito ids of the users that you want to have access. This is explained in the amplify cli documentation.
To have the editors to be of type User I suggest you to create another paramether named differently (for example editorUsers) and connect it to the User model as described here
Your schema should look like this:
type User
#model
#key(name: "byUser", fields: ["username"])
#auth(rules: [
{ allow: owner }
])
{
id: ID!
owner: String!
username: String!
}
type Post
#model
#auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "editors", operations: [update] }
])
{
id: ID!
owner: String!
title: String!
content: String
editors: [String]
editorsUsers: [User] #connection(keyName: "byUser", fields: ["id"])
}
I'm trying to figure out how can you protect at field level a one-to-many #connection with #auth against mutations that shouldn't be allowed. (ie: deny a specific user to run a mutation that will end-up inserting posts as another user.)
Starting with the example for protecting a mutation at the field level: https://aws-amplify.github.io/docs/cli/graphql#field-level-authorization
I tried doing something like this:
type User #model #auth(rules: [{ allow: owner, ownerField: "id" }]) {
id: ID!
posts: [Post]
#connection(name: "UserPosts")
#auth(rules: [{ allow: owner, ownerField: "id" }])
}
type Post #model {
title: String!
user: User!
#connection(name: "UserPosts")
#auth(rules: [{ allow: owner, ownerField: "userPostId" }])
}
Then say there already is a user with an id of regular-user-id
Apparently my auth rules don't stop another user, say with id of: malicious-user-id to run this mutation:
mutation {
createPost(input:{
title:"Oh this is BAD!"
postUserId: "regular-user-id"
}) {
title
}
}
Running a simple query to make sure this really happened:
query {
getUser(id:"regular-user-id"){
posts{
items
{
title
}
}
}
}
=>
{
"data": {
"getUser": {
"posts": {
"items": [
{
"title": "Regular User title"
},
{
"title": "Oh this is BAD!"
},
]
}
}
}
}
I tried various ways to figure this one out and couldn't find any documentation about bi-directional field level authentication. I'm fairly new to AppSync so I think I must be not getting something, but then this is such common use-case scenario that I'm really surprised there isn't more documentation about it.
Some help would be really appreciated.
To protect the Mutation.createPost mutation such that only the owner of the Post as designated via the postUserId may access it you add an #auth directive to the Post object definition:
type Post #model #auth(rules: [{ allow: owner, ownerField: "postUserId" }]) {
title: String!
# This will use a field 'postUserId' by default.
user: User!
#connection(name: "UserPosts")
}
With this setup, a mutation:
mutation {
createPost(input:{
title:"Oh this is BAD!"
postUserId: "regular-user-id"
}) {
title
}
}
will fail if the logged in user is not "regular-user-id".
This answer may help fill things in as well https://github.com/aws-amplify/amplify-cli/issues/1507#issuecomment-513042021.
Should the malicious user be able to update the Post title? I know this isn't an exact answer to your question since you're focusing on the relation field, but from trying to understand how to do these kind of things myself, I read a few things about introducing some form of 'everyone' group so you can define auth for user's who aren't owners. Then you could put auth on the entire Post model so only owners can update any field;
type Post
#model
#auth(rules: [
{ allow: owner, ownerField: "userPostId" },
{ allow: groups, groups: ["everyone"], operations: [read] }
]) {
title: String!
user: User!
#connection(name: "UserPosts")
}
I'm super new to amplify (I'm currently evaluating whether to use it for a project I'm starting), so I could be completely wrong here. If you do actually want a semi-open model where just the reference field has auth, I'm not sure how to do that :(