app/collectors/acmCertificates.scala (158 lines of code) (raw):
package collectors
import java.time.Instant
import agent._
import conf.AWS
import controllers.routes
import play.api.mvc.Call
import software.amazon.awssdk.services.acm.AcmClient
import software.amazon.awssdk.services.acm.model.{
CertificateDetail,
DescribeCertificateRequest,
ListCertificatesRequest,
RenewalSummary,
ResourceRecord,
DomainValidation => AwsDomainValidation
}
import utils.Logging
import scala.jdk.CollectionConverters._
import scala.language.postfixOps
class AmazonCertificateCollectorSet(accounts: Accounts)
extends CollectorSet[AcmCertificate](
ResourceType("acmCertificates"),
accounts,
Some(Regional)
) {
val lookupCollector: PartialFunction[Origin, Collector[AcmCertificate]] = {
case amazon: AmazonOrigin =>
AWSAcmCertificateCollector(
amazon,
resource,
amazon.crawlRate(resource.name)
)
}
}
case class AWSAcmCertificateCollector(
origin: AmazonOrigin,
resource: ResourceType,
crawlRate: CrawlRate
) extends Collector[AcmCertificate]
with Logging {
val client: AcmClient = AcmClient.builder
.credentialsProvider(origin.credentials.provider)
.region(origin.awsRegionV2)
.overrideConfiguration(AWS.clientConfig)
.build
def crawl: Iterable[AcmCertificate] = {
client
.listCertificatesPaginator(ListCertificatesRequest.builder.build)
.certificateSummaryList
.asScala
.map { cert =>
val requestDetails =
DescribeCertificateRequest.builder.certificateArn(cert.certificateArn)
val resultDetails = client.describeCertificate(requestDetails.build)
AcmCertificate.fromApiData(resultDetails.certificate, origin)
}
}
}
case class DomainResourceRecord(
name: String,
resourceType: String,
value: String
)
object DomainResourceRecord {
def fromApiData(drr: ResourceRecord): DomainResourceRecord = {
DomainResourceRecord(
name = drr.name,
resourceType = drr.typeAsString,
value = drr.value
)
}
}
case class DomainValidation(
domainName: String,
validationEmails: List[String],
validationDomain: String,
validationStatus: String,
validationMethod: String,
resourceRecord: Option[DomainResourceRecord]
)
object DomainValidation {
def fromApiData(dv: AwsDomainValidation): DomainValidation = {
DomainValidation(
dv.domainName,
Option(dv.validationEmails).toList.flatMap(_.asScala.toList),
dv.validationDomain,
dv.validationStatusAsString,
dv.validationMethodAsString,
Option(dv.resourceRecord).map(DomainResourceRecord.fromApiData)
)
}
}
case class RenewalInfo(
renewalStatus: String,
domainValidationOptions: List[DomainValidation]
)
object RenewalInfo {
def fromApiData(ri: RenewalSummary): RenewalInfo = {
RenewalInfo(
ri.renewalStatusAsString,
Option(ri.domainValidationOptions)
.map(_.asScala)
.getOrElse(Nil)
.map(DomainValidation.fromApiData)
.toList
)
}
}
object AcmCertificate {
def fromApiData(
cert: CertificateDetail,
origin: AmazonOrigin
): AcmCertificate = AcmCertificate(
arn = cert.certificateArn,
domainName = cert.domainName,
subjectAlternativeNames = cert.subjectAlternativeNames.asScala.toList,
certificateType = cert.typeAsString,
status = cert.statusAsString,
issuer = cert.issuer,
inUseBy = cert.inUseBy.asScala.toList,
notBefore = Option(cert.notBefore),
notAfter = Option(cert.notAfter),
createdAt = Option(cert.createdAt),
issuedAt = Option(cert.issuedAt),
failureReason = Option(cert.failureReasonAsString),
subject = cert.subject,
keyAlgorithm = cert.keyAlgorithmAsString,
signatureAlgorithm = cert.signatureAlgorithm,
serial = cert.serial,
validationMethod = cert.domainValidationOptions.asScala.headOption
.map(_.validationMethodAsString),
domainValidationOptions = cert.domainValidationOptions.asScala.toList
.map(DomainValidation.fromApiData),
renewalStatus = Option(cert.renewalSummary).map(RenewalInfo.fromApiData)
)
}
case class AcmCertificate(
arn: String,
domainName: String,
subjectAlternativeNames: List[String],
certificateType: String,
status: String,
issuer: String,
inUseBy: List[String],
notBefore: Option[Instant],
notAfter: Option[Instant],
createdAt: Option[Instant],
issuedAt: Option[Instant],
failureReason: Option[String],
subject: String,
keyAlgorithm: String,
signatureAlgorithm: String,
serial: String,
validationMethod: Option[String],
domainValidationOptions: List[DomainValidation],
renewalStatus: Option[RenewalInfo]
) extends IndexedItem {
def callFromArn: (String) => Call = arn => routes.Api.acmCertificate(arn)
}