diff --git a/internal/certacme/client_obtain.go b/internal/certacme/client_obtain.go index 61907e18..80c3c641 100644 --- a/internal/certacme/client_obtain.go +++ b/internal/certacme/client_obtain.go @@ -59,6 +59,7 @@ type ObtainCertificateRequest struct { } type ObtainCertificateResponse struct { + CAProvider domain.CAProviderType CSR string FullChainCertificate string IssuerCertificate string @@ -183,6 +184,7 @@ func (c *ACMEClient) ObtainCertificate(ctx context.Context, request *ObtainCerti } return &ObtainCertificateResponse{ + CAProvider: domain.CAProviderType(c.account.CA), CSR: strings.TrimSpace(string(resp.CSR)), FullChainCertificate: strings.TrimSpace(string(resp.Certificate)), IssuerCertificate: strings.TrimSpace(string(resp.IssuerCertificate)), diff --git a/internal/certificate/service.go b/internal/certificate/service.go index 7403e0e3..86b6e740 100644 --- a/internal/certificate/service.go +++ b/internal/certificate/service.go @@ -235,7 +235,7 @@ func (s *CertificateService) RevokeCertificate(ctx context.Context, req *dtos.Ce return nil, fmt.Errorf("could not revoke a certificate which is already revoked") } - acmeAccount, err := s.acmeAccountRepo.GetByAcctUrl(ctx, certificate.ACMEAcctUrl) + acmeAccount, err := s.acmeAccountRepo.GetByCAAndAcctUrl(ctx, certificate.CA, certificate.ACMEAcctUrl) if err != nil { return nil, fmt.Errorf("failed to revoke certificate: could not find acme account: %w", err) } diff --git a/internal/certificate/service_deps.go b/internal/certificate/service_deps.go index 2ce80994..3c91e877 100644 --- a/internal/certificate/service_deps.go +++ b/internal/certificate/service_deps.go @@ -9,7 +9,7 @@ import ( ) type acmeAccountRepository interface { - GetByAcctUrl(ctx context.Context, acctUrl string) (*domain.ACMEAccount, error) + GetByCAAndAcctUrl(ctx context.Context, ca string, acctUrl string) (*domain.ACMEAccount, error) } type certificateRepository interface { diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index f61cd399..7aba077c 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -31,6 +31,7 @@ type Certificate struct { ValidityNotBefore time.Time `db:"validityNotBefore" json:"validityNotBefore"` ValidityNotAfter time.Time `db:"validityNotAfter" json:"validityNotAfter"` ValidityInterval int32 `db:"validityInterval" json:"validityInterval"` + CA string `db:"ca" json:"ca"` ACMEAcctUrl string `db:"acmeAcctUrl" json:"acmeAcctUrl"` ACMECertUrl string `db:"acmeCertUrl" json:"acmeCertUrl"` IsRenewed bool `db:"isRenewed" json:"isRenewed"` diff --git a/internal/repository/acme_account.go b/internal/repository/acme_account.go index 8e0f9444..234c3454 100644 --- a/internal/repository/acme_account.go +++ b/internal/repository/acme_account.go @@ -36,11 +36,11 @@ func (r *ACMEAccountRepository) GetByCAAndEmail(ctx context.Context, ca, caDirUr return r.castRecordToModel(record) } -func (r *ACMEAccountRepository) GetByAcctUrl(ctx context.Context, acctUrl string) (*domain.ACMEAccount, error) { +func (r *ACMEAccountRepository) GetByCAAndAcctUrl(ctx context.Context, ca string, acctUrl string) (*domain.ACMEAccount, error) { record, err := app.GetApp().FindFirstRecordByFilter( domain.CollectionNameACMEAccount, - "acmeAcctUrl={:acmeAcctUrl}", - dbx.Params{"acmeAcctUrl": acctUrl}, + "ca={:ca} && acmeAcctUrl={:acmeAcctUrl}", + dbx.Params{"ca": ca, "acmeAcctUrl": acctUrl}, ) if err != nil { if errors.Is(err, sql.ErrNoRows) { diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go index 57f687d9..7accdcaa 100644 --- a/internal/repository/certificate.go +++ b/internal/repository/certificate.go @@ -107,6 +107,7 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce record.Set("validityNotBefore", certificate.ValidityNotBefore) record.Set("validityNotAfter", certificate.ValidityNotAfter) record.Set("validityInterval", certificate.ValidityInterval) + record.Set("ca", certificate.CA) record.Set("acmeAcctUrl", certificate.ACMEAcctUrl) record.Set("acmeCertUrl", certificate.ACMECertUrl) record.Set("isRenewed", certificate.IsRenewed) @@ -172,6 +173,7 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain. ValidityNotBefore: record.GetDateTime("validityNotBefore").Time(), ValidityNotAfter: record.GetDateTime("validityNotAfter").Time(), ValidityInterval: int32(record.GetInt("validityInterval")), + CA: record.GetString("ca"), ACMEAcctUrl: record.GetString("acmeAcctUrl"), ACMECertUrl: record.GetString("acmeCertUrl"), IsRenewed: record.GetBool("isRenewed"), diff --git a/internal/workflow/engine/executor_bizapply.go b/internal/workflow/engine/executor_bizapply.go index f410e5cf..5ea0cc75 100644 --- a/internal/workflow/engine/executor_bizapply.go +++ b/internal/workflow/engine/executor_bizapply.go @@ -109,6 +109,7 @@ func (ne *bizApplyNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExe Certificate: obtainResp.FullChainCertificate, PrivateKey: obtainResp.PrivateKey, IssuerCertificate: obtainResp.IssuerCertificate, + CA: obtainResp.CAProvider.String(), ACMEAcctUrl: obtainResp.ACMEAcctUrl, ACMECertUrl: obtainResp.ACMECertUrl, WorkflowId: execCtx.WorkflowId, diff --git a/migrations/1780704000_upgrade_v0.4.25.go b/migrations/1780934400_upgrade_v0.4.25.go similarity index 75% rename from migrations/1780704000_upgrade_v0.4.25.go rename to migrations/1780934400_upgrade_v0.4.25.go index 1842ed3b..c92cb91d 100644 --- a/migrations/1780704000_upgrade_v0.4.25.go +++ b/migrations/1780934400_upgrade_v0.4.25.go @@ -2,7 +2,9 @@ package migrations import ( "errors" + "strings" + "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" m "github.com/pocketbase/pocketbase/migrations" @@ -93,6 +95,7 @@ func init() { // - add field `subjectName` // - add field `issuerName` // - add field `validationPolicy` + // - add field `ca` { collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") if err != nil { @@ -207,6 +210,24 @@ func init() { return err } + if err := collection.Fields.AddMarshaledJSONAt(15, []byte(`{ + "autogeneratePattern": "", + "help": "", + "hidden": false, + "id": "text4045880084", + "max": 0, + "min": 0, + "name": "ca", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { return err } @@ -221,7 +242,12 @@ func init() { for _, record := range records { changed := false - if certX509, err := xcert.ParseCertificateFromPEM(record.GetString("certificate")); err == nil { + if record.GetString("certificate") != "" { + certX509, err := xcert.ParseCertificateFromPEM(record.GetString("certificate")) + if err != nil { + continue + } + record.Set("subjectName", certX509.Subject.CommonName) record.Set("issuerName", certX509.Issuer.CommonName) @@ -241,6 +267,35 @@ func init() { changed = true } + if record.GetString("acmeAcctUrl") != "" { + acmeAcctUrl := record.GetString("acmeAcctUrl") + acmeAcctRecord, err := app.FindFirstRecordByFilter("acme_accounts", "acmeAcctUrl={:acmeAcctUrl}", dbx.Params{"acmeAcctUrl": acmeAcctUrl}) + if err != nil { + mapping := map[string]string{ + "https://acme-v02.api.letsencrypt.org/": "letsencrypt", + "https://acme-staging-v02.api.letsencrypt.org/": "letsencryptstaging", + "https://acme-api.actalis.com/": "actalisssl", + "https://acme.digicert.com/": "digicert", + "https://emea.acme.atlas.globalsign.com/": "globalsignatlas", + "https://dv.acme-v02.api.pki.goog/": "googletrustservices", + "https://acme.litessl.com/acme/v2/": "litessl", + "https://acme.ssl.com/": "sslcom", + "https://acme.sectigo.com/": "sectigo", + "https://acme.zerossl.com/": "zerossl", + } + for url, ca := range mapping { + if strings.HasPrefix(acmeAcctUrl, url) { + record.Set("ca", ca) + changed = true + break + } + } + } else { + record.Set("ca", acmeAcctRecord.GetString("ca")) + changed = true + } + } + if changed { if err := app.Save(record); err != nil { return err