Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add storage access rules to outputs #1927

Merged
merged 15 commits into from
Sep 26, 2024
Merged

add storage access rules to outputs #1927

merged 15 commits into from
Sep 26, 2024

Conversation

rtpascual
Copy link
Contributor

@rtpascual rtpascual commented Aug 27, 2024

Problem

Storage access rules defined in defineStorage are not in amplify_outputs.json.

Issue number, if available:

Changes

Adds storage access rules to amplify_outputs.json. Considering the following backend code:

// amplify/storage/resource.ts

export const storage = defineStorage({
  name: 'myBucket',
  access: (allow) => ({
    'media/*': [allow.authenticated.to(['read', 'write', 'delete'])],
    'other/*': [
      allow.guest.to(['read']),
      allow.authenticated.to(['read', 'write'])
    ]
  })
});

The outputs for storage will be (note: read is replaced with get and list):

"storage": {
  "aws_region": "us-east-1",
  "bucket_name": "amplify-myapp-mybucket",
  "buckets": [
    {
      "name": "myBucket",
      "bucket_name": "amplify-myapp-mybucket",
      "aws_region": "us-east-1",
      "paths": {
        "media/*": {
          "authenticated": ["get", "list", "write", "delete"]
        },
        "other/*": {
          "guest": ["get", "list"],
          "authenticated": ["get", "list", "write"]
        }
      }
    }
  ]
}

Corresponding docs PR, if applicable:

Validation

E2E and unit tests

Checklist

  • If this PR includes a functional change to the runtime behavior of the code, I have added or updated automated test coverage for this change.
  • If this PR requires a change to the Project Architecture README, I have included that update in this PR.
  • If this PR requires a docs update, I have linked to that docs PR above.
  • If this PR modifies E2E tests, makes changes to resource provisioning, or makes SDK calls, I have run the PR checks with the run-e2e label set.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Copy link

changeset-bot bot commented Aug 27, 2024

🦋 Changeset detected

Latest commit: 2b23793

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@aws-amplify/client-config Minor
@aws-amplify/backend-output-schemas Patch
@aws-amplify/integration-tests Patch
@aws-amplify/backend-storage Patch
@aws-amplify/backend Patch
@aws-amplify/backend-cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@rtpascual rtpascual added the run-e2e Label that will include e2e tests in PR checks workflow label Aug 28, 2024
@rtpascual rtpascual marked this pull request as ready for review August 28, 2024 17:15
@rtpascual rtpascual requested review from a team as code owners August 28, 2024 17:15
sobolk
sobolk previously approved these changes Aug 28, 2024
ashika112
ashika112 previously approved these changes Sep 4, 2024
Copy link
Member

@ashika112 ashika112 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall change looks good to me.

Question on npx ampx generate outputs, will customer be able to specify and generate version like 1, 1.1, 1.2?

@rtpascual
Copy link
Contributor Author

Question on npx ampx generate outputs, will customer be able to specify and generate version like 1, 1.1, 1.2?

Yes they can specify using the --outputs-version option

Copy link
Member

@AllanZhengYP AllanZhengYP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice the access can be 1 of enum value from 2 sets: read | write | delete or get | list | write | delete. Is it possible the output file only use the later one? It would make client side logic a lot easier.

@AllanZhengYP
Copy link
Member

The DX from the PR description makes sense. The JS library can resolve which of the authenticated, guest, owner, group the caller belongs to, but I'm not sure about the behavior of Functions. How does the client side know whether the client has the permission based on the Functions? I'm referring to this doc: https://docs.amplify.aws/flutter/build-a-backend/storage/authorization/#access-types

@rtpascual
Copy link
Contributor Author

I notice the access can be 1 of enum value from 2 sets: read | write | delete or get | list | write | delete. Is it possible the output file only use the later one? It would make client side logic a lot easier.

Yes we can make this change

@rtpascual
Copy link
Contributor Author

The DX from the PR description makes sense. The JS library can resolve which of the authenticated, guest, owner, group the caller belongs to, but I'm not sure about the behavior of Functions. How does the client side know whether the client has the permission based on the Functions? I'm referring to this doc: https://docs.amplify.aws/flutter/build-a-backend/storage/authorization/#access-types

Functions specifically have separate logic through environment variables that reference the resource they are given access to to avoid circular dependencies. For more information see https://docs.amplify.aws/flutter/build-a-backend/functions/grant-access-to-other-resources/.

@rtpascual rtpascual dismissed stale reviews from ashika112 and sobolk via 98662a8 September 6, 2024 19:12
'write',
'delete',
]);
const storageAccessActionEnum = z.enum(['get', 'list', 'write', 'delete']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this be a breaking change for deployed apps ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I forgot to think about impact on existing apps, adding this back in.

@@ -356,7 +356,7 @@
"$defs": {
"amplify_storage_access_actions": {
"type": "string",
"enum": ["read", "get", "list", "write", "delete"]
"enum": ["get", "list", "write", "delete"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may deprecate and not use read but we should keep it.

sobolk
sobolk previously approved these changes Sep 23, 2024
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed here.

Comment on lines 60 to 63
/**
* JSON schema
*/
$schema?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we typically remove this from the code-gen

Comment on lines 248 to 253
export interface AmazonLocationServiceConfig {
/**
* Map style
*/
style?: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the name property is missing from this type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might have misconfigured or am using a different version of json-schema-to-typescript. The closest I would get to the existing client config was running:
json2ts packages/client-config/src/client-config-schema/schema_v1.2.json -o packages/client-config/src/client-config-schema/client_config_v1.2.ts --style.singleQuote --ignoreMinAndMaxItems

I'll update this manually.

@@ -104,6 +103,7 @@ export class StorageOutputsAspect implements IAspect {
name: node.name,
bucketName: node.resources.bucket.bucketName,
storageRegion: Stack.of(node).region,
paths: node.accessDefinition,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't need to make changes here

const bucketSchema = z.object({
name: z.string(),
bucketName: z.string(),
storageRegion: z.string(),
});
?

We might also be missing some automated testing if we didn't catch this failure, or Zod is not strict here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to add paths to the amplify_outputs.json file. paths is optional so storage with no access rules defined will not fail and not have paths in their amplify_outputs.json.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the paths be added as optional since we are adding that in the bucketSchema object before stringifying.

Copy link
Contributor Author

@rtpascual rtpascual Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, and yup I believe Zod is not strict enough here, I'll add a test to make sure we cover for both cases of storage with access and without.

Comment on lines 543 to 547
paths: {
'path/*': {
guest: ['get'],
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add all five types of access rules we support here.

For groups, entity and resource, do we print the actual substituted value (for the group, entity and resource) or just the key name groups, entity etc.

Copy link
Contributor Author

@rtpascual rtpascual Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will add all the types of access rules.

For group and entity, we append the group name or entityId to the key name.

For resource since we use environment variables they go through different logic since we don't create storage policies.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added all the access rules but we won't see much benefit from adding more to this test since we are just mapping paths from the payload to the config. For key names, this is handled in access_builder.test.ts and tested there and for verifying the paths is built correctly that is done when we assert storageAccessDefinitionOutput in storage_access_orchestrator.test.ts.

Copy link
Member

@ashika112 ashika112 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving from JS client side. The outputs generated works as expected without breaking our existing outputs parsing logic.

@rtpascual rtpascual merged commit d538ecc into main Sep 26, 2024
60 of 62 checks passed
@rtpascual rtpascual deleted the storage-access-outputs branch September 26, 2024 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
run-e2e Label that will include e2e tests in PR checks workflow
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants