"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthTransformer = void 0;
const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core");
const graphql_transformer_interfaces_1 = require("@aws-amplify/graphql-transformer-interfaces");
const graphql_1 = require("graphql");
const lodash_1 = require("lodash");
const graphql_model_transformer_1 = require("@aws-amplify/graphql-model-transformer");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const iam = __importStar(require("aws-cdk-lib/aws-iam"));
const cdk = __importStar(require("aws-cdk-lib"));
const graphql_relational_transformer_1 = require("@aws-amplify/graphql-relational-transformer");
const accesscontrol_1 = require("./accesscontrol");
const utils_1 = require("./utils");
const warnings_1 = require("./utils/warnings");
const ddb_vtl_generator_1 = require("./vtl-generator/ddb/ddb-vtl-generator");
const rds_vtl_generator_1 = require("./vtl-generator/rds/rds-vtl-generator");
const getReadRolesForField = (acm, readRoles, fieldName) => {
    const hasCognitoPrivateRole = readRoles.some((r) => r === 'userPools:private') &&
        acm.isAllowed('userPools:private', fieldName, 'get') &&
        acm.isAllowed('userPools:private', fieldName, 'list') &&
        acm.isAllowed('userPools:private', fieldName, 'sync') &&
        acm.isAllowed('userPools:private', fieldName, 'search') &&
        acm.isAllowed('userPools:private', fieldName, 'listen');
    const hasOIDCPrivateRole = readRoles.some((r) => r === 'oidc:private') &&
        acm.isAllowed('oidc:private', fieldName, 'get') &&
        acm.isAllowed('oidc:private', fieldName, 'list') &&
        acm.isAllowed('oidc:private', fieldName, 'sync') &&
        acm.isAllowed('oidc:private', fieldName, 'search') &&
        acm.isAllowed('oidc:private', fieldName, 'listen');
    let allowedRoles = [...readRoles];
    if (hasCognitoPrivateRole) {
        allowedRoles = allowedRoles.filter((r) => !(r.startsWith('userPools:') && r !== 'userPools:private'));
    }
    if (hasOIDCPrivateRole) {
        allowedRoles = allowedRoles.filter((r) => !(r.startsWith('oidc:') && r !== 'oidc:private'));
    }
    return allowedRoles;
};
class AuthTransformer extends graphql_transformer_core_1.TransformerAuthBase {
    constructor() {
        super('amplify-auth-transformer', utils_1.authDirectiveDefinition);
        this.authPolicyResources = new Set();
        this.unauthPolicyResources = new Set();
        this.before = (context) => {
            this.configuredAuthProviders = (0, utils_1.getConfiguredAuthProviders)(context);
        };
        this.object = (def, directive, context) => {
            var _a;
            const modelDirective = (_a = def.directives) === null || _a === void 0 ? void 0 : _a.find((dir) => dir.name.value === 'model');
            if (!modelDirective) {
                throw new graphql_transformer_core_1.TransformerContractError('Types annotated with @auth must also be annotated with @model.');
            }
            const typeName = def.name.value;
            let isJoinType = false;
            if (context.metadata.has('joinTypeList')) {
                isJoinType = context.metadata.get('joinTypeList').includes(typeName);
            }
            const isSqlDataSource = (0, graphql_transformer_core_1.isModelType)(context, typeName) && (0, graphql_transformer_core_1.isSqlModel)(context, typeName);
            const getAuthRulesOptions = (0, lodash_1.merge)({ isField: false, isSqlDataSource }, (0, graphql_transformer_core_1.generateGetArgumentsInput)(context.transformParameters));
            this.rules = (0, utils_1.getAuthDirectiveRules)(new graphql_transformer_core_1.DirectiveWrapper(directive), getAuthRulesOptions);
            (0, utils_1.validateRules)(this.rules, this.configuredAuthProviders, def.name.value, context);
            const acm = new accesscontrol_1.AccessControlMatrix({
                name: def.name.value,
                operations: utils_1.MODEL_OPERATIONS,
                resources: (0, utils_1.collectFieldNames)(def),
            });
            this.setAuthPolicyFlag(this.rules);
            this.setUnauthPolicyFlag(this.rules);
            this.addTypeToResourceReferences(def.name.value, this.rules);
            this.convertRulesToRoles(acm, this.rules, isJoinType, undefined, undefined, context);
            this.modelDirectiveConfig.set(typeName, (0, utils_1.getModelConfig)(modelDirective, typeName, context.transformParameters, context.isProjectUsingDataStore()));
            this.authModelConfig.set(typeName, acm);
        };
        this.after = (context) => {
            const claimWarning = (0, warnings_1.defaultIdentityClaimWarning)(context, this.rules);
            if (claimWarning) {
                this.warn(claimWarning);
            }
            const reassignWarning = (0, warnings_1.ownerCanReassignWarning)(this.authModelConfig);
            if (reassignWarning) {
                this.warn(reassignWarning.message);
            }
            const iamProviderWarning = (0, warnings_1.deprecatedIAMProviderWarning)(this.rules);
            if (iamProviderWarning) {
                this.warn(iamProviderWarning);
            }
        };
        this.field = (parent, field, directive, context) => {
            var _a;
            if (parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
                throw new graphql_transformer_core_1.InvalidDirectiveError(`The @auth directive cannot be placed on an interface's field. See ${parent.name.value}${field.name.value}`);
            }
            const isParentTypeBuiltinType = parent.name.value === context.output.getQueryTypeName() ||
                parent.name.value === context.output.getMutationTypeName() ||
                parent.name.value === context.output.getSubscriptionTypeName();
            if (isParentTypeBuiltinType) {
                console.warn('Be careful when using @auth directives on a field in a root type. @auth directives on field definitions use the source ' +
                    'object to perform authorization logic and the source will be an empty object for fields on root types. ' +
                    'Static group authorization should perform as expected.');
            }
            const modelDirective = (_a = parent.directives) === null || _a === void 0 ? void 0 : _a.find((dir) => dir.name.value === 'model');
            const typeName = parent.name.value;
            const fieldName = field.name.value;
            const isSqlDataSource = (0, graphql_transformer_core_1.isModelType)(context, parent.name.value) && (0, graphql_transformer_core_1.isSqlModel)(context, parent.name.value);
            const getAuthRulesOptions = (0, lodash_1.merge)({ isField: true, isSqlDataSource }, (0, graphql_transformer_core_1.generateGetArgumentsInput)(context.transformParameters));
            const rules = (0, utils_1.getAuthDirectiveRules)(new graphql_transformer_core_1.DirectiveWrapper(directive), getAuthRulesOptions);
            (0, utils_1.validateFieldRules)(new graphql_transformer_core_1.DirectiveWrapper(directive), isParentTypeBuiltinType, modelDirective !== undefined, field.name.value, context.transformParameters, parent, context);
            (0, utils_1.validateRules)(rules, this.configuredAuthProviders, field.name.value, context);
            this.setAuthPolicyFlag(rules);
            this.setUnauthPolicyFlag(rules);
            this.addFieldToResourceReferences(parent.name.value, field.name.value, rules);
            if (modelDirective) {
                let acm;
                if (!this.modelDirectiveConfig.has(typeName)) {
                    this.modelDirectiveConfig.set(typeName, (0, utils_1.getModelConfig)(modelDirective, typeName, context.transformParameters, context.isProjectUsingDataStore()));
                    acm = new accesscontrol_1.AccessControlMatrix({
                        name: parent.name.value,
                        operations: utils_1.MODEL_OPERATIONS,
                        resources: (0, utils_1.collectFieldNames)(parent),
                    });
                }
                else {
                    acm = this.authModelConfig.get(typeName);
                    acm.resetAccessForResource(fieldName);
                }
                this.convertRulesToRoles(acm, rules, false, fieldName, undefined, context);
                this.authModelConfig.set(typeName, acm);
            }
            else {
                const staticRules = rules.filter((rule) => rule.allow !== 'owner' && !rule.groupsField);
                const typeFieldName = `${typeName}:${fieldName}`;
                const acm = new accesscontrol_1.AccessControlMatrix({
                    name: typeFieldName,
                    operations: ['list', 'get', 'search', 'listen', 'sync'],
                    resources: [typeFieldName],
                });
                this.convertRulesToRoles(acm, staticRules, false, typeFieldName, ['list', 'get', 'search', 'listen', 'sync']);
                this.authNonModelConfig.set(typeFieldName, acm);
            }
        };
        this.transformSchema = (context) => {
            const searchableAggregateServiceDirectives = new Set();
            const getOwnerFields = (acm) => acm.getRoles().reduce((prev, role) => {
                if (this.roleMap.get(role).strategy === 'owner')
                    prev.push(this.roleMap.get(role).entity);
                return prev;
            }, []);
            this.removeAuthFieldsFromSubscriptionFilter(context);
            this.authModelConfig.forEach((acm, modelName) => {
                const def = context.output.getObject(modelName);
                const modelHasSearchable = def.directives.some((dir) => dir.name.value === 'searchable');
                const ownerFields = getOwnerFields(acm);
                if ((0, graphql_transformer_core_1.isDynamoDbModel)(context, modelName)) {
                    const filterInput = context.output.getInput((0, graphql_transformer_core_1.getFilterInputName)(modelName));
                    if (filterInput) {
                        const updatedFilterInput = { ...filterInput, fields: [...filterInput.fields] };
                        ownerFields.forEach((ownerField) => {
                            if (!filterInput.fields.some((field) => field.name.value === ownerField)) {
                                updatedFilterInput.fields.push((0, graphql_transformer_common_1.makeInputValueDefinition)(ownerField, (0, graphql_transformer_common_1.makeNamedType)('ModelStringInput')));
                            }
                        });
                        context.output.updateInput(updatedFilterInput);
                    }
                    const conditionInput = context.output.getInput((0, graphql_transformer_core_1.getConditionInputName)(modelName));
                    if (conditionInput) {
                        const updatedConditionInput = { ...conditionInput, fields: [...conditionInput.fields] };
                        ownerFields.forEach((ownerField) => {
                            if (!conditionInput.fields.some((field) => field.name.value === ownerField)) {
                                updatedConditionInput.fields.push((0, graphql_transformer_common_1.makeInputValueDefinition)(ownerField, (0, graphql_transformer_common_1.makeNamedType)('ModelStringInput')));
                            }
                        });
                        context.output.updateInput(updatedConditionInput);
                    }
                    const subscriptionFilterInput = context.output.getInput((0, graphql_transformer_core_1.getSubscriptionFilterInputName)(modelName));
                    if (subscriptionFilterInput) {
                        const updatedSubscriptionFilterInput = { ...subscriptionFilterInput, fields: [...subscriptionFilterInput.fields] };
                        ownerFields.forEach((ownerField) => {
                            if (!subscriptionFilterInput.fields.some((field) => field.name.value === ownerField)) {
                                updatedSubscriptionFilterInput.fields.push((0, graphql_transformer_common_1.makeInputValueDefinition)(ownerField, (0, graphql_transformer_common_1.makeNamedType)('ModelStringInput')));
                            }
                        });
                        context.output.updateInput(updatedSubscriptionFilterInput);
                    }
                }
                this.addFieldsToObject(context, modelName, ownerFields);
                const providers = this.getAuthProviders(acm.getRoles());
                const addDefaultIfNeeded = providers.length === 0 ? this.configuredAuthProviders.shouldAddDefaultServiceDirective : false;
                const directives = this.getServiceDirectives(providers, addDefaultIfNeeded);
                if (modelHasSearchable) {
                    providers.forEach((p) => searchableAggregateServiceDirectives.add(p));
                }
                if (directives.length > 0) {
                    (0, utils_1.extendTypeWithDirectives)(context, modelName, directives);
                }
                this.protectSchemaOperations(context, def, acm);
                this.propagateAuthDirectivesToNestedTypes(context, context.output.getObject(modelName), providers);
            });
            this.authNonModelConfig.forEach((acm, typeFieldName) => {
                const [typeName, fieldName] = typeFieldName.split(':');
                const providers = this.getAuthProviders(acm.getRoles());
                const directives = this.getServiceDirectives(providers, false);
                if (directives.length > 0) {
                    (0, utils_1.addDirectivesToField)(context, typeName, fieldName, directives);
                }
            });
            if (searchableAggregateServiceDirectives.size > 0) {
                const serviceDirectives = this.getServiceDirectives(Array.from(searchableAggregateServiceDirectives), false);
                utils_1.SEARCHABLE_AGGREGATE_TYPES.forEach((aggType) => {
                    (0, utils_1.extendTypeWithDirectives)(context, aggType, serviceDirectives);
                });
            }
        };
        this.generateResolvers = (context) => {
            this.generateIAMPolicies(context);
            this.authModelConfig.forEach((acm, modelName) => {
                var _a, _b, _c, _d;
                const indexKeyName = `${modelName}:indicies`;
                const def = context.output.getObject(modelName);
                const modelNameConfig = this.modelDirectiveConfig.get(modelName);
                const searchableDirective = def.directives.find((dir) => dir.name.value === 'searchable');
                const queryFields = (0, utils_1.getQueryFieldNames)(this.modelDirectiveConfig.get(modelName));
                queryFields.forEach((query) => {
                    switch (query.type) {
                        case graphql_transformer_interfaces_1.QueryFieldType.GET:
                            this.protectGetResolver(context, def, query.typeName, query.fieldName, acm);
                            break;
                        case graphql_transformer_interfaces_1.QueryFieldType.LIST:
                            this.protectListResolver(context, def, query.typeName, query.fieldName, acm);
                            break;
                        case graphql_transformer_interfaces_1.QueryFieldType.SYNC:
                            this.protectSyncResolver(context, def, query.typeName, query.fieldName, acm);
                            break;
                        default:
                            throw new graphql_transformer_core_1.TransformerContractError('Unknown query field type');
                    }
                });
                if (context.metadata.has(indexKeyName)) {
                    context.metadata.get(indexKeyName).forEach((index) => {
                        const [indexName, indexQueryName] = index.split(':');
                        this.protectListResolver(context, def, 'Query', indexQueryName, acm, indexName);
                    });
                }
                if (searchableDirective) {
                    const config = (0, utils_1.getSearchableConfig)(searchableDirective, modelName, context.transformParameters);
                    this.protectSearchResolver(context, def, context.output.getQueryTypeName(), config.queries.search, acm);
                }
                const readRoles = [...new Set(...utils_1.READ_MODEL_OPERATIONS.map((op) => acm.getRolesPerOperation(op)))];
                const modelFields = (_b = (_a = def.fields) === null || _a === void 0 ? void 0 : _a.filter((f) => acm.hasResource(f.name.value))) !== null && _b !== void 0 ? _b : [];
                const errorFields = new Array();
                modelFields.forEach((field) => {
                    const fieldReadRoles = getReadRolesForField(acm, readRoles, field.name.value);
                    const allowedRoles = fieldReadRoles.filter((r) => utils_1.READ_MODEL_OPERATIONS.some((op) => acm.isAllowed(r, field.name.value, op)));
                    const needsFieldResolver = allowedRoles.length < fieldReadRoles.length;
                    if (needsFieldResolver && field.type.kind === graphql_1.Kind.NON_NULL_TYPE) {
                        errorFields.push(field.name.value);
                    }
                    if ((0, utils_1.hasRelationalDirective)(field)) {
                        this.protectRelationalResolver(context, def, modelName, field, needsFieldResolver ? allowedRoles : null);
                    }
                    else if (needsFieldResolver) {
                        this.protectFieldResolver(context, def, modelName, field.name.value, allowedRoles);
                    }
                });
                const subscriptionLevel = (_d = (_c = modelNameConfig.subscriptions) === null || _c === void 0 ? void 0 : _c.level) !== null && _d !== void 0 ? _d : graphql_model_transformer_1.SubscriptionLevel.on;
                if (errorFields.length > 0 && subscriptionLevel === graphql_model_transformer_1.SubscriptionLevel.on) {
                    throw new graphql_transformer_core_1.InvalidDirectiveError("When using field-level authorization rules you need to add rules to all of the model's required fields with at least read permissions. " +
                        `Found model "${def.name.value}" with required fields ${JSON.stringify(errorFields)} missing field-level authorization rules.\n\n` +
                        'For more information visit https://docs.amplify.aws/cli/graphql/authorization-rules/#field-level-authorization-rules');
                }
                const mutationFields = (0, utils_1.getMutationFieldNames)(this.modelDirectiveConfig.get(modelName));
                mutationFields.forEach((mutation) => {
                    switch (mutation.type) {
                        case graphql_transformer_interfaces_1.MutationFieldType.CREATE:
                            this.protectCreateResolver(context, def, mutation.typeName, mutation.fieldName, acm);
                            break;
                        case graphql_transformer_interfaces_1.MutationFieldType.UPDATE:
                            this.protectUpdateResolver(context, def, mutation.typeName, mutation.fieldName, acm);
                            break;
                        case graphql_transformer_interfaces_1.MutationFieldType.DELETE:
                            this.protectDeleteResolver(context, def, mutation.typeName, mutation.fieldName, acm);
                            break;
                        default:
                            throw new graphql_transformer_core_1.TransformerContractError('Unknown Mutation field type');
                    }
                });
                const subscriptionFieldNames = (0, utils_1.getSubscriptionFieldNames)(this.modelDirectiveConfig.get(modelName));
                const subscriptionRoles = acm.getRolesPerOperation('listen').map((role) => this.roleMap.get(role));
                subscriptionFieldNames.forEach((subscription) => {
                    this.protectSubscriptionResolver(context, subscription.typeName, subscription.fieldName, subscriptionRoles, def);
                });
                if (context.transformParameters.useSubUsernameForDefaultIdentityClaim) {
                    const roleDefinitions = acm.getRoles().map((role) => this.roleMap.get(role));
                    roleDefinitions.forEach((role) => {
                        var _a, _b;
                        const hasMultiClaims = ((_b = (_a = role.claim) === null || _a === void 0 ? void 0 : _a.split(utils_1.IDENTITY_CLAIM_DELIMITER)) === null || _b === void 0 ? void 0 : _b.length) > 1;
                        const createOwnerFieldResolver = role.strategy === 'owner' && hasMultiClaims;
                        if (createOwnerFieldResolver) {
                            this.addFieldResolverForDynamicAuth(context, def, modelName, role.entity);
                        }
                    });
                }
            });
            this.authNonModelConfig.forEach((acm, typeFieldName) => {
                const [typeName, fieldName] = typeFieldName.split(':');
                const def = context.output.getObject(typeName);
                this.protectFieldResolver(context, def, typeName, fieldName, acm.getRoles());
            });
        };
        this.removeAuthFieldsFromSubscriptionFilter = (context) => {
            this.authModelConfig.forEach((acm, modelName) => {
                acm
                    .getRoles()
                    .map((role) => this.roleMap.get(role))
                    .forEach((role) => {
                    if (!role.static && ((0, utils_1.isAuthProviderEqual)(role.provider, 'userPools') || (0, utils_1.isAuthProviderEqual)(role.provider, 'oidc'))) {
                        (0, graphql_model_transformer_1.removeSubscriptionFilterInputAttribute)(context, modelName, role.entity);
                    }
                });
            });
        };
        this.addFieldResolverForDynamicAuth = (ctx, def, typeName, fieldName) => {
            let resolver = ctx.resolvers.getResolver(typeName, fieldName);
            if (resolver) {
                resolver.addToSlot('finish', undefined, graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(this.getVtlGenerator(ctx, def.name.value).generateFieldResolverForOwner(fieldName), `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`));
            }
            else {
                const hasModelDirective = def.directives.some((dir) => dir.name.value === 'model');
                const scope = (0, utils_1.getScopeForField)(ctx, def, fieldName, hasModelDirective);
                resolver = ctx.resolvers.addResolver(typeName, fieldName, new graphql_transformer_core_1.TransformerResolver(typeName, fieldName, graphql_transformer_common_1.ResolverResourceIDs.ResolverResourceID(typeName, fieldName), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString('$util.toJson({"version":"2018-05-29","payload":{}})', `${typeName}.${fieldName}.req.vtl`), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(this.getVtlGenerator(ctx, def.name.value).generateFieldResolverForOwner(fieldName), `${typeName}.${fieldName}.res.vtl`), ['init'], ['finish']));
                resolver.setScope(scope);
            }
        };
        this.protectSchemaOperations = (ctx, def, acm) => {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
            const modelConfig = this.modelDirectiveConfig.get(def.name.value);
            const indexKeyName = `${def.name.value}:indicies`;
            const searchableDirective = def.directives.find((dir) => dir.name.value === 'searchable');
            const addServiceDirective = (typeName, operation, operationName = null) => {
                if (operationName) {
                    const includeDefault = this.doesTypeHaveRulesForOperation(acm, operation);
                    const providers = this.getAuthProviders(acm.getRolesPerOperation(operation, operation === 'delete'));
                    const operationDirectives = this.getServiceDirectives(providers, includeDefault);
                    if (operationDirectives.length > 0) {
                        (0, utils_1.addDirectivesToOperation)(ctx, typeName, operationName, operationDirectives);
                    }
                    this.addOperationToResourceReferences(typeName, operationName, acm.getRoles());
                }
            };
            addServiceDirective(ctx.output.getQueryTypeName(), 'get', (_a = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.queries) === null || _a === void 0 ? void 0 : _a.get);
            addServiceDirective(ctx.output.getQueryTypeName(), 'list', (_b = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.queries) === null || _b === void 0 ? void 0 : _b.list);
            addServiceDirective(ctx.output.getQueryTypeName(), 'sync', (_c = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.queries) === null || _c === void 0 ? void 0 : _c.sync);
            addServiceDirective(ctx.output.getMutationTypeName(), 'create', (_d = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.mutations) === null || _d === void 0 ? void 0 : _d.create);
            addServiceDirective(ctx.output.getMutationTypeName(), 'update', (_e = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.mutations) === null || _e === void 0 ? void 0 : _e.update);
            addServiceDirective(ctx.output.getMutationTypeName(), 'delete', (_f = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.mutations) === null || _f === void 0 ? void 0 : _f.delete);
            if (ctx.metadata.has(indexKeyName)) {
                ctx.metadata.get(indexKeyName).forEach((index) => {
                    addServiceDirective(ctx.output.getQueryTypeName(), 'list', index.split(':')[1]);
                    addServiceDirective(ctx.output.getQueryTypeName(), 'get', index.split(':')[1]);
                });
            }
            if (searchableDirective) {
                const config = (0, utils_1.getSearchableConfig)(searchableDirective, def.name.value, ctx.transformParameters);
                addServiceDirective(ctx.output.getQueryTypeName(), 'search', config.queries.search);
            }
            const subscriptions = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.subscriptions;
            const subscriptionLevel = (_g = subscriptions === null || subscriptions === void 0 ? void 0 : subscriptions.level) !== null && _g !== void 0 ? _g : graphql_model_transformer_1.SubscriptionLevel.on;
            if (subscriptionLevel === graphql_model_transformer_1.SubscriptionLevel.on) {
                const subscriptionArguments = acm
                    .getRolesPerOperation('listen')
                    .map((role) => this.roleMap.get(role))
                    .filter((roleDef) => { var _a; return roleDef.strategy === 'owner' && !(0, utils_1.fieldIsList)((_a = def.fields) !== null && _a !== void 0 ? _a : [], roleDef.entity); });
                if ((subscriptions === null || subscriptions === void 0 ? void 0 : subscriptions.onCreate) && ((_h = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.mutations) === null || _h === void 0 ? void 0 : _h.create)) {
                    subscriptions.onCreate.forEach((onCreateSub) => {
                        addServiceDirective(ctx.output.getSubscriptionTypeName(), 'listen', onCreateSub);
                        (0, utils_1.addSubscriptionArguments)(ctx, onCreateSub, subscriptionArguments);
                    });
                }
                if ((subscriptions === null || subscriptions === void 0 ? void 0 : subscriptions.onUpdate) && ((_j = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.mutations) === null || _j === void 0 ? void 0 : _j.update)) {
                    subscriptions.onUpdate.forEach((onUpdateSub) => {
                        addServiceDirective(ctx.output.getSubscriptionTypeName(), 'listen', onUpdateSub);
                        (0, utils_1.addSubscriptionArguments)(ctx, onUpdateSub, subscriptionArguments);
                    });
                }
                if ((subscriptions === null || subscriptions === void 0 ? void 0 : subscriptions.onDelete) && ((_k = modelConfig === null || modelConfig === void 0 ? void 0 : modelConfig.mutations) === null || _k === void 0 ? void 0 : _k.delete)) {
                    subscriptions.onDelete.forEach((onDeleteSub) => {
                        addServiceDirective(ctx.output.getSubscriptionTypeName(), 'listen', onDeleteSub);
                        (0, utils_1.addSubscriptionArguments)(ctx, onDeleteSub, subscriptionArguments);
                    });
                }
            }
        };
        this.protectGetResolver = (ctx, def, typeName, fieldName, acm) => {
            var _a;
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const roleDefinitions = acm.getRolesPerOperation('get').map((r) => this.roleMap.get(r));
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForQueries(ctx, this.configuredAuthProviders, roleDefinitions, (_a = def.fields) !== null && _a !== void 0 ? _a : [], def, undefined);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`));
        };
        this.protectListResolver = (ctx, def, typeName, fieldName, acm, indexName) => {
            var _a;
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const roleDefinitions = acm.getRolesPerOperation('list').map((r) => this.roleMap.get(r));
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForQueries(ctx, this.configuredAuthProviders, roleDefinitions, (_a = def.fields) !== null && _a !== void 0 ? _a : [], def, indexName);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`));
        };
        this.protectRelationalResolver = (ctx, def, typeName, field, fieldRoles) => {
            var _a, _b, _c;
            let fieldAuthExpression;
            let relatedAuthExpression;
            const relatedModelObject = this.getRelatedModelObject(ctx, (0, graphql_transformer_common_1.getBaseType)(field.type));
            if (this.authModelConfig.has(relatedModelObject.name.value)) {
                const acm = this.authModelConfig.get(relatedModelObject.name.value);
                const roleDefinitions = [
                    ...new Set([
                        ...acm.getRolesPerOperation('get'),
                        ...acm.getRolesPerOperation('list'),
                        ...acm.getRolesPerOperation('sync'),
                        ...acm.getRolesPerOperation('search'),
                        ...acm.getRolesPerOperation('listen'),
                    ]),
                ].map((r) => this.roleMap.get(r));
                relatedAuthExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForRelationQuery(ctx, def, field, relatedModelObject, this.configuredAuthProviders, roleDefinitions, (_a = relatedModelObject.fields) !== null && _a !== void 0 ? _a : []);
            }
            else {
                relatedAuthExpression = this.getVtlGenerator(ctx, def.name.value).generateSandboxExpressionForField(ctx.transformParameters.sandboxModeEnabled, ctx.synthParameters.enableIamAccess);
            }
            if (fieldRoles) {
                const roleDefinitions = fieldRoles.map((r) => this.roleMap.get(r));
                const hasSubsEnabled = ((_b = this.modelDirectiveConfig.get(typeName).subscriptions) === null || _b === void 0 ? void 0 : _b.level) === 'on';
                relatedAuthExpression = `${this.getVtlGenerator(ctx, def.name.value).setDeniedFieldFlag('Mutation', hasSubsEnabled)}\n${relatedAuthExpression}`;
                fieldAuthExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForField(this.configuredAuthProviders, roleDefinitions, (_c = def.fields) !== null && _c !== void 0 ? _c : [], undefined);
            }
            const resolver = ctx.resolvers.getResolver(typeName, field.name.value);
            if (fieldAuthExpression) {
                resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${field.name.value}.{slotName}.{slotIndex}.req.vtl`), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(relatedAuthExpression, `${typeName}.${field.name.value}.{slotName}.{slotIndex}.res.vtl`));
            }
            else {
                resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(relatedAuthExpression, `${typeName}.${field.name.value}.{slotName}.{slotIndex}.req.vtl`));
            }
        };
        this.protectSyncResolver = (ctx, def, typeName, fieldName, acm) => {
            var _a;
            if (ctx.isProjectUsingDataStore()) {
                const resolver = ctx.resolvers.getResolver(typeName, fieldName);
                const roleDefinitions = acm.getRolesPerOperation('sync').map((r) => this.roleMap.get(r));
                const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForQueries(ctx, this.configuredAuthProviders, roleDefinitions, (_a = def.fields) !== null && _a !== void 0 ? _a : [], def, undefined);
                resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`));
            }
        };
        this.protectSearchResolver = (ctx, def, typeName, fieldName, acm) => {
            var _a;
            const acmFields = acm.getResources();
            const modelFields = (_a = def.fields) !== null && _a !== void 0 ? _a : [];
            const allowedAggFields = modelFields.map((f) => f.name.value).filter((f) => !acmFields.includes(f));
            let leastAllowedFields = acmFields;
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const readRoleDefinitions = acm.getRolesPerOperation('search').map((role) => {
                const allowedFields = acmFields.filter((resource) => acm.isAllowed(role, resource, 'search'));
                const roleDefinition = this.roleMap.get(role);
                if (allowedFields.length !== acmFields.length || !roleDefinition.static) {
                    roleDefinition.allowedFields = allowedFields;
                    leastAllowedFields = leastAllowedFields.filter((f) => allowedFields.includes(f));
                }
                else {
                    roleDefinition.allowedFields = null;
                }
                return roleDefinition;
            });
            allowedAggFields.push(...leastAllowedFields);
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForSearchQueries(this.configuredAuthProviders, readRoleDefinitions, modelFields, allowedAggFields);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`));
        };
        this.protectFieldResolver = (ctx, def, typeName, fieldName, roles) => {
            var _a, _b;
            const roleDefinitions = roles.map((r) => this.roleMap.get(r));
            const hasModelDirective = def.directives.some((dir) => dir.name.value === 'model');
            const fieldNode = def.fields.find((f) => f.name.value === fieldName);
            const scope = (0, utils_1.getScopeForField)(ctx, def, fieldName, hasModelDirective);
            if (ctx.api.host.hasResolver(typeName, fieldName)) {
                const fieldResolver = ctx.api.host.getResolver(typeName, fieldName);
                const fieldAuthExpression = this.getVtlGenerator(ctx, def.name.value, fieldNode).generateAuthExpressionForField(this.configuredAuthProviders, roleDefinitions, [], fieldName);
                if (!ctx.api.host.hasDataSource(utils_1.NONE_DS)) {
                    ctx.api.host.addNoneDataSource(utils_1.NONE_DS);
                }
                const authFunction = ctx.api.host.addAppSyncFunction(`${(0, graphql_transformer_common_1.toUpper)(typeName)}${(0, graphql_transformer_common_1.toUpper)(fieldName)}AuthFN`, graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.auth.req.vtl`), graphql_transformer_core_1.MappingTemplate.inlineTemplateFromString('$util.toJson({})'), utils_1.NONE_DS, scope);
                fieldResolver.pipelineConfig.functions.unshift(authFunction.functionId);
            }
            else {
                const fieldAuthExpression = this.getVtlGenerator(ctx, def.name.value, fieldNode).generateAuthExpressionForField(this.configuredAuthProviders, roleDefinitions, (_a = def.fields) !== null && _a !== void 0 ? _a : [], fieldName);
                const subsEnabled = hasModelDirective ? ((_b = this.modelDirectiveConfig.get(typeName).subscriptions) === null || _b === void 0 ? void 0 : _b.level) === 'on' : false;
                const fieldResponse = this.getVtlGenerator(ctx, def.name.value, fieldNode).generateFieldAuthResponse('Mutation', fieldName, subsEnabled);
                const existingResolver = ctx.resolvers.hasResolver(typeName, fieldName);
                if (existingResolver) {
                    const resolver = ctx.resolvers.getResolver(typeName, fieldName);
                    resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(fieldResponse, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`));
                    resolver.setScope(scope);
                }
                else {
                    const resolver = ctx.resolvers.addResolver(typeName, fieldName, new graphql_transformer_core_1.TransformerResolver(typeName, fieldName, graphql_transformer_common_1.ResolverResourceIDs.ResolverResourceID(typeName, fieldName), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.req.vtl`), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(fieldResponse, `${typeName}.${fieldName}.res.vtl`), ['init'], ['finish']));
                    resolver.setScope(scope);
                }
            }
        };
        this.protectCreateResolver = (ctx, def, typeName, fieldName, acm) => {
            var _a;
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const fields = acm.getResources();
            const createRoles = acm.getRolesPerOperation('create').map((role) => {
                const roleDefinition = this.roleMap.get(role);
                const allowedFields = fields.filter((resource) => acm.isAllowed(role, resource, 'create'));
                roleDefinition.areAllFieldsAllowed = allowedFields.length === fields.length;
                roleDefinition.allowedFields = this.addAutoGeneratedFields(ctx, def, allowedFields, fields);
                return roleDefinition;
            });
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForCreate(ctx, this.configuredAuthProviders, createRoles, (_a = def.fields) !== null && _a !== void 0 ? _a : []);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`));
        };
        this.protectUpdateResolver = (ctx, def, typeName, fieldName, acm) => {
            var _a;
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const fields = acm.getResources();
            const updateDeleteRoles = [...new Set([...acm.getRolesPerOperation('update'), ...acm.getRolesPerOperation('delete')])];
            const totalRoles = updateDeleteRoles.map((role) => {
                const allowedFields = fields.filter((resource) => acm.isAllowed(role, resource, 'update'));
                const nullAllowedFields = fields.filter((resource) => acm.isAllowed(role, resource, 'delete'));
                const roleDefinition = this.roleMap.get(role);
                roleDefinition.areAllFieldsAllowed = allowedFields.length === fields.length;
                roleDefinition.areAllFieldsNullAllowed = nullAllowedFields.length === fields.length;
                roleDefinition.allowedFields = this.addAutoGeneratedFields(ctx, def, [...allowedFields, (0, graphql_relational_transformer_1.getObjectPrimaryKey)(def).name.value], fields);
                roleDefinition.nullAllowedFields = nullAllowedFields;
                return roleDefinition;
            });
            const dataSourceName = (0, graphql_transformer_core_1.getModelDataSourceNameForTypeName)(ctx, def.name.value);
            const dataSource = ctx.api.host.getDataSource(dataSourceName);
            const requestExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthRequestExpression(ctx, def);
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForUpdate(this.configuredAuthProviders, totalRoles, (_a = def.fields) !== null && _a !== void 0 ? _a : []);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(requestExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`), dataSource);
        };
        this.protectDeleteResolver = (ctx, def, typeName, fieldName, acm) => {
            var _a;
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const fields = acm.getResources();
            const deleteRoleNames = acm.getRolesPerOperation('delete', true);
            const deleteRoles = deleteRoleNames.map((role) => {
                const allowedFields = fields.filter((resource) => acm.isAllowed(role, resource, 'delete'));
                const roleDefinition = this.roleMap.get(role);
                roleDefinition.allowedFields = allowedFields;
                return roleDefinition;
            });
            const dataSourceName = (0, graphql_transformer_core_1.getModelDataSourceNameForTypeName)(ctx, def.name.value);
            const dataSource = ctx.api.host.getDataSource(dataSourceName);
            const requestExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthRequestExpression(ctx, def);
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForDelete(this.configuredAuthProviders, deleteRoles, (_a = def.fields) !== null && _a !== void 0 ? _a : []);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(requestExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`), graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.res.vtl`), dataSource);
        };
        this.protectSubscriptionResolver = (ctx, typeName, fieldName, subscriptionRoles, def) => {
            const resolver = ctx.resolvers.getResolver(typeName, fieldName);
            const authExpression = this.getVtlGenerator(ctx, def.name.value).generateAuthExpressionForSubscriptions(this.configuredAuthProviders, subscriptionRoles);
            resolver.addToSlot('auth', graphql_transformer_core_1.MappingTemplate.s3MappingTemplateFromString(authExpression, `${typeName}.${fieldName}.{slotName}.{slotIndex}.req.vtl`));
        };
        this.getRelatedModelObject = (ctx, typeName) => {
            const modelObjectName = graphql_transformer_common_1.ModelResourceIDs.IsModelConnectionType(typeName)
                ? graphql_transformer_common_1.ModelResourceIDs.GetModelFromConnectionType(typeName)
                : typeName;
            if (!ctx.output.hasType(modelObjectName)) {
                throw new graphql_transformer_core_1.TransformerContractError(`Could not find type: ${modelObjectName}`);
            }
            else {
                return ctx.output.getObject(modelObjectName);
            }
        };
        this.addFieldsToObject = (ctx, modelName, ownerFields) => {
            const modelObject = ctx.output.getObject(modelName);
            const existingFields = (0, utils_1.collectFieldNames)(modelObject);
            const ownerFieldsToAdd = ownerFields.filter((field) => !existingFields.includes(field));
            ownerFieldsToAdd.forEach((ownerField) => {
                const warningField = existingFields.find((field) => field.toLowerCase() === ownerField.toLowerCase());
                if (warningField) {
                    this.warn((0, warnings_1.ownerFieldCaseWarning)(ownerField, warningField, modelName));
                }
                modelObject.fields.push((0, graphql_transformer_common_1.makeField)(ownerField, [], (0, graphql_transformer_common_1.makeNamedType)('String')));
            });
            ctx.output.putType(modelObject);
        };
        this.addAutoGeneratedFields = (ctx, def, allowedFields, fields) => {
            const allowedFieldsSet = new Set(allowedFields);
            this.addAutoGeneratedRelationalFields(ctx, def, allowedFieldsSet, fields);
            this.addAutoGeneratedIndexFields(def, allowedFieldsSet);
            this.addAutoGeneratedDataStoreFields(ctx, allowedFieldsSet);
            return Array.from(allowedFieldsSet);
        };
        this.addAutoGeneratedRelationalFields = (ctx, def, allowedFields, fields) => {
            const typeDefinitions = ctx.inputDocument.definitions.filter((it) => it.kind === 'ObjectTypeDefinition');
            this.addAutoGeneratedHasManyFields(ctx, typeDefinitions, def, allowedFields);
            this.addAutoGeneratedHasOneFields(ctx, typeDefinitions, fields, def, allowedFields);
        };
        this.addAutoGeneratedIndexFields = (definition, allowedFields) => {
            var _a;
            const sortKeyFieldValues = (_a = definition.fields) === null || _a === void 0 ? void 0 : _a.map((it) => it.directives).flat().filter((it) => it.name.value === 'primaryKey' || it.name.value === 'index').map((it) => it.arguments).flat().filter((it) => it.name.value === 'sortKeyFields' && it.value.kind === 'ListValue' && it.value.values.length > 1).map((it) => it.value).flat();
            sortKeyFieldValues.forEach((sortKeyFieldValue) => {
                const accessOnAllKeys = !sortKeyFieldValue.values.some((it) => it.kind !== 'StringValue' || !allowedFields.has(it.value));
                if (accessOnAllKeys) {
                    const keyName = sortKeyFieldValue.values
                        .map((it) => it.value)
                        .join(graphql_transformer_common_1.ModelResourceIDs.ModelCompositeKeySeparator());
                    allowedFields.add(keyName);
                }
            });
        };
        this.addAutoGeneratedHasManyFields = (ctx, typeDefinitions, def, allowedFields) => {
            const hasManyRelatedFields = typeDefinitions
                .map((it) => it.fields.map((field) => ({ ...field, relatedType: it })))
                .flat()
                .filter((it) => { var _a; return (0, graphql_transformer_common_1.getBaseType)(it.type) === def.name.value && ((_a = it.directives) === null || _a === void 0 ? void 0 : _a.some((d) => d.name.value === 'hasMany')); });
            hasManyRelatedFields.forEach((relatedField) => {
                allowedFields.add((0, graphql_relational_transformer_1.getConnectionAttributeName)(ctx.transformParameters, relatedField.relatedType.name.value, relatedField.name.value, (0, graphql_relational_transformer_1.getObjectPrimaryKey)(relatedField.relatedType).name.value));
                (0, graphql_transformer_core_1.getSortKeyFieldNames)(relatedField.relatedType).forEach((sortKeyFieldName) => {
                    allowedFields.add((0, graphql_relational_transformer_1.getSortKeyConnectionAttributeName)(relatedField.relatedType.name.value, relatedField.name.value, sortKeyFieldName));
                });
            });
        };
        this.addAutoGeneratedHasOneFields = (ctx, typeDefinitions, fields, def, allowedFields) => {
            fields.forEach((field) => {
                var _a;
                const modelField = def.fields.find((it) => it.name.value === field);
                const directives = (_a = modelField.directives) === null || _a === void 0 ? void 0 : _a.filter((dir) => { var _a; return !((_a = dir.arguments) === null || _a === void 0 ? void 0 : _a.some((it) => it.name.value === 'fields')) && (dir.name.value === 'hasOne' || dir.name.value === 'belongsTo'); });
                directives.forEach((directive) => {
                    const relatedType = typeDefinitions.find((it) => it.name.value === (0, graphql_transformer_common_1.getBaseType)(modelField.type));
                    if (directive.name.value === 'hasOne' ||
                        (directive.name.value === 'belongsTo' &&
                            relatedType.fields.some((f) => { var _a; return (0, graphql_transformer_common_1.getBaseType)(f.type) === def.name.value && ((_a = f.directives) === null || _a === void 0 ? void 0 : _a.some((d) => d.name.value === 'hasOne')); }))) {
                        if (!(0, graphql_transformer_core_1.isSqlModel)(ctx, def.name.value)) {
                            allowedFields.add((0, graphql_relational_transformer_1.getConnectionAttributeName)(ctx.transformParameters, def.name.value, field, (0, graphql_relational_transformer_1.getObjectPrimaryKey)(relatedType).name.value));
                        }
                        (0, graphql_transformer_core_1.getSortKeyFieldNames)(def).forEach((sortKeyFieldName) => {
                            allowedFields.add((0, graphql_relational_transformer_1.getSortKeyConnectionAttributeName)(def.name.value, field, sortKeyFieldName));
                        });
                    }
                });
            });
        };
        this.addAutoGeneratedDataStoreFields = (ctx, allowedFields) => {
            const dataStoreFields = ctx.isProjectUsingDataStore() ? ['_version', '_deleted', '_lastChangedAt'] : [];
            dataStoreFields.forEach((item) => allowedFields.add(item));
        };
        this.getVtlGenerator = (ctx, typename, field) => {
            if (field && field.directives.some((dir) => dir.name.value === 'sql')) {
                return new rds_vtl_generator_1.RDSAuthVTLGenerator();
            }
            if ((0, graphql_transformer_core_1.isModelType)(ctx, typename) && (0, graphql_transformer_core_1.isSqlModel)(ctx, typename)) {
                return new rds_vtl_generator_1.RDSAuthVTLGenerator();
            }
            return new ddb_vtl_generator_1.DDBAuthVTLGenerator();
        };
        this.modelDirectiveConfig = new Map();
        this.seenNonModelTypes = new Map();
        this.authModelConfig = new Map();
        this.roleMap = new Map();
        this.generateIAMPolicyForUnauthRole = false;
        this.generateIAMPolicyForAuthRole = false;
        this.authNonModelConfig = new Map();
        this.rules = [];
    }
    convertRulesToRoles(acm, authRules, allowRoleOverwrite, field, overrideOperations, context) {
        authRules.forEach((rule) => {
            const operations = overrideOperations || rule.operations || utils_1.MODEL_OPERATIONS;
            if (rule.groups && !rule.groupsField) {
                rule.groups.forEach((group) => {
                    const groupClaim = rule.groupClaim || utils_1.DEFAULT_GROUP_CLAIM;
                    const roleName = `${rule.provider}:staticGroup:${group}:${groupClaim}`;
                    if (!(roleName in this.roleMap)) {
                        this.roleMap.set(roleName, {
                            provider: rule.provider,
                            strategy: rule.allow,
                            static: true,
                            claim: groupClaim,
                            entity: group,
                        });
                    }
                    acm.setRole({
                        role: roleName,
                        resource: field,
                        operations,
                        allowRoleOverwrite,
                    });
                });
            }
            else {
                let roleName;
                let roleDefinition;
                switch (rule.provider) {
                    case 'apiKey':
                        roleName = 'apiKey:public';
                        roleDefinition = { provider: rule.provider, strategy: rule.allow, static: true };
                        break;
                    case 'function':
                        roleName = 'function:custom';
                        roleDefinition = { provider: rule.provider, strategy: rule.allow, static: true };
                        break;
                    case 'iam':
                    case 'identityPool':
                        roleName = `iam:${rule.allow}`;
                        roleDefinition = {
                            provider: rule.provider,
                            strategy: rule.allow,
                            static: true,
                            claim: rule.allow === 'private' ? 'authRole' : 'unauthRole',
                        };
                        break;
                    case 'oidc':
                    case 'userPools':
                        if (rule.allow === 'groups') {
                            const groupClaim = rule.groupClaim || utils_1.DEFAULT_GROUP_CLAIM;
                            const groupsField = rule.groupsField || utils_1.DEFAULT_GROUPS_FIELD;
                            const fieldType = context.output.getType(acm.getName()).fields.find((f) => f.name.value === groupsField);
                            const isGroupFieldList = fieldType ? (0, graphql_transformer_common_1.isListType)(fieldType.type) : false;
                            roleName = `${rule.provider}:dynamicGroup:${groupsField}:${groupClaim}`;
                            roleDefinition = {
                                provider: rule.provider,
                                strategy: rule.allow,
                                static: false,
                                claim: groupClaim,
                                entity: groupsField,
                                isEntityList: isGroupFieldList,
                            };
                        }
                        else if (rule.allow === 'owner') {
                            const ownerField = rule.ownerField || utils_1.DEFAULT_OWNER_FIELD;
                            const fieldType = context.output.getType(acm.getName()).fields.find((f) => f.name.value === ownerField);
                            const isOwnerFieldList = fieldType ? (0, graphql_transformer_common_1.isListType)(fieldType.type) : false;
                            const useSub = context.transformParameters.useSubUsernameForDefaultIdentityClaim;
                            const ownerClaim = rule.identityClaim || (useSub ? utils_1.DEFAULT_UNIQUE_IDENTITY_CLAIM : utils_1.DEFAULT_IDENTITY_CLAIM);
                            roleName = `${rule.provider}:owner:${ownerField}:${ownerClaim}`;
                            roleDefinition = {
                                provider: rule.provider,
                                strategy: rule.allow,
                                static: false,
                                claim: ownerClaim,
                                entity: ownerField,
                                isEntityList: isOwnerFieldList,
                            };
                        }
                        else if (rule.allow === 'private') {
                            roleName = `${rule.provider}:${rule.allow}`;
                            roleDefinition = {
                                provider: rule.provider,
                                strategy: rule.allow,
                                static: true,
                            };
                        }
                        else {
                            throw new graphql_transformer_core_1.TransformerContractError(`Could not create a role from ${JSON.stringify(rule)}`);
                        }
                        break;
                    default:
                        throw new graphql_transformer_core_1.TransformerContractError(`Could not create a role from ${JSON.stringify(rule)}`);
                }
                if (!(roleName in this.roleMap)) {
                    this.roleMap.set(roleName, roleDefinition);
                }
                acm.setRole({
                    role: roleName,
                    resource: field,
                    operations,
                    allowRoleOverwrite,
                });
            }
        });
    }
    doesTypeHaveRulesForOperation(acm, operation) {
        const rolesHasDefaultProvider = (roles) => roles.some((r) => (0, utils_1.isAuthProviderEqual)(this.roleMap.get(r).provider, this.configuredAuthProviders.default));
        const roles = acm.getRolesPerOperation(operation, operation === 'delete');
        return rolesHasDefaultProvider(roles) || (roles.length === 0 && this.configuredAuthProviders.shouldAddDefaultServiceDirective);
    }
    getAuthProviders(roles) {
        const providers = new Set();
        roles.forEach((role) => providers.add(this.roleMap.get(role).provider));
        if (this.configuredAuthProviders.hasAdminRolesEnabled || this.configuredAuthProviders.genericIamAccessEnabled) {
            providers.add('identityPool');
        }
        return Array.from(providers);
    }
    propagateAuthDirectivesToNestedTypes(ctx, def, providers) {
        const nonModelTypePredicate = (fieldType) => {
            if (fieldType) {
                if (fieldType.kind !== 'ObjectTypeDefinition') {
                    return undefined;
                }
                const typeModel = fieldType.directives.find((dir) => dir.name.value === 'model');
                return typeModel !== undefined ? undefined : fieldType;
            }
            return fieldType;
        };
        const nonModelFieldTypes = def
            .fields.map((f) => ctx.output.getType((0, graphql_transformer_common_1.getBaseType)(f.type)))
            .filter(nonModelTypePredicate);
        nonModelFieldTypes.forEach((nonModelFieldType) => {
            const nonModelName = nonModelFieldType.name.value;
            const hasSeenType = this.seenNonModelTypes.has(nonModelFieldType.name.value);
            let directives = this.getServiceDirectives(providers, hasSeenType);
            if (!hasSeenType) {
                this.seenNonModelTypes.set(nonModelName, new Set([...directives.map((dir) => dir.name.value)]));
                const hasIAM = directives.some((dir) => dir.name.value === 'aws_iam') ||
                    (0, utils_1.isAuthProviderEqual)(this.configuredAuthProviders.default, 'identityPool');
                if (hasIAM) {
                    this.unauthPolicyResources.add(`${nonModelFieldType.name.value}/null`);
                    this.authPolicyResources.add(`${nonModelFieldType.name.value}/null`);
                }
            }
            else {
                const currentDirectives = this.seenNonModelTypes.get(nonModelName);
                directives = directives.filter((dir) => !currentDirectives.has(dir.name.value));
                this.seenNonModelTypes.set(nonModelName, new Set([...directives.map((dir) => dir.name.value), ...currentDirectives]));
            }
            if (directives.length > 0 || !hasSeenType) {
                (0, utils_1.extendTypeWithDirectives)(ctx, nonModelFieldType.name.value, directives);
                this.propagateAuthDirectivesToNestedTypes(ctx, nonModelFieldType, providers);
            }
        });
    }
    getServiceDirectives(providers, addDefaultIfNeeded = true) {
        if (providers.length === 0) {
            return [];
        }
        const directives = [];
        const addDirectiveIfNeeded = (provider, directiveName) => {
            if ((!(0, utils_1.isAuthProviderEqual)(this.configuredAuthProviders.default, provider) && providers.some((p) => (0, utils_1.isAuthProviderEqual)(p, provider))) ||
                ((0, utils_1.isAuthProviderEqual)(this.configuredAuthProviders.default, provider) &&
                    providers.some((p) => !(0, utils_1.isAuthProviderEqual)(p, provider) && addDefaultIfNeeded === true))) {
                directives.push((0, graphql_transformer_common_1.makeDirective)(directiveName, []));
            }
        };
        const directivesAlreadyApplied = new Set();
        utils_1.AUTH_PROVIDER_DIRECTIVE_MAP.forEach((directiveName, authProvider) => {
            if (!directivesAlreadyApplied.has(directiveName)) {
                addDirectiveIfNeeded(authProvider, directiveName);
                directivesAlreadyApplied.add(directiveName);
            }
        });
        if (providers.some((p) => (0, utils_1.isAuthProviderEqual)(p, this.configuredAuthProviders.default)) &&
            providers.some((p) => !(0, utils_1.isAuthProviderEqual)(p, this.configuredAuthProviders.default)) &&
            !directives.some((d) => d.name.value === utils_1.AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default))) {
            directives.push((0, graphql_transformer_common_1.makeDirective)(utils_1.AUTH_PROVIDER_DIRECTIVE_MAP.get(this.configuredAuthProviders.default), []));
        }
        return directives;
    }
    generateIAMPolicies(ctx) {
        if (this.generateIAMPolicyForAuthRole) {
            if (this.authPolicyResources.size === 0) {
                if (!this.configuredAuthProviders.hasAdminRolesEnabled) {
                    throw new graphql_transformer_core_1.TransformerContractError('AuthRole policies should be generated, but no resources were added.');
                }
            }
            else {
                const authRole = ctx.synthParameters.authenticatedUserRoleName;
                const authPolicyDocuments = (0, utils_1.createPolicyDocumentForManagedPolicy)(ctx, this.authPolicyResources);
                const { scope } = ctx.stackManager;
                const iamAuthRoleArn = iam.Role.fromRoleArn(scope, 'auth-role-name', `arn:aws:iam::${cdk.Stack.of(scope).account}:role/${authRole}`);
                authPolicyDocuments.forEach((authPolicyDocument, i) => {
                    const paddedIndex = `${i + 1}`.padStart(2, '0');
                    const resourceName = `${graphql_transformer_common_1.ResourceConstants.RESOURCES.AuthRolePolicy}${paddedIndex}`;
                    new iam.ManagedPolicy(scope, resourceName, {
                        document: iam.PolicyDocument.fromJson(authPolicyDocument),
                        roles: [iamAuthRoleArn],
                    });
                });
            }
        }
        if (this.generateIAMPolicyForUnauthRole) {
            if (this.unauthPolicyResources.size === 0) {
                throw new graphql_transformer_core_1.TransformerContractError('UnauthRole policies should be generated, but no resources were added');
            }
            const unauthRole = ctx.synthParameters.unauthenticatedUserRoleName;
            const unauthPolicyDocuments = (0, utils_1.createPolicyDocumentForManagedPolicy)(ctx, this.unauthPolicyResources);
            const { scope } = ctx.stackManager;
            const iamUnauthRoleArn = iam.Role.fromRoleArn(scope, 'unauth-role-name', `arn:aws:iam::${cdk.Stack.of(scope).account}:role/${unauthRole}`);
            unauthPolicyDocuments.forEach((unauthPolicyDocument, i) => {
                const paddedIndex = `${i + 1}`.padStart(2, '0');
                const resourceName = `${graphql_transformer_common_1.ResourceConstants.RESOURCES.UnauthRolePolicy}${paddedIndex}`;
                new iam.ManagedPolicy(ctx.stackManager.scope, resourceName, {
                    document: iam.PolicyDocument.fromJson(unauthPolicyDocument),
                    roles: [iamUnauthRoleArn],
                });
            });
        }
    }
    setAuthPolicyFlag(rules) {
        if (rules.length === 0 || this.generateIAMPolicyForAuthRole === true) {
            return;
        }
        this.generateIAMPolicyForAuthRole = rules.some((rule) => (rule.allow === 'private' || rule.allow === 'public') && (0, utils_1.isAuthProviderEqual)(rule.provider, 'identityPool'));
    }
    setUnauthPolicyFlag(rules) {
        if (rules.length === 0 || this.generateIAMPolicyForUnauthRole === true) {
            return;
        }
        this.generateIAMPolicyForUnauthRole = rules.some((rule) => rule.allow === 'public' && (0, utils_1.isAuthProviderEqual)(rule.provider, 'identityPool'));
    }
    addOperationToResourceReferences(operationName, fieldName, roles) {
        const iamPublicRolesExist = roles.some((r) => (0, utils_1.isAuthProviderEqual)(this.roleMap.get(r).provider, 'identityPool') && this.roleMap.get(r).strategy === 'public');
        const iamPrivateRolesExist = roles.some((r) => (0, utils_1.isAuthProviderEqual)(this.roleMap.get(r).provider, 'identityPool') && this.roleMap.get(r).strategy === 'private');
        if (iamPublicRolesExist) {
            this.unauthPolicyResources.add(`${operationName}/${fieldName}`);
            this.authPolicyResources.add(`${operationName}/${fieldName}`);
        }
        if (iamPrivateRolesExist) {
            this.authPolicyResources.add(`${operationName}/${fieldName}`);
        }
    }
    addTypeToResourceReferences(typeName, rules) {
        const iamPublicRulesExist = rules.some((r) => r.allow === 'public' && (0, utils_1.isAuthProviderEqual)(r.provider, 'identityPool') && r.generateIAMPolicy);
        const iamPrivateRulesExist = rules.some((r) => r.allow === 'private' && (0, utils_1.isAuthProviderEqual)(r.provider, 'identityPool') && r.generateIAMPolicy);
        if (iamPublicRulesExist) {
            this.unauthPolicyResources.add(`${typeName}/null`);
            this.authPolicyResources.add(`${typeName}/null`);
        }
        if (iamPrivateRulesExist) {
            this.authPolicyResources.add(`${typeName}/null`);
        }
    }
    addFieldToResourceReferences(typeName, fieldName, rules) {
        const iamPublicRulesExist = rules.some((r) => r.allow === 'public' && (0, utils_1.isAuthProviderEqual)(r.provider, 'identityPool') && r.generateIAMPolicy);
        const iamPrivateRulesExist = rules.some((r) => r.allow === 'private' && (0, utils_1.isAuthProviderEqual)(r.provider, 'identityPool') && r.generateIAMPolicy);
        if (iamPublicRulesExist) {
            this.unauthPolicyResources.add(`${typeName}/${fieldName}`);
            this.authPolicyResources.add(`${typeName}/${fieldName}`);
        }
        if (iamPrivateRulesExist) {
            this.authPolicyResources.add(`${typeName}/${fieldName}`);
        }
    }
}
exports.AuthTransformer = AuthTransformer;
//# sourceMappingURL=graphql-auth-transformer.js.map