1package models
2
3import (
4	"crypto/x509"
5	"errors"
6	"fmt"
7	"net"
8	"strings"
9)
10
11// NewPCFCertificateFromx509 converts a x509 certificate to a valid, well-formed PCF certificate,
12// erroring if this isn't possible.
13func NewPCFCertificateFromx509(certificate *x509.Certificate) (*PCFCertificate, error) {
14	if len(certificate.IPAddresses) != 1 {
15		return nil, fmt.Errorf("valid PCF certs have one IP address, but this has %s", certificate.IPAddresses)
16	}
17
18	pcfCert := &PCFCertificate{
19		InstanceID: certificate.Subject.CommonName,
20		IPAddress:  certificate.IPAddresses[0],
21	}
22
23	spaces := 0
24	orgs := 0
25	apps := 0
26	for _, ou := range certificate.Subject.OrganizationalUnit {
27		if strings.HasPrefix(ou, "space:") {
28			pcfCert.SpaceID = strings.Split(ou, "space:")[1]
29			spaces++
30			continue
31		}
32		if strings.HasPrefix(ou, "organization:") {
33			pcfCert.OrgID = strings.Split(ou, "organization:")[1]
34			orgs++
35			continue
36		}
37		if strings.HasPrefix(ou, "app:") {
38			pcfCert.AppID = strings.Split(ou, "app:")[1]
39			apps++
40			continue
41		}
42	}
43	if spaces > 1 {
44		return nil, fmt.Errorf("expected 1 space but received %d", spaces)
45	}
46	if orgs > 1 {
47		return nil, fmt.Errorf("expected 1 org but received %d", orgs)
48	}
49	if apps > 1 {
50		return nil, fmt.Errorf("expected 1 app but received %d", apps)
51	}
52	if err := pcfCert.validate(); err != nil {
53		return nil, err
54	}
55	return pcfCert, nil
56}
57
58// NewPCFCertificateFromx509 converts the given fields to a valid, well-formed PCF certificate,
59// erroring if this isn't possible.
60func NewPCFCertificate(instanceID, orgID, spaceID, appID, ipAddress string) (*PCFCertificate, error) {
61	pcfCert := &PCFCertificate{
62		InstanceID: instanceID,
63		OrgID:      orgID,
64		SpaceID:    spaceID,
65		AppID:      appID,
66		IPAddress:  net.ParseIP(ipAddress),
67	}
68	if err := pcfCert.validate(); err != nil {
69		return nil, err
70	}
71	return pcfCert, nil
72}
73
74// PCFCertificate isn't intended to be instantiated directly; but rather through one of the New
75// methods, which contain logic validating that the expected fields exist.
76type PCFCertificate struct {
77	InstanceID, OrgID, SpaceID, AppID string
78	IPAddress                         net.IP
79}
80
81func (c *PCFCertificate) validate() error {
82	if c.InstanceID == "" {
83		return errors.New("no instance ID on given certificate")
84	}
85	if c.AppID == "" {
86		return errors.New("no app ID on given certificate")
87	}
88	if c.OrgID == "" {
89		return errors.New("no org ID on given certificate")
90	}
91	if c.SpaceID == "" {
92		return errors.New("no space ID on given certificate")
93	}
94	if c.IPAddress.IsUnspecified() {
95		return errors.New("ip address is unspecified")
96	}
97	return nil
98}
99