diff --git a/app/Http/Controllers/Dashboard/SettingsController.php b/app/Http/Controllers/Dashboard/SettingsController.php index 28e81efdf235..aeb6cabc14ef 100644 --- a/app/Http/Controllers/Dashboard/SettingsController.php +++ b/app/Http/Controllers/Dashboard/SettingsController.php @@ -115,6 +115,11 @@ public function __construct() ], ]; + // Remove the credits link if we cannot look them up + if (Config::get('cachet.internet_lookups') === false) { + unset($this->subMenu['credits']); + } + View::share([ 'subTitle' => trans('dashboard.settings.settings'), 'subMenu' => $this->subMenu, @@ -244,6 +249,10 @@ public function showStylesheetView() */ public function showCreditsView() { + if (Config::get('cachet.internet_lookups') === false) { + abort(403, 'Outbound Internet Lookups Disabled'); + } + $this->subMenu['credits']['active'] = true; $credits = app(Credits::class)->latest(); diff --git a/app/Integrations/Core/Credits.php b/app/Integrations/Core/Credits.php index 3df1671ddb1f..382336eb0026 100644 --- a/app/Integrations/Core/Credits.php +++ b/app/Integrations/Core/Credits.php @@ -52,17 +52,27 @@ class Credits implements CreditsContract */ protected $url; + /** + * Are outbound HTTP requests to the internet allowed by + * this installation + * + * @var bool + */ + protected $enabled; + /** * Creates a new credits instance. * * @param \Illuminate\Contracts\Cache\Repository $cache + * @param bool $enabled * @param string|null $url * * @return void */ - public function __construct(Repository $cache, $url = null) + public function __construct(Repository $cache, bool $enabled = true, $url = null) { $this->cache = $cache; + $this->enabled = $enabled; $this->url = $url ?: static::URL; } @@ -73,6 +83,10 @@ public function __construct(Repository $cache, $url = null) */ public function latest() { + if (!$this->enabled) { + return null; + } + $result = $this->cache->remember('credits', 2880, function () { try { return json_decode((new Client())->get($this->url, [ diff --git a/app/Integrations/GitHub/Releases.php b/app/Integrations/GitHub/Releases.php index 7b3cb8314718..84d77c1c6af5 100644 --- a/app/Integrations/GitHub/Releases.php +++ b/app/Integrations/GitHub/Releases.php @@ -62,6 +62,14 @@ class Releases implements ReleasesContract */ protected $url; + /** + * Are outbound HTTP requests to the internet allowed by + * this installation + * + * @var bool + */ + protected $enabled; + /** * Creates a new releases instance. * @@ -72,10 +80,11 @@ class Releases implements ReleasesContract * * @return void */ - public function __construct(Client $client, Repository $cache, $token = null, $url = null) + public function __construct(Client $client, Repository $cache, bool $enabled = true, $token = null, $url = null) { $this->client = $client; $this->cache = $cache; + $this->enabled = $enabled; $this->token = $token; $this->url = $url ?: static::URL; } @@ -87,6 +96,10 @@ public function __construct(Client $client, Repository $cache, $token = null, $u */ public function latest() { + if (!$this->enabled) { + return null; + } + $release = $this->cache->remember('release.latest', 720, function () { $headers = ['Accept' => 'application/vnd.github.v3+json', 'User-Agent' => defined('CACHET_VERSION') ? 'cachet/'.constant('CACHET_VERSION') : 'cachet']; diff --git a/app/Providers/IntegrationServiceProvider.php b/app/Providers/IntegrationServiceProvider.php index 34e1253b3061..1f04353b239e 100644 --- a/app/Providers/IntegrationServiceProvider.php +++ b/app/Providers/IntegrationServiceProvider.php @@ -67,8 +67,9 @@ protected function registerCredits() { $this->app->singleton(CreditsContract::class, function ($app) { $cache = $app['cache.store']; + $enabled = $app['config']->get('cachet.internet_lookups'); - return new Credits($cache); + return new Credits($cache, $enabled); }); } @@ -97,9 +98,10 @@ protected function registerReleases() $this->app->singleton(ReleasesContract::class, function ($app) { $client = $app->make(Client::class); $cache = $app['cache.store']; + $enabled = $app['config']->get('cachet.internet_lookups'); $token = $app['config']->get('services.github.token'); - return new Releases($client, $cache, $token); + return new Releases($client, $cache, $enabled, $token); }); } } diff --git a/config/cachet.php b/config/cachet.php index dd3f27051aa9..c163d63dc58d 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -46,4 +46,16 @@ 'beacon' => env('CACHET_BEACON', true), + /* + |-------------------------------------------------------------------------- + | Internet Lookups + |-------------------------------------------------------------------------- + | + | Should Cachet make outbound HTTP requests to the internet to load external + | resources. Turn off if you're behind a Firewall with no HTTP(S) egress. + | + */ + + 'internet_lookups' => env('CACHET_INTERNET_LOOKUPS', true), + ]; diff --git a/tests/Integrations/GitHub/ReleasesTest.php b/tests/Integrations/GitHub/ReleasesTest.php index 586a094ba5a0..8a614b0e5a18 100644 --- a/tests/Integrations/GitHub/ReleasesTest.php +++ b/tests/Integrations/GitHub/ReleasesTest.php @@ -9,6 +9,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; +use GuzzleHttp\Middleware; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Exception\RequestException; @@ -16,6 +17,7 @@ class ReleasesTest extends AbstractTestCase { + private $request_history = []; /** * Creates Mock Guzzle client * @@ -23,9 +25,12 @@ class ReleasesTest extends AbstractTestCase */ private function createMockClient(array $mock_responses) { + $history = Middleware::history($this->request_history); $mock = new MockHandler($mock_responses); $handlerStack = HandlerStack::create($mock); + $handlerStack->push($history); + $client = new Client(['handler' => $handlerStack]); $this->app->instance(Client::class, $client); } @@ -56,4 +61,17 @@ public function test_latest_returns_empty_on_timeout() $latest = $this->app->make(Releases::class)->latest(); $this->assertEquals(null, $latest); } + + public function test_latest_does_not_make_http_request_when_enabled_false() + { + $mock_responses = [ + new Response(200), + ]; + $this->createMockClient($mock_responses); + + Cache::tags('release.latest')->flush(); + $latest = $this->app->makeWith(Releases::class, ['enabled' => false])->latest(); + $this->assertEquals(null, $latest); + $this->assertEquals(0, count($this->request_history)); + } }