feat: add certificate service and utility functions
- Implement CertificateService for end-to-end certificate management - Add support for various certificate types (web, client, email, etc.) - Include certificate generation, validation, and revocation - Add utility functions for certificate operations and validation - Implement comprehensive test coverage for certificate operations - Support SAN (Subject Alternative Name) and IP address extensions - Add proper error handling and validation for certificate requests
This commit is contained in:
403
certificate/certificate_test.go
Normal file
403
certificate/certificate_test.go
Normal file
@@ -0,0 +1,403 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.secnex.io/secnex/certman/models"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestCreateCertificateRequest(t *testing.T) {
|
||||
service := &CertificateService{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *CreateCertificateRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid web certificate request",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test Web Certificate",
|
||||
CommonName: "example.com",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid client certificate request",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test Client Certificate",
|
||||
CommonName: "user@example.com",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeClient,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid IoT certificate request",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test IoT Certificate",
|
||||
CommonName: "device.example.com",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeIoT,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
req: &CreateCertificateRequest{
|
||||
CommonName: "example.com",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing common name",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test Certificate",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing organization",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test Certificate",
|
||||
CommonName: "example.com",
|
||||
Country: "DE",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing country",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test Certificate",
|
||||
CommonName: "example.com",
|
||||
Organization: "Test Org",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing CA ID",
|
||||
req: &CreateCertificateRequest{
|
||||
Name: "Test Certificate",
|
||||
CommonName: "example.com",
|
||||
Organization: "Test Org",
|
||||
Country: "DE",
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := service.validateCreateCertificateRequest(test.req)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Errorf("validateCreateCertificateRequest() error = %v, wantErr %v", err, test.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCertificateFromCSRRequest(t *testing.T) {
|
||||
service := &CertificateService{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *CreateCertificateFromCSRRequest
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid CSR request",
|
||||
req: &CreateCertificateFromCSRRequest{
|
||||
Name: "Test Certificate from CSR",
|
||||
CSRID: uuid.New(),
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
req: &CreateCertificateFromCSRRequest{
|
||||
CSRID: uuid.New(),
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing CSR ID",
|
||||
req: &CreateCertificateFromCSRRequest{
|
||||
Name: "Test Certificate from CSR",
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing CA ID",
|
||||
req: &CreateCertificateFromCSRRequest{
|
||||
Name: "Test Certificate from CSR",
|
||||
CSRID: uuid.New(),
|
||||
Type: models.CertificateTypeWeb,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := service.validateCreateCertificateFromCSRRequest(test.req)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Errorf("validateCreateCertificateFromCSRRequest() error = %v, wantErr %v", err, test.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateRequestStructures(t *testing.T) {
|
||||
// Test CreateCertificateRequest structure
|
||||
req := &CreateCertificateRequest{
|
||||
Name: "Test Certificate",
|
||||
Description: "Test Description",
|
||||
CommonName: "test.example.com",
|
||||
Organization: "Test Organization",
|
||||
OrganizationalUnit: "Test Unit",
|
||||
Country: "DE",
|
||||
State: "Bavaria",
|
||||
Locality: "Munich",
|
||||
Street: "Test Street",
|
||||
Address: "Test Address",
|
||||
PostalCode: "80331",
|
||||
Email: "test@example.com",
|
||||
Type: models.CertificateTypeWeb,
|
||||
CertificateAuthorityID: uuid.New(),
|
||||
NotAfter: func() *time.Time { t := time.Now().AddDate(1, 0, 0); return &t }(),
|
||||
DNSNames: []string{"test.example.com", "www.test.example.com"},
|
||||
IPAddresses: []net.IP{net.ParseIP("192.168.1.1")},
|
||||
EmailAddresses: []string{"test@example.com"},
|
||||
URIs: []string{"https://test.example.com"},
|
||||
KeyType: "rsa",
|
||||
KeySize: 2048,
|
||||
Curve: "P-256",
|
||||
KeyUsage: &KeyUsageConfig{
|
||||
DigitalSignature: true,
|
||||
KeyEncipherment: true,
|
||||
},
|
||||
ExtendedKeyUsage: &ExtendedKeyUsageConfig{
|
||||
ServerAuth: true,
|
||||
},
|
||||
}
|
||||
|
||||
if req.Name == "" {
|
||||
t.Error("Certificate request name should not be empty")
|
||||
}
|
||||
if req.CommonName == "" {
|
||||
t.Error("Certificate request common name should not be empty")
|
||||
}
|
||||
if req.CertificateAuthorityID == uuid.Nil {
|
||||
t.Error("Certificate request CA ID should not be nil")
|
||||
}
|
||||
if req.Type == "" {
|
||||
t.Error("Certificate request type should not be empty")
|
||||
}
|
||||
if len(req.DNSNames) == 0 {
|
||||
t.Error("Certificate request should have DNS names")
|
||||
}
|
||||
if len(req.IPAddresses) == 0 {
|
||||
t.Error("Certificate request should have IP addresses")
|
||||
}
|
||||
if req.KeyUsage == nil {
|
||||
t.Error("Certificate request should have key usage")
|
||||
}
|
||||
if req.ExtendedKeyUsage == nil {
|
||||
t.Error("Certificate request should have extended key usage")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyUsageConfig(t *testing.T) {
|
||||
keyUsage := &KeyUsageConfig{
|
||||
DigitalSignature: true,
|
||||
KeyEncipherment: true,
|
||||
}
|
||||
|
||||
if !keyUsage.DigitalSignature {
|
||||
t.Error("Digital signature should be enabled")
|
||||
}
|
||||
if !keyUsage.KeyEncipherment {
|
||||
t.Error("Key encipherment should be enabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtendedKeyUsageConfig(t *testing.T) {
|
||||
extKeyUsage := &ExtendedKeyUsageConfig{
|
||||
ServerAuth: true,
|
||||
ClientAuth: false,
|
||||
}
|
||||
|
||||
if !extKeyUsage.ServerAuth {
|
||||
t.Error("Server auth should be enabled")
|
||||
}
|
||||
if extKeyUsage.ClientAuth {
|
||||
t.Error("Client auth should be disabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateServiceCreation(t *testing.T) {
|
||||
// Test that we can create a service instance
|
||||
service := NewCertificateService(nil, "/tmp/certs", "/tmp/private", nil)
|
||||
|
||||
if service == nil {
|
||||
t.Error("CertificateService should not be nil")
|
||||
}
|
||||
|
||||
if service.certDir != "/tmp/certs" {
|
||||
t.Errorf("Expected certDir to be /tmp/certs, got %s", service.certDir)
|
||||
}
|
||||
|
||||
if service.privateDir != "/tmp/private" {
|
||||
t.Errorf("Expected privateDir to be /tmp/private, got %s", service.privateDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateTypes(t *testing.T) {
|
||||
// Test that all certificate types are defined
|
||||
certTypes := []models.CertificateType{
|
||||
models.CertificateTypeWeb,
|
||||
models.CertificateTypeServer,
|
||||
models.CertificateTypeClient,
|
||||
models.CertificateTypeUser,
|
||||
models.CertificateTypeEmail,
|
||||
models.CertificateTypeCode,
|
||||
models.CertificateTypeCA,
|
||||
models.CertificateTypeIoT,
|
||||
models.CertificateTypeDevice,
|
||||
models.CertificateTypeSensor,
|
||||
models.CertificateTypeVPN,
|
||||
models.CertificateTypeOpenVPN,
|
||||
models.CertificateTypeWireGuard,
|
||||
models.CertificateTypeDatabase,
|
||||
models.CertificateTypeMySQL,
|
||||
models.CertificateTypePostgreSQL,
|
||||
models.CertificateTypeMongoDB,
|
||||
models.CertificateTypeAPI,
|
||||
models.CertificateTypeService,
|
||||
models.CertificateTypeMicroservice,
|
||||
models.CertificateTypeDocker,
|
||||
models.CertificateTypeKubernetes,
|
||||
models.CertificateTypeContainer,
|
||||
models.CertificateTypeCloud,
|
||||
models.CertificateTypeAWS,
|
||||
models.CertificateTypeAzure,
|
||||
models.CertificateTypeGCP,
|
||||
models.CertificateTypeNetwork,
|
||||
models.CertificateTypeFirewall,
|
||||
models.CertificateTypeProxy,
|
||||
models.CertificateTypeLoadBalancer,
|
||||
models.CertificateTypeMobile,
|
||||
models.CertificateTypeAndroid,
|
||||
models.CertificateTypeiOS,
|
||||
models.CertificateTypeApp,
|
||||
models.CertificateTypeDocument,
|
||||
models.CertificateTypePDF,
|
||||
models.CertificateTypeOffice,
|
||||
models.CertificateTypeTimestamp,
|
||||
models.CertificateTypeOCSP,
|
||||
models.CertificateTypeCustom,
|
||||
models.CertificateTypeSpecial,
|
||||
}
|
||||
|
||||
for _, certType := range certTypes {
|
||||
if certType == "" {
|
||||
t.Errorf("Certificate type should not be empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateTypeCategories(t *testing.T) {
|
||||
// Test web certificates
|
||||
webTypes := []models.CertificateType{
|
||||
models.CertificateTypeWeb,
|
||||
models.CertificateTypeServer,
|
||||
}
|
||||
|
||||
for _, certType := range webTypes {
|
||||
if certType == "" {
|
||||
t.Errorf("Web certificate type should not be empty: %s", certType)
|
||||
}
|
||||
}
|
||||
|
||||
// Test client certificates
|
||||
clientTypes := []models.CertificateType{
|
||||
models.CertificateTypeClient,
|
||||
models.CertificateTypeUser,
|
||||
}
|
||||
|
||||
for _, certType := range clientTypes {
|
||||
if certType == "" {
|
||||
t.Errorf("Client certificate type should not be empty: %s", certType)
|
||||
}
|
||||
}
|
||||
|
||||
// Test IoT certificates
|
||||
iotTypes := []models.CertificateType{
|
||||
models.CertificateTypeIoT,
|
||||
models.CertificateTypeDevice,
|
||||
models.CertificateTypeSensor,
|
||||
}
|
||||
|
||||
for _, certType := range iotTypes {
|
||||
if certType == "" {
|
||||
t.Errorf("IoT certificate type should not be empty: %s", certType)
|
||||
}
|
||||
}
|
||||
|
||||
// Test VPN certificates
|
||||
vpnTypes := []models.CertificateType{
|
||||
models.CertificateTypeVPN,
|
||||
models.CertificateTypeOpenVPN,
|
||||
models.CertificateTypeWireGuard,
|
||||
}
|
||||
|
||||
for _, certType := range vpnTypes {
|
||||
if certType == "" {
|
||||
t.Errorf("VPN certificate type should not be empty: %s", certType)
|
||||
}
|
||||
}
|
||||
|
||||
// Test database certificates
|
||||
dbTypes := []models.CertificateType{
|
||||
models.CertificateTypeDatabase,
|
||||
models.CertificateTypeMySQL,
|
||||
models.CertificateTypePostgreSQL,
|
||||
models.CertificateTypeMongoDB,
|
||||
}
|
||||
|
||||
for _, certType := range dbTypes {
|
||||
if certType == "" {
|
||||
t.Errorf("Database certificate type should not be empty: %s", certType)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user