hq/app/aws/support/TrustedAdvisorS3.scala (77 lines of code) (raw):

package aws.support import aws.s3.S3 import aws.support.TrustedAdvisor.{getTrustedAdvisorCheckDetails, parseTrustedAdvisorCheckResult} import aws.{AwsClient, AwsClients} import model._ import utils.attempt.{Attempt, FailedAttempt, Failure} import scala.jdk.CollectionConverters._ import scala.concurrent.{ExecutionContext, Future} import scala.util.control.NonFatal import scala.util.{Success, Try} import software.amazon.awssdk.regions.Region import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.support.SupportAsyncClient import software.amazon.awssdk.services.support.model.TrustedAdvisorResourceDetail object TrustedAdvisorS3 { private val S3_Bucket_Permissions = "Pfx0RwqBli" def getAllPublicBuckets(accounts: List[AwsAccount], taClients: AwsClients[SupportAsyncClient], s3Clients: AwsClients[S3Client])(implicit ec: ExecutionContext): Attempt[List[(AwsAccount, Either[FailedAttempt, List[BucketDetail]])]] = { Attempt.Async.Right { Future.traverse(accounts) { account => publicBucketsForAccount(account, taClients, s3Clients).asFuture.map(account -> _) } } } private def getBucketReport(client: AwsClient[SupportAsyncClient])(implicit ec: ExecutionContext): Attempt[TrustedAdvisorDetailsResult[BucketDetail]] = { getTrustedAdvisorCheckDetails(client, S3_Bucket_Permissions) .flatMap(parseTrustedAdvisorCheckResult(parseBucketDetail, ec)) } // When you use server-side encryption, Amazon S3 encrypts an object before saving // it to disk in its data centers and decrypts it when you download the object private def addEncryptionStatus(bucket: BucketDetail, account: AwsAccount, clients: AwsClients[S3Client])(implicit ec: ExecutionContext): Attempt[Option[BucketDetail]] = { val tryFindEncryptionStatus = Try(Region.of(bucket.region)).map { regions => clients.get(account, regions).flatMap { clientWrapper => S3.getBucketEncryption(clientWrapper.client, bucket.bucketName) } } tryFindEncryptionStatus match { case Success(attempt) => attempt.map({ case Encrypted => Some(bucket.copy(isEncrypted = true)) case NotEncrypted => Some(bucket) case BucketNotFound => None }) case scala.util.Failure(_) => Attempt.Left(FailedAttempt(Failure( s"Unrecognised region returned from Trusted Advisor for bucket ${bucket.bucketName}", "Encryption status for this bucket was unable to be fetched due to an unrecognised region being provided by Trusted Advisor.", 500 ))) } } private def publicBucketsForAccount(account: AwsAccount, taClients: AwsClients[SupportAsyncClient], s3Clients: AwsClients[S3Client])(implicit ec: ExecutionContext): Attempt[List[BucketDetail]] = { for { supportClient <- taClients.get(account) bucketResult <- getBucketReport(supportClient) enhancedBuckets <- Attempt.traverse(bucketResult.flaggedResources)(addEncryptionStatus(_, account, s3Clients)) } yield enhancedBuckets.flatten //remove buckets we weren't able to find encryption status for } private[support] def parseBucketDetail(detail: TrustedAdvisorResourceDetail): Attempt[BucketDetail] = { def toBoolean(str: String): Boolean = str.toLowerCase.contentEquals("yes") detail.metadata.asScala.toList match { case region :: _ :: bucketName :: aclAllowsRead :: aclAllowsWrite :: status :: policyAllowsAccess :: _ => Attempt.Right { BucketDetail( region, bucketName, status.toLowerCase, toBoolean(aclAllowsRead), toBoolean(aclAllowsWrite), toBoolean(policyAllowsAccess), isSuppressed = detail.isSuppressed, None ) } case metadata => Attempt.Left { utils.attempt.Failure(s"Could not parse S3 Bucket report from TrustedAdvisorResourceDetail with metadata $metadata", "Could not parse public S3 Buckets", 500).attempt } } } }