feat: implement certificate authority service
- Add CertificateAuthorityService for CA management - Support creation of root and intermediate CAs - Implement CA certificate generation with proper X.509 attributes - Add CA validation and verification functionality - Include comprehensive test coverage for CA operations - Support multiple CA types and configurations - Add proper error handling and logging
This commit is contained in:
637
certificate/authority.go
Normal file
637
certificate/authority.go
Normal file
@@ -0,0 +1,637 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.secnex.io/secnex/certman/certificate/utils"
|
||||
"git.secnex.io/secnex/certman/models"
|
||||
"git.secnex.io/secnex/certman/repositories"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CertificateAuthorityService handles certificate authority operations
|
||||
type CertificateAuthorityService struct {
|
||||
caRepo *repositories.CertificateAuthorityRepository
|
||||
orgRepo *repositories.OrganizationRepository
|
||||
certDir string
|
||||
privateDir string
|
||||
}
|
||||
|
||||
// NewCertificateAuthorityService creates a new certificate authority service
|
||||
func NewCertificateAuthorityService(
|
||||
db *gorm.DB,
|
||||
certDir string,
|
||||
privateDir string,
|
||||
) *CertificateAuthorityService {
|
||||
return &CertificateAuthorityService{
|
||||
caRepo: repositories.NewCertificateAuthorityRepository(db),
|
||||
orgRepo: repositories.NewOrganizationRepository(db),
|
||||
certDir: certDir,
|
||||
privateDir: privateDir,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRootCA creates a new root certificate authority
|
||||
func (s *CertificateAuthorityService) CreateRootCA(req *CreateRootCARequest) (*models.CertificateAuthority, error) {
|
||||
// Validate request
|
||||
if err := s.validateRootCARequest(req); err != nil {
|
||||
return nil, fmt.Errorf("invalid request: %w", err)
|
||||
}
|
||||
|
||||
// Check if organization exists
|
||||
org, err := s.orgRepo.GetByID(req.OrganizationID.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("organization not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if root CA already exists for this organization
|
||||
existingCAs, err := s.caRepo.GetAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check existing CAs: %w", err)
|
||||
}
|
||||
|
||||
for _, ca := range existingCAs {
|
||||
if ca.Root && ca.OrganizationID == req.OrganizationID {
|
||||
return nil, fmt.Errorf("root CA already exists for organization %s", org.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Create certificate configuration
|
||||
var config *utils.CertificateConfig
|
||||
if req.ValidityYears > 0 {
|
||||
config = utils.CACertificateConfigWithValidity(true, req.ValidityYears) // true = isRoot
|
||||
} else {
|
||||
config = utils.CACertificateConfig(true) // true = isRoot
|
||||
}
|
||||
config.CommonName = req.CommonName
|
||||
config.Organization = org.Name
|
||||
config.OrganizationalUnit = req.OrganizationalUnit
|
||||
config.Country = req.Country
|
||||
config.State = req.State
|
||||
config.Locality = req.Locality
|
||||
config.Street = req.Street
|
||||
config.PostalCode = req.PostalCode
|
||||
config.Email = req.Email
|
||||
|
||||
// Validate configuration
|
||||
if err := utils.ValidateCertificateConfig(config); err != nil {
|
||||
return nil, fmt.Errorf("invalid certificate configuration: %w", err)
|
||||
}
|
||||
|
||||
// Generate certificate and private key
|
||||
generator := utils.NewCertificateGenerator(config)
|
||||
cert, privateKey, err := generator.GenerateSelfSignedCertificate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate root CA certificate: %w", err)
|
||||
}
|
||||
|
||||
// Save certificate and private key to files
|
||||
certFileID, err := s.saveCertificate(cert, "root-ca")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save certificate: %w", err)
|
||||
}
|
||||
|
||||
privateKeyFileID, err := s.savePrivateKey(privateKey, "root-ca")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save private key: %w", err)
|
||||
}
|
||||
|
||||
// Create CA model
|
||||
ca := &models.CertificateAuthority{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
SerialNumber: cert.SerialNumber.String(),
|
||||
AttributeCommonName: cert.Subject.CommonName,
|
||||
AttributeOrganization: cert.Subject.Organization[0],
|
||||
AttributeOrganizationUnit: cert.Subject.OrganizationalUnit[0],
|
||||
AttributeCountry: cert.Subject.Country[0],
|
||||
AttributeState: cert.Subject.Province[0],
|
||||
AttributeLocality: cert.Subject.Locality[0],
|
||||
AttributeStreet: cert.Subject.StreetAddress[0],
|
||||
AttributeEmail: req.Email,
|
||||
AttributeAddress: req.Address,
|
||||
AttributePostalCode: cert.Subject.PostalCode[0],
|
||||
AttributeNotBefore: cert.NotBefore,
|
||||
AttributeNotAfter: cert.NotAfter,
|
||||
Root: true,
|
||||
ParentID: nil, // Root CA has no parent
|
||||
OrganizationID: req.OrganizationID,
|
||||
FileID: certFileID,
|
||||
PrivateKeyID: privateKeyFileID,
|
||||
}
|
||||
|
||||
// Save to database
|
||||
createdCA, err := s.caRepo.Create(*ca)
|
||||
if err != nil {
|
||||
// Clean up files if database save fails
|
||||
s.cleanupFiles(certFileID, privateKeyFileID)
|
||||
return nil, fmt.Errorf("failed to save CA to database: %w", err)
|
||||
}
|
||||
|
||||
return &createdCA, nil
|
||||
}
|
||||
|
||||
// CreateIntermediateCA creates a new intermediate certificate authority
|
||||
func (s *CertificateAuthorityService) CreateIntermediateCA(req *CreateIntermediateCARequest) (*models.CertificateAuthority, error) {
|
||||
// Validate request
|
||||
if err := s.validateIntermediateCARequest(req); err != nil {
|
||||
return nil, fmt.Errorf("invalid request: %w", err)
|
||||
}
|
||||
|
||||
// Check if organization exists
|
||||
org, err := s.orgRepo.GetByID(req.OrganizationID.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("organization not found: %w", err)
|
||||
}
|
||||
|
||||
// Get parent CA
|
||||
parentCA, err := s.caRepo.GetByID(req.ParentCAID.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parent CA not found: %w", err)
|
||||
}
|
||||
|
||||
// Load parent CA certificate and private key
|
||||
parentCert, parentPrivateKey, err := s.loadCACertificateAndKey(&parentCA)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load parent CA certificate: %w", err)
|
||||
}
|
||||
|
||||
// Create certificate configuration
|
||||
var config *utils.CertificateConfig
|
||||
if req.ValidityYears > 0 {
|
||||
config = utils.CACertificateConfigWithValidity(false, req.ValidityYears) // false = not root
|
||||
} else {
|
||||
config = utils.CACertificateConfig(false) // false = not root
|
||||
}
|
||||
config.CommonName = req.CommonName
|
||||
config.Organization = org.Name
|
||||
config.OrganizationalUnit = req.OrganizationalUnit
|
||||
config.Country = req.Country
|
||||
config.State = req.State
|
||||
config.Locality = req.Locality
|
||||
config.Street = req.Street
|
||||
config.PostalCode = req.PostalCode
|
||||
config.Email = req.Email
|
||||
|
||||
// Validate configuration
|
||||
if err := utils.ValidateCertificateConfig(config); err != nil {
|
||||
return nil, fmt.Errorf("invalid certificate configuration: %w", err)
|
||||
}
|
||||
|
||||
// Generate certificate and private key
|
||||
generator := utils.NewCertificateGenerator(config)
|
||||
cert, privateKey, err := generator.GenerateCertificate(parentCert, parentPrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate intermediate CA certificate: %w", err)
|
||||
}
|
||||
|
||||
// Save certificate and private key to files
|
||||
certFileID, err := s.saveCertificate(cert, "intermediate-ca")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save certificate: %w", err)
|
||||
}
|
||||
|
||||
privateKeyFileID, err := s.savePrivateKey(privateKey, "intermediate-ca")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save private key: %w", err)
|
||||
}
|
||||
|
||||
// Create CA model
|
||||
ca := &models.CertificateAuthority{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
SerialNumber: cert.SerialNumber.String(),
|
||||
AttributeCommonName: cert.Subject.CommonName,
|
||||
AttributeOrganization: cert.Subject.Organization[0],
|
||||
AttributeOrganizationUnit: cert.Subject.OrganizationalUnit[0],
|
||||
AttributeCountry: cert.Subject.Country[0],
|
||||
AttributeState: cert.Subject.Province[0],
|
||||
AttributeLocality: cert.Subject.Locality[0],
|
||||
AttributeStreet: cert.Subject.StreetAddress[0],
|
||||
AttributeEmail: req.Email,
|
||||
AttributeAddress: req.Address,
|
||||
AttributePostalCode: cert.Subject.PostalCode[0],
|
||||
AttributeNotBefore: cert.NotBefore,
|
||||
AttributeNotAfter: cert.NotAfter,
|
||||
Root: false,
|
||||
ParentID: &req.ParentCAID,
|
||||
OrganizationID: req.OrganizationID,
|
||||
FileID: certFileID,
|
||||
PrivateKeyID: privateKeyFileID,
|
||||
}
|
||||
|
||||
// Save to database
|
||||
createdCA, err := s.caRepo.Create(*ca)
|
||||
if err != nil {
|
||||
// Clean up files if database save fails
|
||||
s.cleanupFiles(certFileID, privateKeyFileID)
|
||||
return nil, fmt.Errorf("failed to save CA to database: %w", err)
|
||||
}
|
||||
|
||||
return &createdCA, nil
|
||||
}
|
||||
|
||||
// GetCA retrieves a certificate authority by ID
|
||||
func (s *CertificateAuthorityService) GetCA(id string) (*models.CertificateAuthority, error) {
|
||||
ca, err := s.caRepo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CA not found: %w", err)
|
||||
}
|
||||
return &ca, nil
|
||||
}
|
||||
|
||||
// GetAllCAs retrieves all certificate authorities
|
||||
func (s *CertificateAuthorityService) GetAllCAs() ([]models.CertificateAuthority, error) {
|
||||
cas, err := s.caRepo.GetAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
||||
}
|
||||
return cas, nil
|
||||
}
|
||||
|
||||
// GetRootCAs retrieves all root certificate authorities
|
||||
func (s *CertificateAuthorityService) GetRootCAs() ([]models.CertificateAuthority, error) {
|
||||
allCAs, err := s.caRepo.GetAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
||||
}
|
||||
|
||||
var rootCAs []models.CertificateAuthority
|
||||
for _, ca := range allCAs {
|
||||
if ca.Root {
|
||||
rootCAs = append(rootCAs, ca)
|
||||
}
|
||||
}
|
||||
|
||||
return rootCAs, nil
|
||||
}
|
||||
|
||||
// GetIntermediateCAs retrieves all intermediate certificate authorities
|
||||
func (s *CertificateAuthorityService) GetIntermediateCAs() ([]models.CertificateAuthority, error) {
|
||||
allCAs, err := s.caRepo.GetAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
||||
}
|
||||
|
||||
var intermediateCAs []models.CertificateAuthority
|
||||
for _, ca := range allCAs {
|
||||
if !ca.Root {
|
||||
intermediateCAs = append(intermediateCAs, ca)
|
||||
}
|
||||
}
|
||||
|
||||
return intermediateCAs, nil
|
||||
}
|
||||
|
||||
// GetCAByParent retrieves all certificate authorities under a specific parent
|
||||
func (s *CertificateAuthorityService) GetCAByParent(parentID string) ([]models.CertificateAuthority, error) {
|
||||
allCAs, err := s.caRepo.GetAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve CAs: %w", err)
|
||||
}
|
||||
|
||||
var childCAs []models.CertificateAuthority
|
||||
for _, ca := range allCAs {
|
||||
if ca.ParentID != nil && ca.ParentID.String() == parentID {
|
||||
childCAs = append(childCAs, ca)
|
||||
}
|
||||
}
|
||||
|
||||
return childCAs, nil
|
||||
}
|
||||
|
||||
// GetCACertificate retrieves the certificate for a CA
|
||||
func (s *CertificateAuthorityService) GetCACertificate(caID string) (*x509.Certificate, error) {
|
||||
ca, err := s.caRepo.GetByID(caID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CA not found: %w", err)
|
||||
}
|
||||
|
||||
cert, _, err := s.loadCACertificateAndKey(&ca)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load CA certificate: %w", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// GetCAPrivateKey retrieves the private key for a CA
|
||||
func (s *CertificateAuthorityService) GetCAPrivateKey(caID string) (interface{}, error) {
|
||||
ca, err := s.caRepo.GetByID(caID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CA not found: %w", err)
|
||||
}
|
||||
|
||||
_, privateKey, err := s.loadCACertificateAndKey(&ca)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load CA private key: %w", err)
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// UpdateCA updates a certificate authority
|
||||
func (s *CertificateAuthorityService) UpdateCA(ca *models.CertificateAuthority) error {
|
||||
return s.caRepo.Update(*ca)
|
||||
}
|
||||
|
||||
// DeleteCA deletes a certificate authority
|
||||
func (s *CertificateAuthorityService) DeleteCA(caID string) error {
|
||||
ca, err := s.caRepo.GetByID(caID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CA not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if CA has children
|
||||
children, err := s.GetCAByParent(caID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check CA children: %w", err)
|
||||
}
|
||||
|
||||
if len(children) > 0 {
|
||||
return fmt.Errorf("cannot delete CA with children: %d intermediate CAs depend on this CA", len(children))
|
||||
}
|
||||
|
||||
// Delete from database
|
||||
if err := s.caRepo.Delete(caID); err != nil {
|
||||
return fmt.Errorf("failed to delete CA from database: %w", err)
|
||||
}
|
||||
|
||||
// Clean up files
|
||||
s.cleanupFiles(ca.FileID, ca.PrivateKeyID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeCA revokes a certificate authority (marks as revoked but keeps in database)
|
||||
func (s *CertificateAuthorityService) RevokeCA(caID string, reason string) error {
|
||||
ca, err := s.caRepo.GetByID(caID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CA not found: %w", err)
|
||||
}
|
||||
|
||||
// Update CA with revocation information
|
||||
ca.Description = fmt.Sprintf("%s [REVOKED: %s]", ca.Description, reason)
|
||||
|
||||
if err := s.caRepo.Update(ca); err != nil {
|
||||
return fmt.Errorf("failed to revoke CA: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateCAChain validates the certificate chain for a CA
|
||||
func (s *CertificateAuthorityService) ValidateCAChain(caID string) error {
|
||||
ca, err := s.caRepo.GetByID(caID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CA not found: %w", err)
|
||||
}
|
||||
|
||||
// Load CA certificate
|
||||
cert, _, err := s.loadCACertificateAndKey(&ca)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load CA certificate: %w", err)
|
||||
}
|
||||
|
||||
// If it's a root CA, validate it's self-signed
|
||||
if ca.Root {
|
||||
if err := cert.CheckSignatureFrom(cert); err != nil {
|
||||
return fmt.Errorf("root CA certificate is not properly self-signed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// For intermediate CAs, validate the chain
|
||||
if ca.ParentID == nil {
|
||||
return fmt.Errorf("intermediate CA must have a parent")
|
||||
}
|
||||
|
||||
parentCA, err := s.caRepo.GetByID(ca.ParentID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("parent CA not found: %w", err)
|
||||
}
|
||||
|
||||
parentCert, _, err := s.loadCACertificateAndKey(&parentCA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load parent CA certificate: %w", err)
|
||||
}
|
||||
|
||||
// Validate certificate chain
|
||||
if err := cert.CheckSignatureFrom(parentCert); err != nil {
|
||||
return fmt.Errorf("CA certificate is not properly signed by parent: %w", err)
|
||||
}
|
||||
|
||||
// Check validity period
|
||||
now := time.Now()
|
||||
if now.Before(cert.NotBefore) {
|
||||
return fmt.Errorf("CA certificate is not yet valid")
|
||||
}
|
||||
if now.After(cert.NotAfter) {
|
||||
return fmt.Errorf("CA certificate has expired")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
// saveCertificate saves a certificate to a file and returns the file ID
|
||||
func (s *CertificateAuthorityService) saveCertificate(cert *x509.Certificate, prefix string) (string, error) {
|
||||
// Create certificate directory if it doesn't exist
|
||||
if err := os.MkdirAll(s.certDir, 0755); err != nil {
|
||||
return "", fmt.Errorf("failed to create certificate directory: %w", err)
|
||||
}
|
||||
|
||||
// Generate file ID
|
||||
fileID := fmt.Sprintf("%s-%s", prefix, uuid.New().String())
|
||||
|
||||
// Export certificate to PEM
|
||||
exporter := utils.NewCertificateExporter()
|
||||
certPEM, err := exporter.ExportCertificateToPEM(cert)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to export certificate to PEM: %w", err)
|
||||
}
|
||||
|
||||
// Save to file
|
||||
certPath := filepath.Join(s.certDir, fmt.Sprintf("%s.crt", fileID))
|
||||
if err := os.WriteFile(certPath, certPEM, 0644); err != nil {
|
||||
return "", fmt.Errorf("failed to write certificate file: %w", err)
|
||||
}
|
||||
|
||||
return fileID, nil
|
||||
}
|
||||
|
||||
// savePrivateKey saves a private key to a file and returns the file ID
|
||||
func (s *CertificateAuthorityService) savePrivateKey(privateKey interface{}, prefix string) (string, error) {
|
||||
// Create private key directory if it doesn't exist
|
||||
if err := os.MkdirAll(s.privateDir, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to create private key directory: %w", err)
|
||||
}
|
||||
|
||||
// Generate file ID
|
||||
fileID := fmt.Sprintf("%s-%s", prefix, uuid.New().String())
|
||||
|
||||
// Export private key to PEM
|
||||
exporter := utils.NewCertificateExporter()
|
||||
keyPEM, err := exporter.ExportPrivateKeyToPEM(privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to export private key to PEM: %w", err)
|
||||
}
|
||||
|
||||
// Save to file
|
||||
keyPath := filepath.Join(s.privateDir, fmt.Sprintf("%s.key", fileID))
|
||||
if err := os.WriteFile(keyPath, keyPEM, 0600); err != nil {
|
||||
return "", fmt.Errorf("failed to write private key file: %w", err)
|
||||
}
|
||||
|
||||
return fileID, nil
|
||||
}
|
||||
|
||||
// loadCACertificateAndKey loads a CA certificate and private key from files
|
||||
func (s *CertificateAuthorityService) loadCACertificateAndKey(ca *models.CertificateAuthority) (*x509.Certificate, interface{}, error) {
|
||||
// Load certificate
|
||||
certPath := filepath.Join(s.certDir, fmt.Sprintf("%s.crt", ca.FileID))
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read certificate file: %w", err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode certificate PEM")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
}
|
||||
|
||||
// Load private key
|
||||
keyPath := filepath.Join(s.privateDir, fmt.Sprintf("%s.key", ca.PrivateKeyID))
|
||||
keyPEM, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read private key file: %w", err)
|
||||
}
|
||||
|
||||
block, _ = pem.Decode(keyPEM)
|
||||
if block == nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode private key PEM")
|
||||
}
|
||||
|
||||
var privateKey interface{}
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
case "EC PRIVATE KEY":
|
||||
privateKey, err = x509.ParseECPrivateKey(block.Bytes)
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unsupported private key type: %s", block.Type)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
return cert, privateKey, nil
|
||||
}
|
||||
|
||||
// cleanupFiles removes certificate and private key files
|
||||
func (s *CertificateAuthorityService) cleanupFiles(certFileID, privateKeyFileID string) {
|
||||
if certFileID != "" {
|
||||
certPath := filepath.Join(s.certDir, fmt.Sprintf("%s.crt", certFileID))
|
||||
os.Remove(certPath)
|
||||
}
|
||||
|
||||
if privateKeyFileID != "" {
|
||||
keyPath := filepath.Join(s.privateDir, fmt.Sprintf("%s.key", privateKeyFileID))
|
||||
os.Remove(keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
// validateRootCARequest validates a root CA creation request
|
||||
func (s *CertificateAuthorityService) validateRootCARequest(req *CreateRootCARequest) error {
|
||||
if req.Name == "" {
|
||||
return fmt.Errorf("name is required")
|
||||
}
|
||||
if req.CommonName == "" {
|
||||
return fmt.Errorf("common name is required")
|
||||
}
|
||||
if req.Organization == "" {
|
||||
return fmt.Errorf("organization is required")
|
||||
}
|
||||
if req.Country == "" {
|
||||
return fmt.Errorf("country is required")
|
||||
}
|
||||
if req.OrganizationID == uuid.Nil {
|
||||
return fmt.Errorf("organization ID is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIntermediateCARequest validates an intermediate CA creation request
|
||||
func (s *CertificateAuthorityService) validateIntermediateCARequest(req *CreateIntermediateCARequest) error {
|
||||
if req.Name == "" {
|
||||
return fmt.Errorf("name is required")
|
||||
}
|
||||
if req.CommonName == "" {
|
||||
return fmt.Errorf("common name is required")
|
||||
}
|
||||
if req.Organization == "" {
|
||||
return fmt.Errorf("organization is required")
|
||||
}
|
||||
if req.Country == "" {
|
||||
return fmt.Errorf("country is required")
|
||||
}
|
||||
if req.OrganizationID == uuid.Nil {
|
||||
return fmt.Errorf("organization ID is required")
|
||||
}
|
||||
if req.ParentCAID == uuid.Nil {
|
||||
return fmt.Errorf("parent CA ID is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Request structures
|
||||
|
||||
// CreateRootCARequest represents a request to create a root CA
|
||||
type CreateRootCARequest struct {
|
||||
Name string
|
||||
Description string
|
||||
CommonName string
|
||||
Organization string
|
||||
OrganizationalUnit string
|
||||
Country string
|
||||
State string
|
||||
Locality string
|
||||
Street string
|
||||
Address string
|
||||
PostalCode string
|
||||
Email string
|
||||
OrganizationID uuid.UUID
|
||||
ValidityYears int // Custom validity period in years (0 = use default)
|
||||
}
|
||||
|
||||
// CreateIntermediateCARequest represents a request to create an intermediate CA
|
||||
type CreateIntermediateCARequest struct {
|
||||
Name string
|
||||
Description string
|
||||
CommonName string
|
||||
Organization string
|
||||
OrganizationalUnit string
|
||||
Country string
|
||||
State string
|
||||
Locality string
|
||||
Street string
|
||||
Address string
|
||||
PostalCode string
|
||||
Email string
|
||||
OrganizationID uuid.UUID
|
||||
ParentCAID uuid.UUID
|
||||
ValidityYears int // Custom validity period in years (0 = use default)
|
||||
}
|
690
certificate/authority_test.go
Normal file
690
certificate/authority_test.go
Normal file
@@ -0,0 +1,690 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.secnex.io/secnex/certman/models"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func setupTestDatabase(t *testing.T) (*gorm.DB, func()) {
|
||||
// Create temporary database file
|
||||
dbPath := filepath.Join(t.TempDir(), "test_certman.db")
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
|
||||
// Auto-migrate models
|
||||
if err := db.AutoMigrate(
|
||||
&models.Organization{},
|
||||
&models.CertificateAuthority{},
|
||||
); err != nil {
|
||||
t.Fatalf("Failed to migrate database: %v", err)
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
os.Remove(dbPath)
|
||||
}
|
||||
|
||||
return db, cleanup
|
||||
}
|
||||
|
||||
func setupTestService(t *testing.T) (*CertificateAuthorityService, *gorm.DB, func()) {
|
||||
db, dbCleanup := setupTestDatabase(t)
|
||||
|
||||
certDir := filepath.Join(t.TempDir(), "certs")
|
||||
privateDir := filepath.Join(t.TempDir(), "private")
|
||||
|
||||
service := NewCertificateAuthorityService(db, certDir, privateDir)
|
||||
|
||||
cleanup := func() {
|
||||
dbCleanup()
|
||||
os.RemoveAll(certDir)
|
||||
os.RemoveAll(privateDir)
|
||||
}
|
||||
|
||||
return service, db, cleanup
|
||||
}
|
||||
|
||||
func createTestOrganization(t *testing.T, db *gorm.DB) *models.Organization {
|
||||
org := &models.Organization{
|
||||
Name: "Test Organization",
|
||||
Description: "Test Organization for CA tests",
|
||||
Status: models.OrganizationStatusActive,
|
||||
}
|
||||
|
||||
if err := db.Create(org).Error; err != nil {
|
||||
t.Fatalf("Failed to create test organization: %v", err)
|
||||
}
|
||||
|
||||
return org
|
||||
}
|
||||
|
||||
func TestCreateRootCA(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
req := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
ca, err := service.CreateRootCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
if ca == nil {
|
||||
t.Fatal("Created CA should not be nil")
|
||||
}
|
||||
|
||||
if !ca.Root {
|
||||
t.Error("Created CA should be marked as root")
|
||||
}
|
||||
|
||||
if ca.ParentID != nil {
|
||||
t.Error("Root CA should not have a parent ID")
|
||||
}
|
||||
|
||||
if ca.Name != req.Name {
|
||||
t.Errorf("Expected name %s, got %s", req.Name, ca.Name)
|
||||
}
|
||||
|
||||
if ca.AttributeCommonName != req.CommonName {
|
||||
t.Errorf("Expected common name %s, got %s", req.CommonName, ca.AttributeCommonName)
|
||||
}
|
||||
|
||||
if ca.FileID == "" {
|
||||
t.Error("Certificate file ID should not be empty")
|
||||
}
|
||||
|
||||
if ca.PrivateKeyID == "" {
|
||||
t.Error("Private key file ID should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateIntermediateCA(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// First create a root CA
|
||||
rootReq := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
rootCA, err := service.CreateRootCA(rootReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Now create an intermediate CA
|
||||
req := &CreateIntermediateCARequest{
|
||||
Name: "Test Intermediate CA",
|
||||
Description: "Test Intermediate CA for testing",
|
||||
CommonName: "Test Intermediate CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "intermediate@example.com",
|
||||
OrganizationID: org.ID,
|
||||
ParentCAID: rootCA.ID,
|
||||
}
|
||||
|
||||
ca, err := service.CreateIntermediateCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create intermediate CA: %v", err)
|
||||
}
|
||||
|
||||
if ca == nil {
|
||||
t.Fatal("Created CA should not be nil")
|
||||
}
|
||||
|
||||
if ca.Root {
|
||||
t.Error("Created CA should not be marked as root")
|
||||
}
|
||||
|
||||
if ca.ParentID == nil {
|
||||
t.Error("Intermediate CA should have a parent ID")
|
||||
}
|
||||
|
||||
if ca.ParentID.String() != rootCA.ID.String() {
|
||||
t.Errorf("Expected parent ID %s, got %s", rootCA.ID.String(), ca.ParentID.String())
|
||||
}
|
||||
|
||||
if ca.Name != req.Name {
|
||||
t.Errorf("Expected name %s, got %s", req.Name, ca.Name)
|
||||
}
|
||||
|
||||
if ca.AttributeCommonName != req.CommonName {
|
||||
t.Errorf("Expected common name %s, got %s", req.CommonName, ca.AttributeCommonName)
|
||||
}
|
||||
|
||||
if ca.FileID == "" {
|
||||
t.Error("Certificate file ID should not be empty")
|
||||
}
|
||||
|
||||
if ca.PrivateKeyID == "" {
|
||||
t.Error("Private key file ID should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCA(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create a root CA
|
||||
req := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
createdCA, err := service.CreateRootCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Retrieve the CA
|
||||
retrievedCA, err := service.GetCA(createdCA.ID.String())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get CA: %v", err)
|
||||
}
|
||||
|
||||
if retrievedCA.ID != createdCA.ID {
|
||||
t.Errorf("Expected ID %s, got %s", createdCA.ID.String(), retrievedCA.ID.String())
|
||||
}
|
||||
|
||||
if retrievedCA.Name != createdCA.Name {
|
||||
t.Errorf("Expected name %s, got %s", createdCA.Name, retrievedCA.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllCAs(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create multiple CAs
|
||||
rootReq := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
rootCA, err := service.CreateRootCA(rootReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
intermediateReq := &CreateIntermediateCARequest{
|
||||
Name: "Test Intermediate CA",
|
||||
Description: "Test Intermediate CA for testing",
|
||||
CommonName: "Test Intermediate CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "intermediate@example.com",
|
||||
OrganizationID: org.ID,
|
||||
ParentCAID: rootCA.ID,
|
||||
}
|
||||
|
||||
_, err = service.CreateIntermediateCA(intermediateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create intermediate CA: %v", err)
|
||||
}
|
||||
|
||||
// Get all CAs
|
||||
allCAs, err := service.GetAllCAs()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get all CAs: %v", err)
|
||||
}
|
||||
|
||||
if len(allCAs) != 2 {
|
||||
t.Errorf("Expected 2 CAs, got %d", len(allCAs))
|
||||
}
|
||||
|
||||
// Get root CAs
|
||||
rootCAs, err := service.GetRootCAs()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get root CAs: %v", err)
|
||||
}
|
||||
|
||||
if len(rootCAs) != 1 {
|
||||
t.Errorf("Expected 1 root CA, got %d", len(rootCAs))
|
||||
}
|
||||
|
||||
// Get intermediate CAs
|
||||
intermediateCAs, err := service.GetIntermediateCAs()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get intermediate CAs: %v", err)
|
||||
}
|
||||
|
||||
if len(intermediateCAs) != 1 {
|
||||
t.Errorf("Expected 1 intermediate CA, got %d", len(intermediateCAs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCACertificate(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create a root CA
|
||||
req := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
ca, err := service.CreateRootCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Get CA certificate
|
||||
cert, err := service.GetCACertificate(ca.ID.String())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get CA certificate: %v", err)
|
||||
}
|
||||
|
||||
if cert == nil {
|
||||
t.Fatal("Certificate should not be nil")
|
||||
}
|
||||
|
||||
if cert.Subject.CommonName != req.CommonName {
|
||||
t.Errorf("Expected common name %s, got %s", req.CommonName, cert.Subject.CommonName)
|
||||
}
|
||||
|
||||
if !cert.IsCA {
|
||||
t.Error("Certificate should be marked as CA")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCAPrivateKey(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create a root CA
|
||||
req := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
ca, err := service.CreateRootCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Get CA private key
|
||||
privateKey, err := service.GetCAPrivateKey(ca.ID.String())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get CA private key: %v", err)
|
||||
}
|
||||
|
||||
if privateKey == nil {
|
||||
t.Fatal("Private key should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCAChain(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create a root CA
|
||||
rootReq := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
rootCA, err := service.CreateRootCA(rootReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Validate root CA chain
|
||||
if err := service.ValidateCAChain(rootCA.ID.String()); err != nil {
|
||||
t.Fatalf("Failed to validate root CA chain: %v", err)
|
||||
}
|
||||
|
||||
// Create an intermediate CA
|
||||
intermediateReq := &CreateIntermediateCARequest{
|
||||
Name: "Test Intermediate CA",
|
||||
Description: "Test Intermediate CA for testing",
|
||||
CommonName: "Test Intermediate CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "intermediate@example.com",
|
||||
OrganizationID: org.ID,
|
||||
ParentCAID: rootCA.ID,
|
||||
}
|
||||
|
||||
intermediateCA, err := service.CreateIntermediateCA(intermediateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create intermediate CA: %v", err)
|
||||
}
|
||||
|
||||
// Validate intermediate CA chain
|
||||
if err := service.ValidateCAChain(intermediateCA.ID.String()); err != nil {
|
||||
t.Fatalf("Failed to validate intermediate CA chain: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevokeCA(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create a root CA
|
||||
req := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
ca, err := service.CreateRootCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Revoke CA
|
||||
reason := "Test revocation"
|
||||
if err := service.RevokeCA(ca.ID.String(), reason); err != nil {
|
||||
t.Fatalf("Failed to revoke CA: %v", err)
|
||||
}
|
||||
|
||||
// Verify CA was updated
|
||||
updatedCA, err := service.GetCA(ca.ID.String())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get updated CA: %v", err)
|
||||
}
|
||||
|
||||
if updatedCA.Description == ca.Description {
|
||||
t.Error("CA description should have been updated with revocation info")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCA(t *testing.T) {
|
||||
service, db, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
org := createTestOrganization(t, db)
|
||||
|
||||
// Create a root CA
|
||||
req := &CreateRootCARequest{
|
||||
Name: "Test Root CA",
|
||||
Description: "Test Root CA for testing",
|
||||
CommonName: "Test Root CA",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "IT Department",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street 1",
|
||||
Address: "Test Street 1, 80331 Munich",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
OrganizationID: org.ID,
|
||||
}
|
||||
|
||||
ca, err := service.CreateRootCA(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create root CA: %v", err)
|
||||
}
|
||||
|
||||
// Delete CA
|
||||
if err := service.DeleteCA(ca.ID.String()); err != nil {
|
||||
t.Fatalf("Failed to delete CA: %v", err)
|
||||
}
|
||||
|
||||
// Verify CA was deleted
|
||||
_, err = service.GetCA(ca.ID.String())
|
||||
if err == nil {
|
||||
t.Error("CA should have been deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRootCARequest(t *testing.T) {
|
||||
service, _, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *CreateRootCARequest
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid request",
|
||||
req: &CreateRootCARequest{
|
||||
Name: "Test CA",
|
||||
CommonName: "Test CA",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
OrganizationID: uuid.New(),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
req: &CreateRootCARequest{
|
||||
CommonName: "Test CA",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
OrganizationID: uuid.New(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing common name",
|
||||
req: &CreateRootCARequest{
|
||||
Name: "Test CA",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
OrganizationID: uuid.New(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing organization",
|
||||
req: &CreateRootCARequest{
|
||||
Name: "Test CA",
|
||||
CommonName: "Test CA",
|
||||
Country: "DE",
|
||||
OrganizationID: uuid.New(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing country",
|
||||
req: &CreateRootCARequest{
|
||||
Name: "Test CA",
|
||||
CommonName: "Test CA",
|
||||
Organization: "Test Org",
|
||||
OrganizationID: uuid.New(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing organization ID",
|
||||
req: &CreateRootCARequest{
|
||||
Name: "Test CA",
|
||||
CommonName: "Test CA",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := service.validateRootCARequest(test.req)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Errorf("validateRootCARequest() error = %v, wantErr %v", err, test.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateIntermediateCARequest(t *testing.T) {
|
||||
service, _, cleanup := setupTestService(t)
|
||||
defer cleanup()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *CreateIntermediateCARequest
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid request",
|
||||
req: &CreateIntermediateCARequest{
|
||||
Name: "Test CA",
|
||||
CommonName: "Test CA",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
OrganizationID: uuid.New(),
|
||||
ParentCAID: uuid.New(),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing parent CA ID",
|
||||
req: &CreateIntermediateCARequest{
|
||||
Name: "Test CA",
|
||||
CommonName: "Test CA",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
OrganizationID: uuid.New(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := service.validateIntermediateCARequest(test.req)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Errorf("validateIntermediateCARequest() error = %v, wantErr %v", err, test.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user