Test Setup Failed
Push — master ( 55d0ee...a8290e )
by Keoghan
26:11 queued 10:32
created

CertificateBuilder   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Test Coverage

Coverage 96.25%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 19
eloc 78
dl 0
loc 215
ccs 77
cts 80
cp 0.9625
rs 10
c 1
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A createCertificate() 0 22 2
A paths() 0 7 1
A caPaths() 0 6 1
A createSigningRequest() 0 11 1
A createPrivateKey() 0 3 1
A destroy() 0 4 2
A build() 0 5 1
A createConf() 0 3 1
A clearCertificates() 0 14 4
A createCa() 0 24 4
A __construct() 0 11 1
1
<?php
2
3
namespace App\Support\Ssl;
4
5
use App\Support\Contracts\Cli;
6
use App\Support\Mechanics\Mechanic;
7
use Illuminate\Filesystem\Filesystem;
8
use Symfony\Component\Finder\SplFileInfo;
9
use League\Flysystem\FileNotFoundException;
10
11
class CertificateBuilder
12
{
13
    protected $cli;
14
    protected $filesystem;
15
    protected $certificatesPath;
16
    protected $oName;
17
    protected $cName;
18
    protected $domain;
19
    protected $email;
20
21
    /**
22
     * Driver that will interact with the operating system.
23
     *
24
     * @var Mechanic
25
     */
26
    protected $mechanic;
27
28 12
    public function __construct(Cli $cli, Filesystem $filesystem, Mechanic $mechanic, $certificatesPath)
29
    {
30 12
        $this->cli = $cli;
31 12
        $this->filesystem = $filesystem;
32 12
        $this->mechanic = $mechanic;
33 12
        $this->certificatesPath = $certificatesPath;
34
35 12
        $this->oName = 'Klever Porter CA Self Signed Organization';
36 12
        $this->cName = 'Klever Porter CA Self Signed CN';
37 12
        $this->domain = 'klever.porter';
38 12
        $this->email = 'rootcertificate@'.$this->domain;
39 12
    }
40
41
    /**
42
     * List the certificate authority paths.
43
     *
44
     * @return object
45
     */
46 6
    public function caPaths()
47
    {
48
        return (object) [
49 6
            'key' => $this->certificatesPath.'/KleverPorterCASelfSigned.key',
50 6
            'pem' => $this->certificatesPath.'/KleverPorterCASelfSigned.pem',
51 6
            'srl' => $this->certificatesPath.'/KleverPorterCASelfSigned.srl',
52
        ];
53
    }
54
55
    /**
56
     * List paths based on the certificate url.
57
     *
58
     * @param $url
59
     *
60
     * @return object
61
     */
62 5
    public function paths($url)
63
    {
64
        return (object) [
65 5
            'key'  => $this->certificatesPath.'/'.$url.'.key',
66 5
            'csr'  => $this->certificatesPath.'/'.$url.'.csr',
67 5
            'crt'  => $this->certificatesPath.'/'.$url.'.crt',
68 5
            'conf' => $this->certificatesPath.'/'.$url.'.conf',
69
        ];
70
    }
71
72
    /**
73
     * Build a certificate based on the url.  Create CA if needed.
74
     *
75
     * @param $url
76
     */
77 4
    public function build($url)
78
    {
79 4
        $this->destroy($url);
80 4
        $this->createCa();
81 4
        $this->createCertificate($url);
82 4
    }
83
84
    /**
85
     * Destroy certificate for site based on url.
86
     *
87
     * @param $url
88
     */
89 5
    public function destroy($url)
90
    {
91 5
        foreach ($this->paths($url) as $path) {
92 5
            $this->filesystem->delete($path);
93
        }
94 5
    }
95
96
    /**
97
     * Create certificate authority.
98
     */
99 4
    public function createCa()
100
    {
101 4
        $paths = $this->caPaths();
102
103 4
        if ($this->filesystem->exists($paths->key) || $this->filesystem->exists($paths->pem)) {
104
            return;
105
        }
106
107 4
        $this->cli->exec(sprintf(
108 4
            'openssl req -new -newkey rsa:2048 -days 730 -nodes -x509 -subj "/C=GB/ST=Berks/O=%s/localityName=Reading/commonName=%s/organizationalUnitName=Developers/emailAddress=%s/" -keyout %s -out %s',
109 4
            $this->oName,
110 4
            $this->cName,
111 4
            $this->email,
112 4
            $paths->key,
113 4
            $paths->pem
114
        ));
115
116 4
        if (! $this->filesystem->exists($paths->pem)) {
117
            throw new FileNotFoundException($paths->pem);
118
        }
119
120
121
122 4
        $this->mechanic->trustCA($paths->pem);
123 4
    }
124
125
    /**
126
     * Create a certificate for the given URL.
127
     *
128
     * @param string $url
129
     *
130
     * @return void
131
     */
132 4
    public function createCertificate($url)
133
    {
134 4
        $paths = $this->paths($url);
135 4
        $caPaths = $this->caPaths();
136
137 4
        $this->createConf($paths->conf, $url);
138 4
        $this->createPrivateKey($paths->key);
139 4
        $this->createSigningRequest($url, $paths->key, $paths->csr, $paths->conf);
140
141 4
        $caSrlParam = ' -CAcreateserial';
142 4
        if ($this->filesystem->exists($caPaths->srl)) {
143
            $caSrlParam = ' -CAserial '.$caPaths->srl;
144
        }
145
146 4
        $this->cli->exec(sprintf(
147 4
            'openssl x509 -req -sha256 -days 730 -CA %s -CAkey %s%s -in %s -out %s -extensions v3_req -extfile %s',
148 4
            $caPaths->pem,
149 4
            $caPaths->key,
150
            $caSrlParam,
151 4
            $paths->csr,
152 4
            $paths->crt,
153 4
            $paths->conf
154
        ));
155
156
        // Trusting the certificate shouldn't be necessary once the CA is trusted.
157
        // $this->mechanic->trustCertificate($paths->crt);
158 4
    }
159
160
    /**
161
     * Build the SSL config for the given URL.
162
     *
163
     * @param $path
164
     * @param $url
165
     */
166 4
    public function createConf($path, $url)
167
    {
168 4
        $this->filesystem->put($path, view('ssl.conf')->withUrl($url)->render());
169 4
    }
170
171
    /**
172
     * Create the private key for the TLS certificate.
173
     *
174
     * @param string $keyPath
175
     *
176
     * @return void
177
     */
178 4
    public function createPrivateKey($keyPath)
179
    {
180 4
        $this->cli->exec(sprintf('openssl genrsa -out %s 2048', $keyPath));
181 4
    }
182
183
    /**
184
     * Create the signing request for the TLS certificate.
185
     *
186
     * @param        $url
187
     * @param string $keyPath
188
     * @param        $csrPath
189
     * @param        $confPath
190
     *
191
     * @return void
192
     */
193 4
    public function createSigningRequest($url, $keyPath, $csrPath, $confPath)
194
    {
195 4
        $this->cli->exec(sprintf(
196 4
            'openssl req -new -key %s -out %s -subj "/C=GB/ST=Berks/O=%s/localityName=Reading/commonName=%s/organizationalUnitName=Developers/emailAddress=%s%s/" -config %s',
197
            $keyPath,
198
            $csrPath,
199 4
            $this->domain,
200
            $url,
201
            $url,
202 4
            '@'.$this->domain,
203
            $confPath
204
        ));
205 4
    }
206
207
    /**
208
     * Clear generated certs. Optionally clear CA too.
209
     *
210
     * @param bool $dropCA
211
     */
212 2
    public function clearCertificates($dropCA = false)
213
    {
214 2
        $caPaths = (array) $this->caPaths();
215
216 2
        $files = $this->filesystem
217 2
            ->allFiles($this->certificatesPath);
218
219 2
        foreach ($files as $file) {
220
            /** @var SplFileInfo $file */
221 2
            if (!$dropCA && in_array($file->getPathname(), $caPaths)) {
222 1
                continue;
223
            }
224
225 2
            $this->filesystem->delete($file->getPathname());
226
        }
227 2
    }
228
}
229