// This file is based on
// https://code.amazon.com/packages/FultonConstructs/blobs/mainline/--/lib/ssm-bastion/spie-bucket-and-role-singleton.ts

import * as cdk from "@aws-cdk/core";
import * as iam from "@aws-cdk/aws-iam";
import * as s3 from "@aws-cdk/aws-s3";

/**
 * This construct will deploy an S3 bucket and IAM permissions for SPIE.
 * These are global resources with hardcoded names, so there can only be one per account.
 *
 * Only deploy this once per account.
 */
export class SPIEBucketAndRoleSingleton extends cdk.Construct {
  public readerRole: iam.IRole;
  public instancePolicy: iam.Policy;
  constructor(scope: cdk.Construct, id: string) {
    super(scope, id);
    const account = cdk.Stack.of(this).account;
    const spieBucket = new s3.Bucket(this, 'SpieBucket', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      bucketName: `singlepass-${account}-client-data`,
    });
    const spieReaderRole = new iam.Role(this, 'SinglePassReaderRole', {
      assumedBy: new iam.AccountPrincipal(account),
      roleName: 'singlepass-sync-reader',
    });
    this.readerRole = spieReaderRole;
    const spieWriterRole = new iam.Role(this, 'SinglepassWriterRole', {
      assumedBy: new iam.ServicePrincipal('accountsync.spie.aws.internal'),
      roleName: 'singlepass-sync-writer',
    });
    spieBucket.addToResourcePolicy(
      new iam.PolicyStatement({
        sid: 'Allow the service role to read and write to bucket',
        effect: iam.Effect.ALLOW,
        principals: [new iam.ArnPrincipal(spieWriterRole.roleArn)],
        actions: ['s3:ListBucket', 's3:GetObject*', 's3:DeleteObject*', 's3:PutObject*', 's3:GetBucket*'],
        resources: [spieBucket.bucketArn, `${spieBucket.bucketArn}/*`],
      }),
    );

    spieBucket.addToResourcePolicy(
      new iam.PolicyStatement({
        sid: 'Allow the client role to read bucket contents.',
        effect: iam.Effect.ALLOW,
        principals: [new iam.ArnPrincipal(this.readerRole.roleArn)],
        actions: ['s3:ListBucket', 's3:GetObject*', 's3:GetBucket*'],
        resources: [spieBucket.bucketArn, `${spieBucket.bucketArn}/*`],
      }),
    );
    spieBucket.addToResourcePolicy(
      new iam.PolicyStatement({
        sid: 'Allow the client role to write to only metric prefix in bucket.',
        effect: iam.Effect.ALLOW,
        principals: [new iam.ArnPrincipal(this.readerRole.roleArn)],
        actions: ['s3:DeleteObject*', 's3:PutObject*'],
        resources: [`${spieBucket.bucketArn}/metrics`],
      }),
    );
    const instancePolicyDocument = new iam.PolicyDocument();
    instancePolicyDocument.addStatements(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ['ec2:DescribeTags'],
        resources: ['*'],
      }),
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ['sts:AssumeRole'],
        resources: [this.readerRole.roleArn],
      }),
    );
    this.instancePolicy = new iam.Policy(this, 'singlePassInstancePolicy', {
      document: instancePolicyDocument,
    });
    const spieSshAccessPolicyDoc = new iam.PolicyDocument();
    spieSshAccessPolicyDoc.addStatements(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ['ec2:DescribeTags', 'ssm:StartSession'],
        resources: ['*'],
      }),
    );
    new iam.Role(this, 'SpieSsmSsh', {
      roleName: 'SpieSshBastion',
      inlinePolicies: {
        SpieAccessPolicy: spieSshAccessPolicyDoc,
      },
      assumedBy: new iam.AccountPrincipal(cdk.Stack.of(this).account),
    });
  }
}