Skip to content

Commit c8f82cb

Browse files
authored
Merge pull request #16250 from uberbrady/improve_tls_client_side_file_caching
Instead of saving TLS cache-files on save, cache them when used
2 parents 393118f + b7bd56d commit c8f82cb

File tree

3 files changed

+57
-32
lines changed

3 files changed

+57
-32
lines changed

app/Http/Controllers/SettingsController.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,6 @@ public function postLdapSettings(StoreLdapSettings $request) : RedirectResponse
869869
}
870870

871871
if ($setting->save()) {
872-
$setting->update_client_side_cert_files();
873872
return redirect()->route('settings.ldap.index')
874873
->with('success', trans('admin/settings/message.update.success'));
875874
}

app/Models/Setting.php

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Models;
44

5+
use Carbon\Carbon;
56
use Illuminate\Database\Eloquent\Factories\HasFactory;
67
use Illuminate\Database\Eloquent\Model;
78
use Illuminate\Notifications\Notifiable;
@@ -339,14 +340,41 @@ public static function getLdapSettings(): Collection
339340
return collect($ldapSettings);
340341
}
341342

343+
/**
344+
* For a particular cache-file, refresh it if the settings have
345+
* been updated more recently than the file. Then return the
346+
* full filepath
347+
*/
348+
public static function get_fresh_file_path($attribute, $path)
349+
{
350+
$full_path = storage_path().'/'.$path;
351+
$file_exists = file_exists($full_path);
352+
if ($file_exists) {
353+
$statblock = stat($full_path);
354+
}
355+
if (!$file_exists || Carbon::createFromTimestamp($statblock['mtime']) < Setting::getSettings()->updated_at) {
356+
if (Setting::getSettings()->{$attribute}) {
357+
file_put_contents($full_path, Setting::getSettings()->{$attribute});
358+
} else {
359+
//this doesn't fire when you might expect it to because a lot of the time we do something like:
360+
// if ($settings->ldap_client_tls_cert && ...
361+
// so we never get a chance to 'uncache' the file.
362+
if ($file_exists) {
363+
unlink($full_path);
364+
}
365+
}
366+
}
367+
return $full_path;
368+
}
369+
342370
/**
343371
* Return the filename for the client-side SSL cert
344372
*
345373
* @var string
346374
*/
347375
public static function get_client_side_cert_path()
348376
{
349-
return storage_path().'/ldap_client_tls.cert';
377+
return self::get_fresh_file_path('ldap_client_tls_cert', 'ldap_client_tls.cert');
350378
}
351379

352380
/**
@@ -356,36 +384,7 @@ public static function get_client_side_cert_path()
356384
*/
357385
public static function get_client_side_key_path()
358386
{
359-
return storage_path().'/ldap_client_tls.key';
387+
return self::get_fresh_file_path('ldap_client_tls_key', 'ldap_client_tls.key');
360388
}
361389

362-
public function update_client_side_cert_files()
363-
{
364-
/**
365-
* I'm not sure if it makes sense to have a cert but no key
366-
* nor vice versa, but for now I'm just leaving it like this.
367-
*
368-
* Also, we could easily set this up with an event handler and
369-
* self::saved() or something like that but there's literally only
370-
* one place where we will do that, so I'll just explicitly call
371-
* this method at that spot instead. It'll be easier to debug and understand.
372-
*/
373-
if ($this->ldap_client_tls_cert) {
374-
file_put_contents(self::get_client_side_cert_path(), $this->ldap_client_tls_cert);
375-
} else {
376-
if (file_exists(self::get_client_side_cert_path())) {
377-
unlink(self::get_client_side_cert_path());
378-
}
379-
}
380-
381-
if ($this->ldap_client_tls_key) {
382-
file_put_contents(self::get_client_side_key_path(), $this->ldap_client_tls_key);
383-
} else {
384-
if (file_exists(self::get_client_side_key_path())) {
385-
unlink(self::get_client_side_key_path());
386-
}
387-
}
388-
}
389-
390-
391390
}

tests/Unit/LdapTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Tests\Unit;
44

5+
use App\Models\Setting;
56
use PHPUnit\Framework\Attributes\Group;
67
use App\Models\Ldap;
78
use Tests\TestCase;
@@ -206,4 +207,30 @@ function ($ldapconn, $search_results, $errcode , $matcheddn , $errmsg , $referra
206207
$this->assertEqualsCanonicalizing(["count" => 2], $results);
207208
}
208209

210+
public function testNonexistentTLSFile()
211+
{
212+
$this->settings->enableLdap()->set(['ldap_client_tls_cert' => 'SAMPLE CERT TEXT']);
213+
$certfile = Setting::get_client_side_cert_path();
214+
$this->assertStringEqualsFile($certfile, 'SAMPLE CERT TEXT');
215+
}
216+
217+
public function testStaleTLSFile()
218+
{
219+
file_put_contents(Setting::get_client_side_cert_path(), 'STALE CERT FILE');
220+
sleep(1); // FIXME - this is going to slow down tests
221+
$this->settings->enableLdap()->set(['ldap_client_tls_cert' => 'SAMPLE CERT TEXT']);
222+
$certfile = Setting::get_client_side_cert_path();
223+
$this->assertStringEqualsFile($certfile, 'SAMPLE CERT TEXT');
224+
}
225+
226+
public function testFreshTLSFile()
227+
{
228+
$this->settings->enableLdap()->set(['ldap_client_tls_cert' => 'SAMPLE CERT TEXT']);
229+
$client_side_cert_path = Setting::get_client_side_cert_path();
230+
file_put_contents($client_side_cert_path, 'WEIRDLY UPDATED CERT FILE');
231+
//the system should respect our cache-file, since the settings haven't been updated
232+
$possibly_recached_cert_file = Setting::get_client_side_cert_path(); //this should *NOT* re-cache from the Settings
233+
$this->assertStringEqualsFile($possibly_recached_cert_file, 'WEIRDLY UPDATED CERT FILE');
234+
}
235+
209236
}

0 commit comments

Comments
 (0)