"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tokens_1 = require("../core/tokens");
const fn_1 = require("./fn");
const pseudo_1 = require("./pseudo");
class PolicyDocument extends tokens_1.Token {
    /**
     * Creates a new IAM policy document.
     * @param defaultDocument An IAM policy document to use as an initial
     * policy. All statements of this document will be copied in.
     */
    constructor(baseDocument) {
        super();
        this.baseDocument = baseDocument;
        this.statements = new Array();
    }
    resolve() {
        if (this.isEmpty) {
            return undefined;
        }
        const doc = this.baseDocument || {};
        doc.Statement = doc.Statement || [];
        doc.Version = doc.Version || '2012-10-17';
        doc.Statement = doc.Statement.concat(this.statements);
        return doc;
    }
    get isEmpty() {
        return this.statements.length === 0;
    }
    /**
     * The number of statements already added to this policy.
     * Can be used, for example, to generate uniuqe "sid"s within the policy.
     */
    get statementCount() {
        return this.statements.length;
    }
    addStatement(statement) {
        this.statements.push(statement);
        return this;
    }
}
exports.PolicyDocument = PolicyDocument;
/**
 * Represents an IAM principal.
 */
class PolicyPrincipal {
    constructor() {
        /**
         * When this Principal is used in an AssumeRole policy, the action to use.
         */
        this.assumeRoleAction = 'sts:AssumeRole';
    }
}
exports.PolicyPrincipal = PolicyPrincipal;
/**
 * A collection of the fields in a PolicyStatement that can be used to identify a principal.
 *
 * This consists of the JSON used in the "Principal" field, and optionally a
 * set of "Condition"s that need to be applied to the policy.
 */
class PrincipalPolicyFragment {
    constructor(principalJson, conditions = {}) {
        this.principalJson = principalJson;
        this.conditions = conditions;
    }
}
exports.PrincipalPolicyFragment = PrincipalPolicyFragment;
class ArnPrincipal extends PolicyPrincipal {
    constructor(arn) {
        super();
        this.arn = arn;
    }
    policyFragment() {
        return new PrincipalPolicyFragment({ AWS: this.arn });
    }
}
exports.ArnPrincipal = ArnPrincipal;
class AccountPrincipal extends ArnPrincipal {
    constructor(accountId) {
        super(new fn_1.FnConcat('arn:', new pseudo_1.AwsPartition(), ':iam::', accountId, ':root'));
        this.accountId = accountId;
    }
}
exports.AccountPrincipal = AccountPrincipal;
/**
 * An IAM principal that represents an AWS service (i.e. sqs.amazonaws.com).
 */
class ServicePrincipal extends PolicyPrincipal {
    constructor(service) {
        super();
        this.service = service;
    }
    policyFragment() {
        return new PrincipalPolicyFragment({ Service: this.service });
    }
}
exports.ServicePrincipal = ServicePrincipal;
/**
 * A policy prinicipal for canonicalUserIds - useful for S3 bucket policies that use
 * Origin Access identities.
 *
 * See https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html
 *
 * and
 *
 * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
 *
 * for more details.
 *
 */
class CanonicalUserPrincipal extends PolicyPrincipal {
    constructor(canonicalUserId) {
        super();
        this.canonicalUserId = canonicalUserId;
    }
    policyFragment() {
        return new PrincipalPolicyFragment({ CanonicalUser: this.canonicalUserId });
    }
}
exports.CanonicalUserPrincipal = CanonicalUserPrincipal;
class FederatedPrincipal extends PolicyPrincipal {
    constructor(federated, conditions, assumeRoleAction = 'sts:AssumeRole') {
        super();
        this.federated = federated;
        this.conditions = conditions;
        this.assumeRoleAction = assumeRoleAction;
    }
    policyFragment() {
        return new PrincipalPolicyFragment({ Federated: this.federated }, this.conditions);
    }
}
exports.FederatedPrincipal = FederatedPrincipal;
class AccountRootPrincipal extends AccountPrincipal {
    constructor() {
        super(new pseudo_1.AwsAccountId());
    }
}
exports.AccountRootPrincipal = AccountRootPrincipal;
/**
 * A principal representing all identities in all accounts
 */
class Anyone extends PolicyPrincipal {
    constructor() {
        super(...arguments);
        /**
         * Interface compatibility with AccountPrincipal for the purposes of the Lambda library
         *
         * The Lambda's addPermission() call works differently from regular
         * statements, and will use the value of this property directly if present
         * (which leads to the correct statement ultimately).
         */
        this.accountId = '*';
    }
    policyFragment() {
        return new PrincipalPolicyFragment('*');
    }
}
exports.Anyone = Anyone;
/**
 * Represents a statement in an IAM policy document.
 */
