I have a Team #model with #auth settings. The #model also has an admin field that implies who has CRUD credentials. Here is the model:
type Team
#model
#auth(
rules: [
{ allow: owner, operations: [create, update, delete] }
{ allow: owner, ownerField: "admins", operations: [create, update, delete] }
{ allow: owner, ownerField: "members", operations: [read, update] }
{ allow: owner, ownerField: "viewers", operations: [read] }
]
) {
id: ID!
name: String!
admins: [User!]!
members: [User]
viewers: [User]
teamInfo: TeamInfo!
}
type User #model {
id: ID!
name: String
}
type TeamInfo #model {
id: ID!
info: String
}
I have a few other tables, such as TeamInfo and others that are connected to the Team #model. How would the other tables inherit the Team #auth rules?
Related
How can I specify any authenticated user can read the resource, which is created by the Admin Group?
type SupportedBrand #model #auth(rules: [
{ allow: groups, groups: ["Admin"], operations: [ read, create, update, delete ] }
])
{
id: ID!
order: Int!
name: String!
enabled: Boolean!
}
I'm working with AWS Amplify on a social media platform and I have a graphql API for users Timelines and Posts.
The web app subscribes to the creation of Timelines and I'm trying to filter by the deleted field on Post. I know how to do it client-side but I'm having trouble filtering in appsync (since I don't want users to be able to get access to deleted posts). Would I use a resolver? If so where would that go in Amplify?
Schema:
type Post
#model(subscriptions: { level: public })
#key(name: "SortByTimestamp", fields:[ "type", "timestamp"], queryField: "listPostsSortedByTimestamp")
#key(name: "BySpecificOwner", fields:[ "owner", "timestamp"], queryField: "listPostsBySpecificOwner")
#auth(rules: [
# {allow: owner, ownerField: "owner", provider: userPools, operations:[read, create, delete]},
{allow: private, provider: userPools, operations:[read]},
{allow: private, provider: iam ,operations:[create]},
])
{
type: String! # Always `post`
id: ID
deleted: Boolean
title: String!
content: String!
owner: String
likes: Int!
downloads: Int!
timestamp: AWSTimestamp!
}
type Timeline
#model(subscriptions: { level: public })
#auth(rules: [
{allow: owner, ownerField: "userId", provider: userPools, operations:[read, create]},
{allow: private, provider: iam, operations:[create]},
])
#key(fields: [ "userId", "timestamp"])
{
userId: ID!
timestamp: AWSTimestamp!
postId: ID!
post: Post #connection(fields: [ "postId"])
}
I created a GraphQL API using AWS' Amplify. In the schema, I have a Comment model that looks like this:
type Comment
#auth(rules: [{ allow: owner }, { allow: private, operations: [read] }, { allow: public, operations: [read] }])
#model
#key(name: "byAuthor", fields: ["authorID"])
#key(name: "byPost", fields: ["postID"]) {
content: String!
createdAt: AWSDateTime!
id: ID!
owner: String
postID: ID!
updatedAt: AWSDateTime!
}
This gives the owner permission to create, read, update, and delete, and restricts unauthenticated/authenticated-non-owner users to read-only. This works as expected; however, the owner can update the ownerField's value, essentially attributing the comment to another user...which is a no-no. To prevent this, I tried using field-level permissions (see below); however, that doesn't appear to be stopping the update.
...
owner: String #auth(rules: [{ allow: owner, operations: [ create ]}])
...
Is there something I'm missing? Any help is much appreciated--thank you!
You're very close. Using a field-level #auth directive, you want to explicitly grant the owner the ability to create and read and explicitly deny the world the ability to update and delete. It might look something like:
type Todo
#model
#auth(rules: [{ allow: owner, ownerField: "author" }]) {
id: ID!
name: String!
author: String
#auth(
rules: [
{ allow: groups, groups: ["FORBIDDEN"], operations: [update, delete] }
{ allow: owner, ownerField: "author", operations: [create, read] }
]
)
}
Here, the "FORBIDDEN" group is a non-existent group. Its name can be anything so long as you do not plan to actually create a group by that name in the future. Because no user will ever have the "FORBIDDEN" group claim, any/all update or delete operations on the field will fail.
E.g. I mostly have Amplify's pre-generated "Many-to-one" schema, i.e., Blogs with Posts and Comments. I am trying to restrict users from posting (i.e. creating a Post) to another user's Blog. How do I do this?
type Blog
#model
#auth(
rules: [
{ allow: owner, operations: [create, update, read] }
{ allow: private, operations: [read] }
]
)
{
id: ID!
name: String!
posts: [Post]
#connection(keyName: "byBlog", fields: ["id"])
#auth(rules: [{ allow: owner, operations: [create, update, delete] }]) // doesn't work!
}
type Post
#model
#auth(
rules: [
{ allow: owner }
{ allow: private, operations: [read] }
]
)
#key(name: "byBlog", fields: ["blogID"]) {
id: ID!
title: String!
content: String!
blogID: ID!
blog: Blog #connection(fields: ["blogID"])
}
// Comments omitted for brevity
Using the AppSync API, I can create a post under the wrong blog, e.g.,
mutation CreatePostFor22 {
createPost(input: {blogID: "47ce4075-6fcd-433f-xyz", content: "hello world from appsync", title: "some title"}) {
id
owner
blog {
owner // doesn't match the post owner!
}
}
}
This is replicable from my webapp.
Thanks!
Background:
I'm using Amplify to create a type Item #model GraphQL datastore with an #auth allow owner which adds the owner as an CognitoID to the Item.
Question:
This item is readable for guests and I would like to show the owner (or other related metadata such as a username/email) for this item.
e.g. Cognito.GetUserAttributes(2b10fb7e-4472-43c9-9bb9-54219b5027a3)
Is somthing like this possible? How is this supposed to be designed for this use case? Do I have to add a lambda that adds the user meta data to the item when it is created?
I would like this solution to be scalable.
The schema:
type Item
#model
#auth(rules: [
{ allow: owner},
{ allow: groups, groups: ["Admin"] },
{ allow: private, operations: [read] },
{ allow: public, operations: [read] }
])
{
id: ID!
title: String!
description: String
}
From DynamoDB:
{
"__typename": "Item",
"_lastChangedAt": 1593707126605,
"_version": 1,
"createdAt": "2020-07-02T16:25:26.582Z",
"description": "Description",
"id": "0592fcd8-408e-406e-84bf-9f63eb43e147",
"owner": "2b10fb7e-4472-43c9-9bb9-54219b5027a3",
"title": "Item",
"updatedAt": "2020-07-02T16:25:26.582Z"
}
You can use a function resolver on a cognitoAttributes field. The lambda function can call admin-get-user in Cognito and return the attributes. The lambda function will receive the dynamo Item in the event.source property so you will have access to the owner id to make the call to Cognito.
type Item
#model
#auth(rules: [
{ allow: owner},
{ allow: groups, groups: ["Admin"] },
{ allow: private, operations: [read] },
{ allow: public, operations: [read] }
])
{
id: ID!
title: String!
cognitoAttributes: [String]! #function('cognitoGetUserAttributes-${env})
description: String
}