private protectUpdateOrDeleteMutation()

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);
      }
    }
  }