-
Notifications
You must be signed in to change notification settings - Fork 820
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add admin roles which have admin control over a graphql api
- Loading branch information
Showing
25 changed files
with
348 additions
and
304 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
type Todo @model @auth(rules: [{ allow: private, provider: iam }]) { | ||
id: ID! | ||
name: String! | ||
description: String | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,33 @@ | ||
import { | ||
addApiWithoutSchema, | ||
addApi, | ||
addAuthWithDefault, | ||
addDDBWithTrigger, | ||
addFunction, | ||
addS3StorageWithSettings, | ||
addSimpleDDB, | ||
AddStorageSettings, | ||
amplifyPush, | ||
amplifyPushAuth, | ||
amplifyPushForce, | ||
createNewProjectDir, | ||
deleteProject, | ||
deleteProjectDir, | ||
getBackendAmplifyMeta, | ||
getFunctionSrcNode, | ||
getProjectMeta, | ||
initJSProjectWithProfile, | ||
invokeFunction, | ||
overrideFunctionSrcNode, | ||
addNodeDependencies, | ||
readJsonFile, | ||
updateFunction, | ||
overrideFunctionCodeNode, | ||
getBackendConfig, | ||
addFeatureFlag, | ||
addAuthWithGroupsAndAdminAPI, | ||
getFunction, | ||
loadFunctionTestFile, | ||
updateApiSchema, | ||
createRandomName, | ||
} from 'amplify-e2e-core'; | ||
import fs from 'fs-extra'; | ||
import path from 'path'; | ||
import _ from 'lodash'; | ||
|
||
const GraphQLTransformerLatestVersion = 2; | ||
|
||
describe('nodejs', () => { | ||
describe('amplify add function with additional permissions', () => { | ||
let projRoot: string; | ||
|
@@ -262,6 +255,66 @@ describe('nodejs', () => { | |
expect(gqlResponse.data.createTodo.description).toEqual('sampleDesc'); | ||
}); | ||
|
||
it('should be able to query AppSync with minimal permissions with featureFlag for function and vNext GraphQL API', async () => { | ||
const appName = 'functiongqlvnext'; | ||
const random = Math.floor(Math.random() * 10000); | ||
const fnName = `apienvvar${random}`; | ||
const createTodo = ` | ||
mutation CreateTodo($input: CreateTodoInput!) { | ||
createTodo(input: $input) { | ||
id | ||
name | ||
description | ||
createdAt | ||
updatedAt | ||
} | ||
}`; | ||
await initJSProjectWithProfile(projRoot, { name: appName }); | ||
addFeatureFlag(projRoot, 'graphqltransformer', 'transformerversion', GraphQLTransformerLatestVersion); | ||
await addApi(projRoot, { | ||
IAM: {}, | ||
}); | ||
updateApiSchema(projRoot, appName, 'iam_simple_model.graphql'); | ||
const beforeMeta = getBackendConfig(projRoot); | ||
const apiName = Object.keys(beforeMeta.api)[0]; | ||
await addFunction( | ||
projRoot, | ||
{ | ||
name: fnName, | ||
functionTemplate: 'Hello World', | ||
additionalPermissions: { | ||
permissions: ['api'], | ||
choices: ['api'], | ||
resources: [apiName], | ||
operations: ['Mutation'], | ||
}, | ||
}, | ||
'nodejs', | ||
); | ||
// Pin aws-appsync to 4.0.3 until https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/647 is fixed. | ||
addNodeDependencies(projRoot, fnName, ['[email protected]', 'isomorphic-fetch', 'graphql-tag']); | ||
overrideFunctionCodeNode(projRoot, fnName, 'mutation-appsync.js'); | ||
await amplifyPush(projRoot); | ||
const meta = getProjectMeta(projRoot); | ||
const { Region: region, Name: functionName } = Object.keys(meta.function).map(key => meta.function[key])[0].output; | ||
const lambdaCFN = readJsonFile( | ||
path.join(projRoot, 'amplify', 'backend', 'function', fnName, `${fnName}-cloudformation-template.json`), | ||
); | ||
const urlKey = Object.keys(lambdaCFN.Resources.LambdaFunction.Properties.Environment.Variables).filter(value => | ||
value.endsWith('GRAPHQLAPIENDPOINTOUTPUT'), | ||
)[0]; | ||
const payloadObj = { urlKey, mutation: createTodo, variables: { input: { name: 'todo', description: 'sampleDesc' } } }; | ||
const fnResponse = await invokeFunction(functionName, JSON.stringify(payloadObj), region); | ||
|
||
expect(fnResponse.StatusCode).toBe(200); | ||
expect(fnResponse.Payload).toBeDefined(); | ||
const gqlResponse = JSON.parse(fnResponse.Payload as string); | ||
|
||
expect(gqlResponse.data).toBeDefined(); | ||
expect(gqlResponse.data.createTodo.name).toEqual('todo'); | ||
expect(gqlResponse.data.createTodo.description).toEqual('sampleDesc'); | ||
}); | ||
|
||
it('should be able to make console calls with feature flag turned off', async () => { | ||
const random = Math.floor(Math.random() * 10000); | ||
const fnName = `apienvvar${random}`; | ||
|
98 changes: 98 additions & 0 deletions
98
...lify-graphql-auth-transformer/src/__tests__/__snapshots__/amplify-admin-auth.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Test simple model with AdminUI enabled should add IAM policy only for fields that have explicit IAM auth 1`] = ` | ||
"## [Start] Authorization Steps. ** | ||
$util.qr($ctx.stash.put(\\"hasAuth\\", true)) | ||
#set( $inputFields = $util.parseJson($util.toJson($ctx.args.input.keySet())) ) | ||
#set( $isAuthorized = false ) | ||
#set( $allowedFields = [] ) | ||
#if( $util.authType() == \\"IAM Authorization\\" ) | ||
#set( $adminRoles = [\\"us-fake-1_uuid_Full-access/CognitoIdentityCredentials\\",\\"us-fake-1_uuid_Manage-only/CognitoIdentityCredentials\\"] ) | ||
#foreach( $adminRole in $adminRoles ) | ||
#if( $ctx.identity.userArn.contains($adminRole) ) | ||
#return($util.toJson({})) | ||
#end | ||
#end | ||
$util.unauthorized() | ||
#end | ||
#if( $util.authType() == \\"User Pool Authorization\\" ) | ||
#end | ||
#if( !$isAuthorized && $allowedFields.isEmpty() ) | ||
$util.unauthorized() | ||
#end | ||
#if( !$isAuthorized ) | ||
#set( $deniedFields = $util.list.copyAndRemoveAll($inputFields, $allowedFields) ) | ||
#if( $deniedFields.size() > 0 ) | ||
$util.error(\\"Unauthorized on \${deniedFields}\\", \\"Unauthorized\\") | ||
#end | ||
#end | ||
$util.toJson({\\"version\\":\\"2018-05-29\\",\\"payload\\":{}}) | ||
## [End] Authorization Steps. **" | ||
`; | ||
|
||
exports[`Test simple model with AdminUI enabled should add IAM policy only for fields that have explicit IAM auth 2`] = ` | ||
"## [Start] Authorization Steps. ** | ||
$util.qr($ctx.stash.put(\\"hasAuth\\", true)) | ||
#if( $ctx.error ) | ||
$util.error($ctx.error.message, $ctx.error.type) | ||
#end | ||
#set( $inputFields = $util.parseJson($util.toJson($ctx.args.input.keySet())) ) | ||
#set( $isAuthorized = false ) | ||
#set( $allowedFields = [] ) | ||
#set( $nullAllowedFields = [] ) | ||
#set( $deniedFields = {} ) | ||
#if( $util.authType() == \\"IAM Authorization\\" ) | ||
#set( $adminRoles = [\\"us-fake-1_uuid_Full-access/CognitoIdentityCredentials\\",\\"us-fake-1_uuid_Manage-only/CognitoIdentityCredentials\\"] ) | ||
#foreach( $adminRole in $adminRoles ) | ||
#if( $ctx.identity.userArn.contains($adminRole) ) | ||
#return($util.toJson({})) | ||
#end | ||
#end | ||
$util.unauthorized() | ||
#end | ||
#if( $util.authType() == \\"User Pool Authorization\\" ) | ||
#end | ||
#if( !$isAuthorized && $allowedFields.isEmpty() && $nullAllowedFields.isEmpty() ) | ||
$util.unauthorized() | ||
#end | ||
#if( !$isAuthorized ) | ||
#foreach( $entry in $util.map.copyAndRetainAllKeys($ctx.args.input, $inputFields).entrySet() ) | ||
#if( $util.isNull($entry.value) && !$nullAllowedFields.contains($entry.key) ) | ||
$util.qr($deniedFields.put($entry.key, \\"\\")) | ||
#end | ||
#end | ||
#foreach( $deniedField in $util.list.copyAndRemoveAll($inputFields, $allowedFields) ) | ||
$util.qr($deniedFields.put($deniedField, \\"\\")) | ||
#end | ||
#end | ||
#if( $deniedFields.keySet().size() > 0 ) | ||
$util.error(\\"Unauthorized on \${deniedFields.keySet()}\\", \\"Unauthorized\\") | ||
#end | ||
$util.toJson({}) | ||
## [End] Authorization Steps. **" | ||
`; | ||
|
||
exports[`Test simple model with AdminUI enabled should add IAM policy only for fields that have explicit IAM auth 3`] = ` | ||
"## [Start] Authorization Steps. ** | ||
$util.qr($ctx.stash.put(\\"hasAuth\\", true)) | ||
#set( $isAuthorized = false ) | ||
#if( $util.authType() == \\"IAM Authorization\\" ) | ||
#set( $adminRoles = [\\"us-fake-1_uuid_Full-access/CognitoIdentityCredentials\\",\\"us-fake-1_uuid_Manage-only/CognitoIdentityCredentials\\"] ) | ||
#foreach( $adminRole in $adminRoles ) | ||
#if( $ctx.identity.userArn.contains($adminRole) ) | ||
#return($util.toJson({})) | ||
#end | ||
#end | ||
$util.unauthorized() | ||
#end | ||
#if( $util.authType() == \\"User Pool Authorization\\" ) | ||
#end | ||
#if( !$isAuthorized ) | ||
$util.unauthorized() | ||
#end | ||
$util.toJson({\\"version\\":\\"2018-05-29\\",\\"payload\\":{}}) | ||
## [End] Authorization Steps. **" | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.