class PolicyStatement extends tokens_1.Token {
    constructor(effect = PolicyStatementEffect.Allow) {
        super();
        this.action = new Array();
        this.principal = new Array();
        this.resource = new Array();
        this.condition = {};
        this.effect = effect;
    }
    //
    // Actions
    //
    addAction(action) {
        this.action.push(action);
        return this;
    }
    addActions(...actions) {
        actions.forEach(action => this.addAction(action));
        return this;
    }
    //
    // Principal
    //
    /**
     * Indicates if this permission has a "Principal" section.
     */
    get hasPrincipal() {
        return this.principal && this.principal.length > 0;
    }
    addPrincipal(principal) {
        const fragment = principal.policyFragment();
        this.principal.push(fragment.principalJson);
        this.addConditions(fragment.conditions);
        return this;
    }
    addAwsPrincipal(arn) {
        return this.addPrincipal(new ArnPrincipal(arn));
    }
    addAwsAccountPrincipal(accountId) {
        return this.addPrincipal(new AccountPrincipal(accountId));
    }
    addServicePrincipal(service) {
        return this.addPrincipal(new ServicePrincipal(service));
    }
    addFederatedPrincipal(federated, conditions) {
        return this.addPrincipal(new FederatedPrincipal(federated, conditions));
    }
    addAccountRootPrincipal() {
        return this.addPrincipal(new AccountRootPrincipal());
    }
    //
    // Resources
    //
    addResource(resource) {
        this.resource.push(resource);
        return this;
    }
    /**
     * Adds a ``"*"`` resource to this statement.
     */
    addAllResources() {
        return this.addResource('*');
    }
    addResources(...resources) {
        resources.forEach(r => this.addResource(r));
        return this;
    }
    /**
     * Indicates if this permission as at least one resource associated with it.
     */
    get hasResource() {
        return this.resource && this.resource.length > 0;
    }
    /**
     * Indicates if this permission has only a ``"*"`` resource associated with it.
     */
    get isOnlyStarResource() {
        return this.resource && this.resource.length === 1 && this.resource[0] === '*';
    }
    describe(sid) {
        this.sid = sid;
        return this;
    }
    //
    // Effect
    //
    /**
     * Sets the permission effect to deny access to resources.
     */
    allow() {
        this.effect = PolicyStatementEffect.Allow;
        return this;
    }
    /**
     * Sets the permission effect to allow access to resources.
     */
    deny() {
        this.effect = PolicyStatementEffect.Deny;
        return this;
    }
    //
    // Condition
    //
    /**
     * Add a condition to the Policy
     */
    addCondition(key, value) {
        this.condition[key] = value;
        return this;
    }
    /**
     * Add multiple conditions to the Policy
     */
    addConditions(conditions) {
        Object.keys(conditions).map(key => {
            this.addCondition(key, conditions[key]);
        });
        return this;
    }
    /**
     * Add a condition to the Policy.
     *
     * @deprecated For backwards compatibility. Use addCondition() instead.
     */
    setCondition(key, value) {
        return this.addCondition(key, value);
    }
    limitToAccount(accountId) {
        return this.addCondition('StringEquals', new tokens_1.Token(() => {
            return { 'sts:ExternalId': accountId };
        }));
    }
    //
    // Serialization
    //
    resolve() {
        return this.toJson();
    }
    toJson() {
        return {
            Action: _norm(this.action),
            Condition: _norm(this.condition),
            Effect: _norm(this.effect),
            Principal: _norm(this.principal),
            Resource: _norm(this.resource),
            Sid: _norm(this.sid),
        };
        function _norm(values) {
            if (typeof (values) === 'undefined') {
                return undefined;
            }
            if (Array.isArray(values)) {
                if (!values || values.length === 0) {
                    return undefined;
                }
                if (values.length === 1) {
                    return values[0];
                }
            }
            if (typeof (values) === 'object') {
                if (Object.keys(values).length === 0) {
                    return undefined;
                }
            }
            return values;
        }
    }
}
exports.PolicyStatement = PolicyStatement;
var PolicyStatementEffect;
(function (PolicyStatementEffect) {
    PolicyStatementEffect["Allow"] = "Allow";
    PolicyStatementEffect["Deny"] = "Deny";
})(PolicyStatementEffect = exports.PolicyStatementEffect || (exports.PolicyStatementEffect = {}));
//# sourceMappingURL=data:application/json;base64, 