Files
certctl/internal/connector/issuerfactory/factory.go
T
shankar0123 3f619bcaac feat(M49): Entrust, GlobalSign & EJBCA issuer connectors
Add three new issuer connectors completing commercial and open-source CA
coverage. Entrust uses mTLS client certificate auth with sync/async
issuance. GlobalSign Atlas uses mTLS + API key/secret dual auth with
serial-based tracking. EJBCA supports dual auth (mTLS or OAuth2) for
self-hosted Keyfactor CAs.

Each connector implements the full issuer.Connector interface (9 methods),
includes httptest-based unit tests (~14 each), and follows established
patterns (injectable HTTP clients, RFC 5280 revocation reason mapping,
CRL/OCSP delegated to CA).

Also includes: issuer factory cases, env var seeding, config structs,
domain types, seed data (3 rows, all disabled), OpenAPI enum updates,
frontend issuer catalog entries with config fields, and full docs
(connectors.md, architecture.md, features.md, README).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 22:24:12 -04:00

120 lines
3.9 KiB
Go

package issuerfactory
import (
"encoding/json"
"fmt"
"log/slog"
"github.com/shankar0123/certctl/internal/connector/issuer"
"github.com/shankar0123/certctl/internal/connector/issuer/acme"
"github.com/shankar0123/certctl/internal/connector/issuer/awsacmpca"
"github.com/shankar0123/certctl/internal/connector/issuer/digicert"
"github.com/shankar0123/certctl/internal/connector/issuer/ejbca"
"github.com/shankar0123/certctl/internal/connector/issuer/entrust"
"github.com/shankar0123/certctl/internal/connector/issuer/globalsign"
"github.com/shankar0123/certctl/internal/connector/issuer/googlecas"
"github.com/shankar0123/certctl/internal/connector/issuer/local"
"github.com/shankar0123/certctl/internal/connector/issuer/openssl"
"github.com/shankar0123/certctl/internal/connector/issuer/sectigo"
"github.com/shankar0123/certctl/internal/connector/issuer/stepca"
"github.com/shankar0123/certctl/internal/connector/issuer/vault"
)
// NewFromConfig instantiates an issuer connector from its type string and config JSON.
// The config JSON keys use snake_case matching the connector Config struct json tags.
// This replaces the manual wiring in cmd/server/main.go.
func NewFromConfig(issuerType string, configJSON json.RawMessage, logger *slog.Logger) (issuer.Connector, error) {
if len(configJSON) == 0 {
configJSON = []byte("{}")
}
switch issuerType {
case "local", "GenericCA":
var cfg local.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid Local CA config: %w", err)
}
return local.New(&cfg, logger), nil
case "ACME":
var cfg acme.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid ACME config: %w", err)
}
return acme.New(&cfg, logger), nil
case "StepCA":
var cfg stepca.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid step-ca config: %w", err)
}
return stepca.New(&cfg, logger), nil
case "OpenSSL":
var cfg openssl.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid OpenSSL config: %w", err)
}
return openssl.New(&cfg, logger), nil
case "VaultPKI":
var cfg vault.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid Vault PKI config: %w", err)
}
return vault.New(&cfg, logger), nil
case "DigiCert":
var cfg digicert.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid DigiCert config: %w", err)
}
return digicert.New(&cfg, logger), nil
case "Sectigo":
var cfg sectigo.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid Sectigo config: %w", err)
}
return sectigo.New(&cfg, logger), nil
case "GoogleCAS":
var cfg googlecas.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid Google CAS config: %w", err)
}
return googlecas.New(&cfg, logger), nil
case "AWSACMPCA":
var cfg awsacmpca.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid AWS ACM PCA config: %w", err)
}
return awsacmpca.New(&cfg, logger), nil
case "Entrust":
var cfg entrust.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid Entrust config: %w", err)
}
return entrust.New(&cfg, logger), nil
case "GlobalSign":
var cfg globalsign.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid GlobalSign config: %w", err)
}
return globalsign.New(&cfg, logger), nil
case "EJBCA":
var cfg ejbca.Config
if err := json.Unmarshal(configJSON, &cfg); err != nil {
return nil, fmt.Errorf("invalid EJBCA config: %w", err)
}
return ejbca.New(&cfg, logger), nil
default:
return nil, fmt.Errorf("unknown issuer type: %q", issuerType)
}
}