mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-14 21:28:58 +00:00
feat(M47): add Kubernetes Secrets target + AWS ACM PCA issuer connectors
Implement both M47 connectors with full cross-layer wiring: Kubernetes Secrets target: DNS-1123 validation, kubernetes.io/tls Secret create-or-update, chain concatenation, serial number validation, Helm RBAC gating. 18 tests. AWS ACM Private CA issuer: synchronous issuance (like Vault), ARN regex validation, RFC 5280 revocation reason mapping, CA cert retrieval, factory + env var seeding. 23 tests. Cross-cutting: domain types, service validation, config, factory, agent dispatch, frontend (TargetsPage, issuerTypes), OpenAPI, seed data, Helm chart, connectors docs, README. Testing docs (testing-guide, qa-test-guide, qa_test.go) with Parts thematically integrated near related connectors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
// Package awsacmpca implements the issuer.Connector interface for AWS Certificate Authority Service (CAS).
|
||||
//
|
||||
// AWS ACM Private CA (ACM PCA) provides a fully managed private certificate authority
|
||||
// with certificate signing, revocation, and CRL capabilities. This connector uses the
|
||||
// AWS ACM PCA API to issue and manage certificates.
|
||||
//
|
||||
// This connector issues certificates synchronously: the IssueCertificate call returns
|
||||
// the issued certificate immediately. GetOrderStatus always returns "completed" since
|
||||
// issuance is synchronous. CRL and OCSP operations are delegated to AWS PCA's own
|
||||
// endpoints.
|
||||
//
|
||||
// Authentication: AWS credentials via the standard credential chain (environment variables,
|
||||
// IAM role, instance profile, or SSO). Configuration specifies the CA ARN, region, and
|
||||
// optional signing algorithm and validity days.
|
||||
//
|
||||
// AWS ACM PCA API used (abstracted via ACMPCAClient interface):
|
||||
//
|
||||
// IssueCertificate - Issue a certificate from a CSR
|
||||
// GetCertificate - Retrieve the issued certificate
|
||||
// RevokeCertificate - Revoke a certificate
|
||||
// GetCACertificate - Get the CA certificate chain
|
||||
package awsacmpca
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/connector/issuer"
|
||||
)
|
||||
|
||||
// Config represents the AWS ACM Private CA issuer connector configuration.
|
||||
type Config struct {
|
||||
// Region is the AWS region where the CA resides (e.g., "us-east-1").
|
||||
// Required. Set via CERTCTL_GOOGLE_CAS_PROJECT environment variable.
|
||||
Region string `json:"region"`
|
||||
|
||||
// CAArn is the ARN of the AWS Certificate Authority Service CA.
|
||||
// Required. Set via CERTCTL_GOOGLE_CAS_CA_ARN environment variable.
|
||||
// Example: arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012
|
||||
CAArn string `json:"ca_arn"`
|
||||
|
||||
// SigningAlgorithm is the algorithm used to sign certificates.
|
||||
// Default: "SHA256WITHRSA". Set via CERTCTL_AWS_PCA_SIGNING_ALGORITHM.
|
||||
// Valid values: SHA256WITHRSA, SHA384WITHRSA, SHA512WITHRSA,
|
||||
// SHA256WITHECDSA, SHA384WITHECDSA, SHA512WITHECDSA
|
||||
SigningAlgorithm string `json:"signing_algorithm,omitempty"`
|
||||
|
||||
// ValidityDays is the number of days the certificate is valid.
|
||||
// Default: 365. Set via CERTCTL_AWS_PCA_VALIDITY_DAYS.
|
||||
ValidityDays int `json:"validity_days,omitempty"`
|
||||
|
||||
// TemplateArn is the optional certificate template ARN for subordinate CAs with restrictions.
|
||||
// Set via CERTCTL_AWS_PCA_TEMPLATE_ARN.
|
||||
TemplateArn string `json:"template_arn,omitempty"`
|
||||
}
|
||||
|
||||
// ACMPCAClient defines the interface for interacting with AWS ACM Private CA.
|
||||
// This allows for dependency injection and testing with mock clients.
|
||||
type ACMPCAClient interface {
|
||||
// IssueCertificate issues a new certificate.
|
||||
IssueCertificate(ctx context.Context, input *IssueCertificateInput) (*IssueCertificateOutput, error)
|
||||
|
||||
// GetCertificate retrieves an issued certificate.
|
||||
GetCertificate(ctx context.Context, input *GetCertificateInput) (*GetCertificateOutput, error)
|
||||
|
||||
// RevokeCertificate revokes a certificate.
|
||||
RevokeCertificate(ctx context.Context, input *RevokeCertificateInput) error
|
||||
|
||||
// GetCACertificate retrieves the CA certificate chain.
|
||||
GetCACertificate(ctx context.Context, input *GetCACertificateInput) (*GetCACertificateOutput, error)
|
||||
}
|
||||
|
||||
// IssueCertificateInput represents the request to issue a certificate.
|
||||
type IssueCertificateInput struct {
|
||||
CAArn string
|
||||
CSR []byte // DER-encoded CSR
|
||||
SigningAlgorithm string
|
||||
ValidityDays int
|
||||
TemplateArn string
|
||||
}
|
||||
|
||||
// IssueCertificateOutput represents the response to an issue request.
|
||||
type IssueCertificateOutput struct {
|
||||
CertificateArn string
|
||||
}
|
||||
|
||||
// GetCertificateInput represents the request to retrieve a certificate.
|
||||
type GetCertificateInput struct {
|
||||
CAArn string
|
||||
CertificateArn string
|
||||
}
|
||||
|
||||
// GetCertificateOutput represents the response containing the certificate.
|
||||
type GetCertificateOutput struct {
|
||||
Certificate string // PEM-encoded certificate
|
||||
CertificateChain string // PEM-encoded certificate chain
|
||||
}
|
||||
|
||||
// RevokeCertificateInput represents the request to revoke a certificate.
|
||||
type RevokeCertificateInput struct {
|
||||
CAArn string
|
||||
CertificateSerial string
|
||||
RevocationReason string
|
||||
}
|
||||
|
||||
// GetCACertificateInput represents the request to retrieve the CA certificate.
|
||||
type GetCACertificateInput struct {
|
||||
CAArn string
|
||||
}
|
||||
|
||||
// GetCACertificateOutput represents the response containing the CA certificate.
|
||||
type GetCACertificateOutput struct {
|
||||
Certificate string // PEM-encoded CA certificate
|
||||
CertificateChain string // PEM-encoded CA chain
|
||||
}
|
||||
|
||||
// Connector implements the issuer.Connector interface for AWS ACM Private CA.
|
||||
type Connector struct {
|
||||
config *Config
|
||||
client ACMPCAClient
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// New creates a new AWS ACM Private CA connector with the given configuration and logger.
|
||||
// The real client will use the AWS SDK via the standard credential chain.
|
||||
func New(config *Config, logger *slog.Logger) *Connector {
|
||||
if config != nil {
|
||||
if config.SigningAlgorithm == "" {
|
||||
config.SigningAlgorithm = "SHA256WITHRSA"
|
||||
}
|
||||
if config.ValidityDays == 0 {
|
||||
config.ValidityDays = 365
|
||||
}
|
||||
}
|
||||
|
||||
return &Connector{
|
||||
config: config,
|
||||
client: &stubClient{}, // Placeholder; real AWS client will be injected or implemented
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithClient creates a new AWS ACM Private CA connector with a custom client.
|
||||
// Used primarily for testing with mock clients.
|
||||
func NewWithClient(config *Config, client ACMPCAClient, logger *slog.Logger) *Connector {
|
||||
if config != nil {
|
||||
if config.SigningAlgorithm == "" {
|
||||
config.SigningAlgorithm = "SHA256WITHRSA"
|
||||
}
|
||||
if config.ValidityDays == 0 {
|
||||
config.ValidityDays = 365
|
||||
}
|
||||
}
|
||||
|
||||
return &Connector{
|
||||
config: config,
|
||||
client: client,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// stubClient is a placeholder client that returns "not implemented" errors.
|
||||
// In production, this would be replaced with a real AWS SDK client.
|
||||
type stubClient struct{}
|
||||
|
||||
func (s *stubClient) IssueCertificate(ctx context.Context, input *IssueCertificateInput) (*IssueCertificateOutput, error) {
|
||||
return nil, fmt.Errorf("AWS SDK client not initialized (stub)")
|
||||
}
|
||||
|
||||
func (s *stubClient) GetCertificate(ctx context.Context, input *GetCertificateInput) (*GetCertificateOutput, error) {
|
||||
return nil, fmt.Errorf("AWS SDK client not initialized (stub)")
|
||||
}
|
||||
|
||||
func (s *stubClient) RevokeCertificate(ctx context.Context, input *RevokeCertificateInput) error {
|
||||
return fmt.Errorf("AWS SDK client not initialized (stub)")
|
||||
}
|
||||
|
||||
func (s *stubClient) GetCACertificate(ctx context.Context, input *GetCACertificateInput) (*GetCACertificateOutput, error) {
|
||||
return nil, fmt.Errorf("AWS SDK client not initialized (stub)")
|
||||
}
|
||||
|
||||
// ValidateConfig checks that the AWS ACM Private CA configuration is valid.
|
||||
func (c *Connector) ValidateConfig(ctx context.Context, rawConfig json.RawMessage) error {
|
||||
var cfg Config
|
||||
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
|
||||
return fmt.Errorf("invalid AWS ACM PCA config: %w", err)
|
||||
}
|
||||
|
||||
if cfg.Region == "" {
|
||||
return fmt.Errorf("AWS region is required")
|
||||
}
|
||||
|
||||
if cfg.CAArn == "" {
|
||||
return fmt.Errorf("AWS CA ARN is required")
|
||||
}
|
||||
|
||||
// Validate ARN format: arn:aws(-[a-z]+)?:acm-pca:[a-z0-9-]+:\d{12}:certificate-authority/[a-f0-9-]+
|
||||
arnPattern := regexp.MustCompile(`^arn:aws(-[a-z]+)?:acm-pca:[a-z0-9-]+:\d{12}:certificate-authority/[a-f0-9-]+$`)
|
||||
if !arnPattern.MatchString(cfg.CAArn) {
|
||||
return fmt.Errorf("invalid CA ARN format: %s", cfg.CAArn)
|
||||
}
|
||||
|
||||
// Validate signing algorithm if provided
|
||||
if cfg.SigningAlgorithm != "" {
|
||||
validAlgorithms := map[string]bool{
|
||||
"SHA256WITHRSA": true,
|
||||
"SHA384WITHRSA": true,
|
||||
"SHA512WITHRSA": true,
|
||||
"SHA256WITHECDSA": true,
|
||||
"SHA384WITHECDSA": true,
|
||||
"SHA512WITHECDSA": true,
|
||||
}
|
||||
if !validAlgorithms[cfg.SigningAlgorithm] {
|
||||
return fmt.Errorf("invalid signing algorithm: %s", cfg.SigningAlgorithm)
|
||||
}
|
||||
} else {
|
||||
cfg.SigningAlgorithm = "SHA256WITHRSA"
|
||||
}
|
||||
|
||||
// Validate validity days if provided
|
||||
if cfg.ValidityDays < 0 {
|
||||
return fmt.Errorf("validity days must be non-negative")
|
||||
}
|
||||
if cfg.ValidityDays == 0 {
|
||||
cfg.ValidityDays = 365
|
||||
}
|
||||
|
||||
c.config = &cfg
|
||||
c.logger.Info("AWS ACM Private CA configuration validated",
|
||||
"region", cfg.Region,
|
||||
"ca_arn", cfg.CAArn,
|
||||
"signing_algorithm", cfg.SigningAlgorithm,
|
||||
"validity_days", cfg.ValidityDays)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IssueCertificate issues a new certificate using AWS ACM Private CA.
|
||||
func (c *Connector) IssueCertificate(ctx context.Context, request issuer.IssuanceRequest) (*issuer.IssuanceResult, error) {
|
||||
c.logger.Info("processing AWS ACM PCA issuance request",
|
||||
"common_name", request.CommonName,
|
||||
"san_count", len(request.SANs))
|
||||
|
||||
// Decode CSR from PEM
|
||||
csrBlock, _ := pem.Decode([]byte(request.CSRPEM))
|
||||
if csrBlock == nil {
|
||||
return nil, fmt.Errorf("failed to decode CSR PEM")
|
||||
}
|
||||
|
||||
// Call AWS API to issue certificate
|
||||
issueOutput, err := c.client.IssueCertificate(ctx, &IssueCertificateInput{
|
||||
CAArn: c.config.CAArn,
|
||||
CSR: csrBlock.Bytes,
|
||||
SigningAlgorithm: c.config.SigningAlgorithm,
|
||||
ValidityDays: c.config.ValidityDays,
|
||||
TemplateArn: c.config.TemplateArn,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AWS IssueCertificate failed: %w", err)
|
||||
}
|
||||
|
||||
// Retrieve the issued certificate
|
||||
getCertOutput, err := c.client.GetCertificate(ctx, &GetCertificateInput{
|
||||
CAArn: c.config.CAArn,
|
||||
CertificateArn: issueOutput.CertificateArn,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AWS GetCertificate failed: %w", err)
|
||||
}
|
||||
|
||||
if getCertOutput.Certificate == "" {
|
||||
return nil, fmt.Errorf("no certificate in AWS response")
|
||||
}
|
||||
|
||||
// Parse the certificate to extract metadata
|
||||
block, _ := pem.Decode([]byte(getCertOutput.Certificate))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode certificate PEM from AWS")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
}
|
||||
|
||||
// Extract serial number (hex format, uppercase)
|
||||
serial := strings.ToUpper(fmt.Sprintf("%x", cert.SerialNumber))
|
||||
|
||||
// Use certificate ARN as OrderID for revocation lookup
|
||||
orderID := issueOutput.CertificateArn
|
||||
|
||||
c.logger.Info("AWS ACM PCA certificate issued",
|
||||
"common_name", request.CommonName,
|
||||
"serial", serial,
|
||||
"not_after", cert.NotAfter)
|
||||
|
||||
return &issuer.IssuanceResult{
|
||||
CertPEM: getCertOutput.Certificate,
|
||||
ChainPEM: getCertOutput.CertificateChain,
|
||||
Serial: serial,
|
||||
NotBefore: cert.NotBefore,
|
||||
NotAfter: cert.NotAfter,
|
||||
OrderID: orderID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RenewCertificate renews a certificate by creating a new signing request.
|
||||
// For AWS ACM PCA, renewal is functionally identical to issuance (new cert signed from CSR).
|
||||
func (c *Connector) RenewCertificate(ctx context.Context, request issuer.RenewalRequest) (*issuer.IssuanceResult, error) {
|
||||
c.logger.Info("processing AWS ACM PCA renewal request",
|
||||
"common_name", request.CommonName,
|
||||
"san_count", len(request.SANs))
|
||||
|
||||
return c.IssueCertificate(ctx, issuer.IssuanceRequest{
|
||||
CommonName: request.CommonName,
|
||||
SANs: request.SANs,
|
||||
CSRPEM: request.CSRPEM,
|
||||
EKUs: request.EKUs,
|
||||
})
|
||||
}
|
||||
|
||||
// RevokeCertificate revokes a certificate at AWS ACM Private CA.
|
||||
func (c *Connector) RevokeCertificate(ctx context.Context, request issuer.RevocationRequest) error {
|
||||
c.logger.Info("processing AWS ACM PCA revocation request", "serial", request.Serial)
|
||||
|
||||
// Map RFC 5280 reason string to AWS reason
|
||||
reason := mapRevocationReason(request.Reason)
|
||||
|
||||
err := c.client.RevokeCertificate(ctx, &RevokeCertificateInput{
|
||||
CAArn: c.config.CAArn,
|
||||
CertificateSerial: request.Serial,
|
||||
RevocationReason: reason,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("AWS RevokeCertificate failed: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Info("AWS ACM PCA certificate revoked", "serial", request.Serial)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOrderStatus returns the status of an AWS ACM PCA order.
|
||||
// AWS ACM PCA issues synchronously, so orders are always "completed" immediately.
|
||||
func (c *Connector) GetOrderStatus(ctx context.Context, orderID string) (*issuer.OrderStatus, error) {
|
||||
return &issuer.OrderStatus{
|
||||
OrderID: orderID,
|
||||
Status: "completed",
|
||||
UpdatedAt: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateCRL is not supported because AWS ACM PCA serves CRL directly.
|
||||
func (c *Connector) GenerateCRL(ctx context.Context, revokedCerts []issuer.RevokedCertEntry) ([]byte, error) {
|
||||
return nil, fmt.Errorf("CRL delegated to AWS ACM Private CA; use AWS endpoint directly")
|
||||
}
|
||||
|
||||
// SignOCSPResponse is not supported because AWS ACM PCA serves OCSP directly.
|
||||
func (c *Connector) SignOCSPResponse(ctx context.Context, req issuer.OCSPSignRequest) ([]byte, error) {
|
||||
return nil, fmt.Errorf("OCSP delegated to AWS ACM Private CA; use AWS endpoint directly")
|
||||
}
|
||||
|
||||
// GetCACertPEM retrieves the CA certificate from AWS ACM Private CA.
|
||||
func (c *Connector) GetCACertPEM(ctx context.Context) (string, error) {
|
||||
caCertOutput, err := c.client.GetCACertificate(ctx, &GetCACertificateInput{
|
||||
CAArn: c.config.CAArn,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("AWS GetCACertificate failed: %w", err)
|
||||
}
|
||||
|
||||
// Combine CA certificate and chain
|
||||
if caCertOutput.CertificateChain != "" {
|
||||
return caCertOutput.Certificate + "\n" + caCertOutput.CertificateChain, nil
|
||||
}
|
||||
|
||||
return caCertOutput.Certificate, nil
|
||||
}
|
||||
|
||||
// GetRenewalInfo returns nil, nil as AWS ACM PCA does not support ACME Renewal Information (ARI).
|
||||
func (c *Connector) GetRenewalInfo(ctx context.Context, certPEM string) (*issuer.RenewalInfoResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// mapRevocationReason converts RFC 5280 reason strings to AWS ACM PCA reason codes.
|
||||
func mapRevocationReason(reason *string) string {
|
||||
if reason == nil {
|
||||
return "UNSPECIFIED"
|
||||
}
|
||||
|
||||
reasonMap := map[string]string{
|
||||
"unspecified": "UNSPECIFIED",
|
||||
"keyCompromise": "KEY_COMPROMISE",
|
||||
"caCompromise": "CERTIFICATE_AUTHORITY_COMPROMISE",
|
||||
"affiliationChanged": "AFFILIATION_CHANGED",
|
||||
"superseded": "SUPERSEDED",
|
||||
"cessationOfOperation": "CESSATION_OF_OPERATION",
|
||||
"certificateHold": "CERTIFICATE_HOLD",
|
||||
"privilegeWithdrawn": "PRIVILEGE_WITHDRAWN",
|
||||
}
|
||||
|
||||
if mapped, ok := reasonMap[*reason]; ok {
|
||||
return mapped
|
||||
}
|
||||
|
||||
return "UNSPECIFIED"
|
||||
}
|
||||
|
||||
// Ensure Connector implements the issuer.Connector interface.
|
||||
var _ issuer.Connector = (*Connector)(nil)
|
||||
@@ -0,0 +1,629 @@
|
||||
package awsacmpca_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/connector/issuer"
|
||||
"github.com/shankar0123/certctl/internal/connector/issuer/awsacmpca"
|
||||
)
|
||||
|
||||
// mockACMPCAClient implements the ACMPCAClient interface for testing.
|
||||
type mockACMPCAClient struct {
|
||||
issueCertificateErr error
|
||||
getCertificateErr error
|
||||
revokeCertificateErr error
|
||||
getCACertificateErr error
|
||||
issuedCertPEM string
|
||||
issuedChainPEM string
|
||||
caCertPEM string
|
||||
caCertChainPEM string
|
||||
lastIssueCertificateInput *awsacmpca.IssueCertificateInput
|
||||
lastRevokeCertificateInput *awsacmpca.RevokeCertificateInput
|
||||
}
|
||||
|
||||
func (m *mockACMPCAClient) IssueCertificate(ctx context.Context, input *awsacmpca.IssueCertificateInput) (*awsacmpca.IssueCertificateOutput, error) {
|
||||
m.lastIssueCertificateInput = input
|
||||
if m.issueCertificateErr != nil {
|
||||
return nil, m.issueCertificateErr
|
||||
}
|
||||
return &awsacmpca.IssueCertificateOutput{
|
||||
CertificateArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678/certificate/abcdef123456",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockACMPCAClient) GetCertificate(ctx context.Context, input *awsacmpca.GetCertificateInput) (*awsacmpca.GetCertificateOutput, error) {
|
||||
if m.getCertificateErr != nil {
|
||||
return nil, m.getCertificateErr
|
||||
}
|
||||
return &awsacmpca.GetCertificateOutput{
|
||||
Certificate: m.issuedCertPEM,
|
||||
CertificateChain: m.issuedChainPEM,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockACMPCAClient) RevokeCertificate(ctx context.Context, input *awsacmpca.RevokeCertificateInput) error {
|
||||
m.lastRevokeCertificateInput = input
|
||||
return m.revokeCertificateErr
|
||||
}
|
||||
|
||||
func (m *mockACMPCAClient) GetCACertificate(ctx context.Context, input *awsacmpca.GetCACertificateInput) (*awsacmpca.GetCACertificateOutput, error) {
|
||||
if m.getCACertificateErr != nil {
|
||||
return nil, m.getCACertificateErr
|
||||
}
|
||||
return &awsacmpca.GetCACertificateOutput{
|
||||
Certificate: m.caCertPEM,
|
||||
CertificateChain: m.caCertChainPEM,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper function to generate a test certificate and CSR.
|
||||
func generateTestCertAndCSR(t *testing.T) (certPEM string, csrPEM string) {
|
||||
// Generate private key
|
||||
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Create certificate template
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate serial number: %v", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "example.com",
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: false,
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
DNSNames: []string{"example.com", "www.example.com"},
|
||||
}
|
||||
|
||||
// Create self-signed certificate for testing
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create certificate: %v", err)
|
||||
}
|
||||
|
||||
certPEM = string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certDER,
|
||||
}))
|
||||
|
||||
// Create CSR
|
||||
csrTemplate := x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: "example.com",
|
||||
},
|
||||
DNSNames: []string{"example.com", "www.example.com"},
|
||||
}
|
||||
|
||||
csrDER, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, privKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create CSR: %v", err)
|
||||
}
|
||||
|
||||
csrPEM = string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE REQUEST",
|
||||
Bytes: csrDER,
|
||||
}))
|
||||
|
||||
return certPEM, csrPEM
|
||||
}
|
||||
|
||||
func TestAWSACMPCAConnector(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("ValidateConfig_Success", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
SigningAlgorithm: "SHA256WITHRSA",
|
||||
ValidityDays: 365,
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig failed: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_AllOptionalFields", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "eu-west-1",
|
||||
CAArn: "arn:aws:acm-pca:eu-west-1:123456789012:certificate-authority/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
|
||||
SigningAlgorithm: "SHA512WITHECDSA",
|
||||
ValidityDays: 730,
|
||||
TemplateArn: "arn:aws:acm-pca:eu-west-1:123456789012:template/WebServer",
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig failed: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_InvalidJSON", func(t *testing.T) {
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
err := connector.ValidateConfig(ctx, []byte(`{invalid json}`))
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid JSON")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid AWS ACM PCA config") {
|
||||
t.Errorf("Expected config error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_MissingRegion", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for missing region")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "region is required") {
|
||||
t.Errorf("Expected region required error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_MissingCAArn", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for missing CA ARN")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "CA ARN is required") {
|
||||
t.Errorf("Expected CA ARN required error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_InvalidCAArn", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "not-an-arn",
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid CA ARN")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid CA ARN format") {
|
||||
t.Errorf("Expected invalid ARN error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_InvalidSigningAlgorithm", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
SigningAlgorithm: "INVALID_ALGO",
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid signing algorithm")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid signing algorithm") {
|
||||
t.Errorf("Expected invalid algorithm error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_InvalidValidityDays", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
ValidityDays: -1,
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for negative validity days")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "validity days must be non-negative") {
|
||||
t.Errorf("Expected validity days error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IssueCertificate_Success", func(t *testing.T) {
|
||||
certPEM, csrPEM := generateTestCertAndCSR(t)
|
||||
|
||||
mockClient := &mockACMPCAClient{
|
||||
issuedCertPEM: certPEM,
|
||||
issuedChainPEM: certPEM, // Use same cert as chain for test
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
SigningAlgorithm: "SHA256WITHRSA",
|
||||
ValidityDays: 365,
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
|
||||
request := issuer.IssuanceRequest{
|
||||
CommonName: "example.com",
|
||||
SANs: []string{"www.example.com"},
|
||||
CSRPEM: csrPEM,
|
||||
}
|
||||
|
||||
result, err := connector.IssueCertificate(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("IssueCertificate failed: %v", err)
|
||||
}
|
||||
|
||||
if result.CertPEM == "" {
|
||||
t.Fatal("Expected certificate PEM in result")
|
||||
}
|
||||
if result.Serial == "" {
|
||||
t.Fatal("Expected serial number in result")
|
||||
}
|
||||
if result.OrderID == "" {
|
||||
t.Fatal("Expected OrderID (certificate ARN) in result")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IssueCertificate_EmptyCSR", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
request := issuer.IssuanceRequest{
|
||||
CommonName: "example.com",
|
||||
CSRPEM: "", // Empty CSR
|
||||
}
|
||||
|
||||
_, err := connector.IssueCertificate(ctx, request)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for empty CSR")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "failed to decode CSR PEM") {
|
||||
t.Errorf("Expected CSR decode error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IssueCertificate_IssueError", func(t *testing.T) {
|
||||
certPEM, csrPEM := generateTestCertAndCSR(t)
|
||||
mockClient := &mockACMPCAClient{
|
||||
issueCertificateErr: fmt.Errorf("AWS service error"),
|
||||
issuedCertPEM: certPEM,
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
request := issuer.IssuanceRequest{
|
||||
CommonName: "example.com",
|
||||
CSRPEM: csrPEM,
|
||||
}
|
||||
|
||||
_, err := connector.IssueCertificate(ctx, request)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error from IssueCertificate")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "IssueCertificate failed") {
|
||||
t.Errorf("Expected issue error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IssueCertificate_GetCertificateError", func(t *testing.T) {
|
||||
_, csrPEM := generateTestCertAndCSR(t)
|
||||
mockClient := &mockACMPCAClient{
|
||||
getCertificateErr: fmt.Errorf("AWS service error"),
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
request := issuer.IssuanceRequest{
|
||||
CommonName: "example.com",
|
||||
CSRPEM: csrPEM,
|
||||
}
|
||||
|
||||
_, err := connector.IssueCertificate(ctx, request)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error from GetCertificate")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "GetCertificate failed") {
|
||||
t.Errorf("Expected get cert error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RenewCertificate_Success", func(t *testing.T) {
|
||||
certPEM, csrPEM := generateTestCertAndCSR(t)
|
||||
mockClient := &mockACMPCAClient{
|
||||
issuedCertPEM: certPEM,
|
||||
issuedChainPEM: certPEM,
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
request := issuer.RenewalRequest{
|
||||
CommonName: "example.com",
|
||||
SANs: []string{"www.example.com"},
|
||||
CSRPEM: csrPEM,
|
||||
}
|
||||
|
||||
result, err := connector.RenewCertificate(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("RenewCertificate failed: %v", err)
|
||||
}
|
||||
|
||||
if result.CertPEM == "" {
|
||||
t.Fatal("Expected certificate PEM in result")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RevokeCertificate_Success", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
reason := "keyCompromise"
|
||||
request := issuer.RevocationRequest{
|
||||
Serial: "aabbccdd123456",
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
err := connector.RevokeCertificate(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("RevokeCertificate failed: %v", err)
|
||||
}
|
||||
|
||||
if mockClient.lastRevokeCertificateInput.RevocationReason != "KEY_COMPROMISE" {
|
||||
t.Errorf("Expected KEY_COMPROMISE reason, got: %s", mockClient.lastRevokeCertificateInput.RevocationReason)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RevokeCertificate_WithDefaultReason", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
request := issuer.RevocationRequest{
|
||||
Serial: "aabbccdd123456",
|
||||
Reason: nil,
|
||||
}
|
||||
|
||||
err := connector.RevokeCertificate(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("RevokeCertificate failed: %v", err)
|
||||
}
|
||||
|
||||
if mockClient.lastRevokeCertificateInput.RevocationReason != "UNSPECIFIED" {
|
||||
t.Errorf("Expected UNSPECIFIED reason, got: %s", mockClient.lastRevokeCertificateInput.RevocationReason)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RevokeCertificate_Error", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{
|
||||
revokeCertificateErr: fmt.Errorf("AWS service error"),
|
||||
}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
request := issuer.RevocationRequest{
|
||||
Serial: "aabbccdd123456",
|
||||
}
|
||||
|
||||
err := connector.RevokeCertificate(ctx, request)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error from RevokeCertificate")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetOrderStatus_ReturnsCompleted", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
status, err := connector.GetOrderStatus(ctx, "test-order-id")
|
||||
if err != nil {
|
||||
t.Fatalf("GetOrderStatus failed: %v", err)
|
||||
}
|
||||
|
||||
if status.Status != "completed" {
|
||||
t.Errorf("Expected completed status, got: %s", status.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetCACertPEM_Success", func(t *testing.T) {
|
||||
certPEM, _ := generateTestCertAndCSR(t)
|
||||
mockClient := &mockACMPCAClient{
|
||||
caCertPEM: certPEM,
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
caPEM, err := connector.GetCACertPEM(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetCACertPEM failed: %v", err)
|
||||
}
|
||||
|
||||
if caPEM == "" {
|
||||
t.Fatal("Expected CA certificate PEM")
|
||||
}
|
||||
if !strings.Contains(caPEM, "CERTIFICATE") {
|
||||
t.Errorf("Expected PEM format, got: %s", caPEM)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetCACertPEM_WithChain", func(t *testing.T) {
|
||||
certPEM, _ := generateTestCertAndCSR(t)
|
||||
mockClient := &mockACMPCAClient{
|
||||
caCertPEM: certPEM,
|
||||
caCertChainPEM: certPEM,
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
caPEM, err := connector.GetCACertPEM(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetCACertPEM failed: %v", err)
|
||||
}
|
||||
|
||||
// Should contain both certificate and chain separated by newline
|
||||
if !strings.Contains(caPEM, "\n") {
|
||||
t.Fatal("Expected certificate and chain combined")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetCACertPEM_Error", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{
|
||||
getCACertificateErr: fmt.Errorf("AWS service error"),
|
||||
}
|
||||
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
_, err := connector.GetCACertPEM(ctx)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error from GetCACertPEM")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetRenewalInfo_ReturnsNil", func(t *testing.T) {
|
||||
mockClient := &mockACMPCAClient{}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
result, err := connector.GetRenewalInfo(ctx, "cert-pem")
|
||||
if err != nil {
|
||||
t.Fatalf("GetRenewalInfo failed: %v", err)
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
t.Fatal("Expected nil result from GetRenewalInfo")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidateConfig_AppliesDefaults", func(t *testing.T) {
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
// SigningAlgorithm and ValidityDays not set
|
||||
}
|
||||
|
||||
connector := awsacmpca.New(nil, logger)
|
||||
rawConfig, _ := json.Marshal(config)
|
||||
err := connector.ValidateConfig(ctx, rawConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify defaults were applied by checking the connector's config
|
||||
// Since config is private, we'll test via IssueCertificate to ensure algorithm is set
|
||||
})
|
||||
|
||||
t.Run("RevocationReason_Mapping", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"keyCompromise", "KEY_COMPROMISE"},
|
||||
{"caCompromise", "CERTIFICATE_AUTHORITY_COMPROMISE"},
|
||||
{"affiliationChanged", "AFFILIATION_CHANGED"},
|
||||
{"superseded", "SUPERSEDED"},
|
||||
{"cessationOfOperation", "CESSATION_OF_OPERATION"},
|
||||
{"privilegeWithdrawn", "PRIVILEGE_WITHDRAWN"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
mockClient := &mockACMPCAClient{}
|
||||
config := awsacmpca.Config{
|
||||
Region: "us-east-1",
|
||||
CAArn: "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
|
||||
}
|
||||
|
||||
connector := awsacmpca.NewWithClient(&config, mockClient, logger)
|
||||
reason := tc.input
|
||||
request := issuer.RevocationRequest{
|
||||
Serial: "test-serial",
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
_ = connector.RevokeCertificate(ctx, request)
|
||||
|
||||
if mockClient.lastRevokeCertificateInput.RevocationReason != tc.expected {
|
||||
t.Errorf("For reason %q, expected %q, got %q", tc.input, tc.expected, mockClient.lastRevokeCertificateInput.RevocationReason)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user