app/notification/LambdaDistributionBucket.scala (87 lines of code) (raw):

package notification import com.amazonaws.services.s3.AmazonS3 import play.api.libs.json._ object LambdaDistributionBucket { def updateBucketPolicy( s3Client: AmazonS3, bucketName: String, stage: String, accountNumbers: Seq[String] ): Unit = { val policyText = Option(s3Client.getBucketPolicy(bucketName).getPolicyText) .filter(_.nonEmpty) val copierStatement = LambdaDistributionBucket.createCopierStatement( bucketName, stage, accountNumbers ) val newPolicy = LambdaDistributionBucket.updateCopierStatement( stage, policyText, copierStatement ) s3Client.setBucketPolicy(bucketName, newPolicy) } /* Implement the policy grammar (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html) */ case class Statement( Sid: Option[String], Effect: String, Principal: Option[JsValue] = None, NotPrincipal: Option[JsValue] = None, Action: Option[JsValue] = None, NotAction: Option[JsValue] = None, Resource: Option[JsValue] = None, NotResource: Option[JsValue] = None, Condition: Option[JsValue] = None ) case class BucketPolicy( Id: Option[String], Version: Option[String], Statement: List[Statement] ) implicit val statementFormat: Format[Statement] = Json.format[Statement] implicit val bucketPolicyFormat: Format[BucketPolicy] = Json.format[BucketPolicy] def parsePolicyText(policyText: String): BucketPolicy = Json.fromJson[BucketPolicy](Json.parse(policyText)).get def createPolicyText(bucketPolicy: BucketPolicy): String = Json.toJson(bucketPolicy).toString def imageCopierDistributionSid(stage: String) = s"ImageCopierDistribution$stage" def createCopierStatement( bucketName: String, stage: String, accounts: Seq[String] ): Statement = { Statement( Sid = Some(imageCopierDistributionSid(stage)), Effect = "Allow", Principal = Some(Json.obj("AWS" -> accounts)), Action = Some(JsString("s3:GetObject")), Resource = Some( JsArray( Seq( JsString(s"arn:aws:s3:::$bucketName/deploy/$stage/imagecopier/*"), JsString( s"arn:aws:s3:::$bucketName/deploy/$stage/housekeeping-lambda/*" ) ) ) ) ) } /* Add the provided statement, removing prior statement with the image copier SID */ def updateCopierStatement( stage: String, maybeBucketPolicyText: Option[String], newStatement: Statement ): String = { val bucketPolicy = maybeBucketPolicyText .map(parsePolicyText) .getOrElse(BucketPolicy(None, None, Nil)) val newPolicy = bucketPolicy.copy( Statement = newStatement :: bucketPolicy.Statement.filterNot( _.Sid.contains(imageCopierDistributionSid(stage)) ) ) createPolicyText(newPolicy) } }