Skip to content

Commit

Permalink
Feature improve certificate builder (#34)
Browse files Browse the repository at this point in the history
* Use the cli and filesystem in Certificate builder. Add some more tests.
  • Loading branch information
Keoghan authored Sep 20, 2018
1 parent ee18aa2 commit 2d68970
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 50 deletions.
2 changes: 1 addition & 1 deletion app/Commands/Site/RenewCertificates.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class RenewCertificates extends BaseCommand
*/
public function handle(): void
{
app(CertificateBuilder::class)->clearDirectory((bool) $this->option('clear-ca'));
app(CertificateBuilder::class)->clearCertificates((bool) $this->option('clear-ca'));

foreach (Site::where('secure', true)->get() as $site) {
$site->buildCertificate();
Expand Down
7 changes: 6 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use App\Support\FilePublisher;
use App\Support\Images\ImageSetRepository;
use App\Support\Ssl\CertificateBuilder;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
Expand All @@ -30,7 +31,11 @@ public function boot(): void
public function register(): void
{
$this->app->singleton(CertificateBuilder::class, function () {
return new CertificateBuilder(app(PorterLibrary::class)->sslPath());
return new CertificateBuilder(
app(CliContract::class),
app(Filesystem::class),
app(PorterLibrary::class)->sslPath()
);
});

$this->app->bind(ConsoleWriter::class);
Expand Down
45 changes: 25 additions & 20 deletions app/Support/Ssl/CertificateBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@

namespace App\Support\Ssl;

use App\Support\Contracts\Cli;
use App\Support\Mechanics\ChooseMechanic;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Finder\SplFileInfo;

class CertificateBuilder
{
protected $cli;
protected $filesystem;
protected $certificatesPath;
protected $oName;
protected $cName;
protected $domain;
protected $email;

public function __construct($certificatesPath)
public function __construct(Cli $cli, Filesystem $filesystem, $certificatesPath)
{
$this->cli = $cli;
$this->filesystem = $filesystem;
$this->certificatesPath = $certificatesPath;

$this->oName = 'Klever Porter CA Self Signed Organization';
Expand All @@ -30,8 +37,8 @@ public function __construct($certificatesPath)
public function caPaths()
{
return (object) [
'key' => $this->certificatesPath.'/KleverPorterSelfSigned.key',
'pem' => $this->certificatesPath.'/KleverPorterSelfSigned.pem',
'key' => $this->certificatesPath.'/KleverPorterCASelfSigned.key',
'pem' => $this->certificatesPath.'/KleverPorterCASelfSigned.pem',
'srl' => $this->certificatesPath.'/KleverPorterCASelfSigned.srl',
];
}
Expand Down Expand Up @@ -73,7 +80,7 @@ public function build($url)
public function destroy($url)
{
foreach ($this->paths($url) as $path) {
@unlink($path);
$this->filesystem->delete($path);
}
}

Expand All @@ -84,11 +91,11 @@ public function createCa()
{
$paths = $this->caPaths();

if (file_exists($paths->key) || file_exists($paths->pem)) {
if ($this->filesystem->exists($paths->key) || $this->filesystem->exists($paths->pem)) {
return;
}

exec(sprintf(
$this->cli->exec(sprintf(
'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',
$this->oName, $this->cName, $this->email, $paths->key, $paths->pem
));
Expand All @@ -113,11 +120,11 @@ public function createCertificate($url)
$this->createSigningRequest($url, $paths->key, $paths->csr, $paths->conf);

$caSrlParam = ' -CAcreateserial';
if (file_exists($caPaths->srl)) {
if ($this->filesystem->exists($caPaths->srl)) {
$caSrlParam = ' -CAserial '.$caPaths->srl;
}

exec(sprintf(
$this->cli->exec(sprintf(
'openssl x509 -req -sha256 -days 730 -CA %s -CAkey %s%s -in %s -out %s -extensions v3_req -extfile %s',
$caPaths->pem, $caPaths->key, $caSrlParam, $paths->csr, $paths->crt, $paths->conf
));
Expand All @@ -134,7 +141,7 @@ public function createCertificate($url)
*/
public function createConf($path, $url)
{
file_put_contents($path, view('ssl.conf')->withUrl($url)->render());
$this->filesystem->put($path, view('ssl.conf')->withUrl($url)->render());
}

/**
Expand All @@ -146,7 +153,7 @@ public function createConf($path, $url)
*/
public function createPrivateKey($keyPath)
{
exec(sprintf('openssl genrsa -out %s 2048', $keyPath));
$this->cli->exec(sprintf('openssl genrsa -out %s 2048', $keyPath));
}

/**
Expand All @@ -161,7 +168,7 @@ public function createPrivateKey($keyPath)
*/
public function createSigningRequest($url, $keyPath, $csrPath, $confPath)
{
exec(sprintf(
$this->cli->exec(sprintf(
'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',
$keyPath, $csrPath, $this->domain, $url, $url, '@'.$this->domain, $confPath
));
Expand All @@ -172,22 +179,20 @@ public function createSigningRequest($url, $keyPath, $csrPath, $confPath)
*
* @param bool $dropCA
*/
public function clearDirectory($dropCA = false)
public function clearCertificates($dropCA = false)
{
$caPaths = (array) $this->caPaths();

foreach (scandir($this->certificatesPath) as $item) {
if ($item == '.' || $item == '..' || $item == '.gitkeep') {
continue;
}

$current = $this->certificatesPath.'/'.$item;
$files = $this->filesystem
->allFiles($this->certificatesPath);

if (!$dropCA && in_array($current, $caPaths)) {
foreach ($files as $file) {
/** @var SplFileInfo $file */
if (!$dropCA && in_array($file->getPathname(), $caPaths)) {
continue;
}

unlink($current);
$this->filesystem->delete($file->getPathname());
}
}
}
4 changes: 3 additions & 1 deletion tests/Feature/ProviderBaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

namespace Tests\Feature;

use App\Support\Console\Cli;
use App\Support\Ssl\CertificateBuilder;
use Illuminate\Filesystem\Filesystem;
use Tests\BaseTestCase;

class ProviderBaseTest extends BaseTestCase
{
/** @test */
public function it_passes_the_correct_ssl_directory_to_the_certificate_builder()
{
$certificateBuilder = new CertificateBuilder('/my/path/ssl');
$certificateBuilder = new CertificateBuilder(new Cli(), new Filesystem(), '/my/path/ssl');

$expected = [
'key' => '/my/path/ssl/url.key',
Expand Down
121 changes: 94 additions & 27 deletions tests/Unit/Ssl/CertificateBuilderBaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,116 @@

namespace Tests\Unit\Ssl;

use App\Support\Console\Cli;
use App\Support\Ssl\CertificateBuilder;
use Illuminate\Filesystem\Filesystem;
use Tests\BaseTestCase;

class CertificateBuilderBaseTest extends BaseTestCase
{
/**
* @test
*/
public function it_creates_a_certificate()
protected $dir;

public function setUp() : void
{
$dir = storage_path('test_library/ssl');
mkdir($dir, 0755, true);
parent::setUp();

$builder = new CertificateBuilder($dir);
$builder->build('klever.test');
$this->dir = storage_path('test_library/ssl');
mkdir($this->dir, 0755, true);
}

$this->assertFileExists($dir.'/klever.test.conf');
$this->assertFileExists($dir.'/klever.test.crt');
$this->assertFileExists($dir.'/klever.test.csr');
$this->assertFileExists($dir.'/klever.test.key');
/** @test */
public function it_creates_a_certificate()
{
$builder = new CertificateBuilder(new Cli(), new Filesystem(), $this->dir);
$builder->build('klever.test');

$this->assertFileExists($dir.'/KleverPorterSelfSigned.key');
$this->assertFileExists($dir.'/KleverPorterSelfSigned.pem');
$this->assertFileExists($dir.'/KleverPorterSelfSigned.srl');
$this->assertHasCertificates();
$this->assertHasCaCertificates();
}

/** @test */
public function it_removes_a_certificate()
{
$dir = storage_path('test_library/ssl');
mkdir($dir, 0755, true);
$this->dummyCerts();

@touch($dir.'/klever.test.conf');
@touch($dir.'/klever.test.crt');
@touch($dir.'/klever.test.csr');
@touch($dir.'/klever.test.key');

$builder = new CertificateBuilder($dir);
$builder = new CertificateBuilder(new Cli(), new Filesystem(), $this->dir);
$builder->destroy('klever.test');

$this->assertFileNotExists($dir.'/klever.test.conf');
$this->assertFileNotExists($dir.'/klever.test.crt');
$this->assertFileNotExists($dir.'/klever.test.csr');
$this->assertFileNotExists($dir.'/klever.test.key');
$this->assertNoCertificates();
}

/** @test */
public function it_removes_all_certificates_except_ca()
{
$this->dummyCaCerts();
$this->dummyCerts('klever.test');
$this->dummyCerts('klever-one.test');

$builder = new CertificateBuilder(new Cli(), new Filesystem(), $this->dir);
$builder->clearCertificates(/* $dropCa = false */);

$this->assertNoCertificates('klever.test');
$this->assertNoCertificates('klever-one.test');
$this->assertHasCaCertificates();
}

/** @test */
public function it_removes_all_certificates()
{
$this->dummyCaCerts();
$this->dummyCerts('klever.test');
$this->dummyCerts('klever-one.test');

$builder = new CertificateBuilder(new Cli(), new Filesystem(), $this->dir);
$builder->clearCertificates($dropCa = true);

$this->assertNoCertificates('klever.test');
$this->assertNoCertificates('klever-one.test');
$this->assertNoCaCertificates();
}

protected function dummyCerts($url = 'klever.test')
{
@touch($this->dir."/{$url}.conf");
@touch($this->dir."{$url}.crt");
@touch($this->dir."/{$url}.csr");
@touch($this->dir."{$url}.key");
}

protected function dummyCaCerts()
{
@touch($this->dir.'/KleverPorterCASelfSigned.key');
@touch($this->dir.'/KleverPorterCASelfSigned.pem');
@touch($this->dir.'/KleverPorterCASelfSigned.srl');
}

protected function assertHasCertificates($url = 'klever.test')
{
$this->assertFileExists($this->dir."/{$url}.conf");
$this->assertFileExists($this->dir."/{$url}.crt");
$this->assertFileExists($this->dir."/{$url}.csr");
$this->assertFileExists($this->dir."/{$url}.key");
}

protected function assertNoCertificates($url = 'klever.test')
{
$this->assertFileNotExists($this->dir."/{$url}.conf");
$this->assertFileNotExists($this->dir."/{$url}.crt");
$this->assertFileNotExists($this->dir."/{$url}.csr");
$this->assertFileNotExists($this->dir."/{$url}.key");
}

protected function assertHasCaCertificates()
{
$this->assertFileExists($this->dir.'/KleverPorterCASelfSigned.key');
$this->assertFileExists($this->dir.'/KleverPorterCASelfSigned.pem');
$this->assertFileExists($this->dir.'/KleverPorterCASelfSigned.srl');
}

protected function assertNoCaCertificates()
{
$this->assertFileNotExists($this->dir.'/KleverPorterCASelfSigned.key');
$this->assertFileNotExists($this->dir.'/KleverPorterCASelfSigned.pem');
$this->assertFileNotExists($this->dir.'/KleverPorterCASelfSigned.srl');
}
}

0 comments on commit 2d68970

Please sign in to comment.