in packages/graphql-auth-transformer/src/ModelAuthTransformer.ts [1475:1625]
private protectUpdateOrDeleteMutation(
ctx: TransformerContext,
resolverResourceId: string,
rules: AuthRule[],
parent: ObjectTypeDefinitionNode,
modelConfiguration: ModelDirectiveConfiguration,
isUpdate: boolean,
field?: FieldDefinitionNode,
ifCondition?: Expression,
subscriptionOperation?: ModelDirectiveOperationType,
) {
const resolver = ctx.getResource(resolverResourceId);
if (!rules || rules.length === 0 || !resolver) {
return;
} else {
const mutationTypeName = ctx.getMutationTypeName();
if (modelConfiguration.shouldHave(isUpdate ? 'update' : 'delete')) {
const operationName = modelConfiguration.getName(isUpdate ? 'update' : 'delete');
// If the parent type has any rules for this operation AND
// the default provider we've to get directives including the default
// as well.
const includeDefault = Boolean(!field && this.doesTypeHaveRulesForOperation(parent, isUpdate ? 'update' : 'delete'));
const operationDirectives = this.getDirectivesForRules(rules, includeDefault);
if (operationDirectives.length > 0) {
this.addDirectivesToOperation(ctx, mutationTypeName, operationName, operationDirectives);
}
this.addFieldToResourceReferences(mutationTypeName, operationName, rules);
}
// Break the rules out by strategy.
const staticGroupAuthorizationRules = this.getStaticGroupRules(rules);
const dynamicGroupAuthorizationRules = this.getDynamicGroupRules(rules);
const ownerAuthorizationRules = this.getOwnerRules(rules);
const providerAuthorization = this.hasProviderAuthRules(rules);
if (
(staticGroupAuthorizationRules.length > 0 || dynamicGroupAuthorizationRules.length > 0 || ownerAuthorizationRules.length > 0) &&
providerAuthorization === false
) {
// Generate the expressions to validate each strategy.
const staticGroupAuthorizationExpression = this.resources.staticGroupAuthorizationExpression(staticGroupAuthorizationRules, field);
const fieldIsList = (fieldName: string) => {
const field = parent.fields.find(field => field.name.value === fieldName);
if (field) {
return isListType(field.type);
}
return false;
};
// In create mutations, the dynamic group and ownership authorization checks
// are done before calling PutItem.
const dynamicGroupAuthorizationExpression = this.resources.dynamicGroupAuthorizationExpressionForUpdateOrDeleteOperations(
dynamicGroupAuthorizationRules,
fieldIsList,
field ? field.name.value : undefined,
);
const ownerAuthorizationExpression = this.resources.ownerAuthorizationExpressionForUpdateOrDeleteOperations(
ownerAuthorizationRules,
fieldIsList,
field ? field.name.value : undefined,
);
const collectAuthCondition = this.resources.collectAuthCondition();
const staticGroupAuthorizedVariable = this.resources.getStaticAuthorizationVariable(field);
const ifNotStaticallyAuthedCreateAuthCondition = iff(
raw(`! $${staticGroupAuthorizedVariable}`),
compoundExpression([
dynamicGroupAuthorizationExpression,
newline(),
ownerAuthorizationExpression,
newline(),
collectAuthCondition,
]),
);
const throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty =
this.resources.throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty(field);
// If we've any modes to check, then add the authMode check code block
// to the start of the resolver.
const authModesToCheck = new Set<AuthProvider>();
const expressions: Array<Expression> = new Array();
if (
ownerAuthorizationRules.find(r => r.provider === 'userPools') ||
staticGroupAuthorizationRules.find(r => r.provider === 'userPools') ||
dynamicGroupAuthorizationRules.find(r => r.provider === 'userPools')
) {
authModesToCheck.add('userPools');
}
if (
ownerAuthorizationRules.find(r => r.provider === 'oidc') ||
staticGroupAuthorizationRules.find(r => r.provider === 'oidc') ||
dynamicGroupAuthorizationRules.find(r => r.provider === 'oidc')
) {
authModesToCheck.add('oidc');
}
if (authModesToCheck.size > 0) {
const isUserPoolTheDefault = this.configuredAuthProviders.default === 'userPools';
expressions.push(this.resources.getAuthModeDeterminationExpression(authModesToCheck, isUserPoolTheDefault));
}
// These statements will be wrapped into an authMode check if statement
const authorizationLogic = compoundExpression([
staticGroupAuthorizationExpression,
newline(),
ifNotStaticallyAuthedCreateAuthCondition,
newline(),
throwIfNotStaticGroupAuthorizedOrAuthConditionIsEmpty,
]);
// Create the authMode if block and add it to the resolver
expressions.push(this.resources.getAuthModeCheckWrappedExpression(authModesToCheck, authorizationLogic));
const templateParts = [
print(field && ifCondition ? iff(ifCondition, compoundExpression(expressions)) : compoundExpression(expressions)),
resolver.Properties.RequestMappingTemplate,
];
resolver.Properties.RequestMappingTemplate = templateParts.join('\n\n');
ctx.setResource(resolverResourceId, resolver);
}
// if protect is for field and there is a subscription for update / delete then protect the field in that operation
if (
field &&
subscriptionOperation &&
modelConfiguration.shouldHave(subscriptionOperation) &&
(modelConfiguration.getName('level') as ModelSubscriptionLevel) === 'on'
) {
let mutationResolver = resolver;
let mutationResolverResourceID = resolverResourceId;
// if we are protecting delete then we need to get the delete resolver
if (subscriptionOperation === 'onDelete') {
mutationResolverResourceID = ResolverResourceIDs.DynamoDBDeleteResolverResourceID(parent.name.value);
mutationResolver = ctx.getResource(mutationResolverResourceID);
}
const getTemplateParts = [mutationResolver.Properties.ResponseMappingTemplate];
if (!this.isOperationExpressionSet(mutationTypeName, mutationResolver.Properties.ResponseMappingTemplate)) {
getTemplateParts.unshift(this.resources.setOperationExpression(mutationTypeName));
}
mutationResolver.Properties.ResponseMappingTemplate = getTemplateParts.join('\n\n');
ctx.setResource(mutationResolverResourceID, mutationResolver);
}
}
}