1package models
2
3import (
4	"crypto/x509"
5	"errors"
6	"fmt"
7	"net"
8	"strings"
9)
10
11// NewCFCertificateFromx509 converts a x509 certificate to a valid, well-formed CF certificate,
12// erroring if this isn't possible.
13func NewCFCertificateFromx509(certificate *x509.Certificate) (*CFCertificate, error) {
14	if len(certificate.IPAddresses) != 1 {
15		return nil, fmt.Errorf("valid CF certs have one IP address, but this has %s", certificate.IPAddresses)
16	}
17
18	cfCert := &CFCertificate{
19		InstanceID: certificate.Subject.CommonName,
20		IPAddress:  certificate.IPAddresses[0].String(),
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			cfCert.SpaceID = strings.Split(ou, "space:")[1]
29			spaces++
30			continue
31		}
32		if strings.HasPrefix(ou, "organization:") {
33			cfCert.OrgID = strings.Split(ou, "organization:")[1]
34			orgs++
35			continue
36		}
37		if strings.HasPrefix(ou, "app:") {
38			cfCert.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 := cfCert.validate(); err != nil {
53		return nil, err
54	}
55	return cfCert, nil
56}
57
58// NewCFCertificateFromx509 converts the given fields to a valid, well-formed CF certificate,
59// erroring if this isn't possible.
60func NewCFCertificate(instanceID, orgID, spaceID, appID, ipAddress string) (*CFCertificate, error) {
61	cfCert := &CFCertificate{
62		InstanceID: instanceID,
63		OrgID:      orgID,
64		SpaceID:    spaceID,
65		AppID:      appID,
66		IPAddress:  ipAddress,
67	}
68	if err := cfCert.validate(); err != nil {
69		return nil, err
70	}
71	return cfCert, nil
72}
73
74// CFCertificate 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 CFCertificate struct {
77	InstanceID, OrgID, SpaceID, AppID, IPAddress string
78}
79
80func (c *CFCertificate) validate() error {
81	if c.InstanceID == "" {
82		return errors.New("no instance ID on given certificate")
83	}
84	if c.AppID == "" {
85		return errors.New("no app ID on given certificate")
86	}
87	if c.OrgID == "" {
88		return errors.New("no org ID on given certificate")
89	}
90	if c.SpaceID == "" {
91		return errors.New("no space ID on given certificate")
92	}
93	if c.IPAddress == "" {
94		return errors.New("ip address is unspecified")
95	}
96	if net.ParseIP(c.IPAddress) == nil {
97		return fmt.Errorf("%q could not be parsed as a valid IP address", c.IPAddress)
98	}
99	return nil
100}
101