-
-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #152 from vtsykun/feat/customer-page
Multi host web-ui protection for agencies
- Loading branch information
Showing
7 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Custom landing page | ||
|
||
If you are distributing packages to your customers, | ||
you may want to create a separate domain for Composer metadata-only to hide | ||
the default web interface and login page. | ||
|
||
Add following lines to you configuration. `config.yaml or config/packages/*.yaml` | ||
|
||
```yaml | ||
packeton: | ||
web_protection: | ||
## Multi host protection, disable web-ui if host !== app.example.com and ips != 127.0.0.1, 10.9.1.0/24 | ||
## But the repo metadata will be available for all hosts and ips. | ||
repo_hosts: ['*', '!app.example.com'] | ||
allow_ips: '127.0.0.1, 10.9.1.0/24' | ||
status_code: 402 | ||
custom_page: > # Custom landing non-auth page. Path or HTML | ||
<html> | ||
<head><title>402 Payment Required</title></head> | ||
<body> | ||
<center><h1>402 Payment Required</h1></center> | ||
<hr><center>nginx</center> | ||
</body> | ||
</html> | ||
``` | ||
Where `custom_page` html content or path to html page. | ||
|
||
Here all hosts will be hidden under this page (if ip is not match or host != app.example.com). | ||
|
||
`app.example.com` - this is host for default Web-UI. | ||
|
||
### Example 2 | ||
|
||
```yaml | ||
web_protection: | ||
repo_hosts: ['repo.example.com'] | ||
``` | ||
|
||
Here Web-UI will be hidden for `repo_hosts` host `repo.example.com`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Packeton\EventListener; | ||
|
||
use Symfony\Component\DependencyInjection\Attribute\Autowire; | ||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener; | ||
use Symfony\Component\HttpFoundation\IpUtils; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Event\RequestEvent; | ||
|
||
class ProtectHostListener | ||
{ | ||
public static $allowedRoutes = [ | ||
'root_packages' => 1, | ||
'root_providers' => 1, | ||
'metadata_changes' => 1, | ||
'root_package' => 1, | ||
'root_package_v2' => 1, | ||
'download_dist_package' => 1, | ||
'track_download' => 1, | ||
'track_download_batch' =>1, | ||
'root_packages_slug' =>1, | ||
'root_providers_slug' => 1, | ||
'root_package_slug' => 1, | ||
'root_package_v2_slug' => 1, | ||
'mirror_root' => 1, | ||
'mirror_metadata_v2' => 1, | ||
'mirror_metadata_v1' => 1, | ||
'mirror_zipball' => 1, | ||
'mirror_provider_includes' => 1, | ||
]; | ||
|
||
public function __construct( | ||
#[Autowire(param: 'packeton_web_protection')] | ||
protected ?array $protection = null | ||
) { | ||
} | ||
|
||
#[AsEventListener('kernel.request', priority: 30)] | ||
public function onKernelRequest(RequestEvent $event): void | ||
{ | ||
if (empty($this->protection)) { | ||
return; | ||
} | ||
|
||
$request = $event->getRequest(); | ||
$route = $request->attributes->get('_route'); | ||
if (isset(static::$allowedRoutes[$route])) { | ||
return; | ||
} | ||
|
||
if ($protectedHosts = ($this->protection['repo_hosts'] ?? [])) { | ||
$host = $request->getHost(); | ||
if (in_array($host, $protectedHosts, true) || (in_array('*', $protectedHosts, true) && !in_array('!'.$host, $protectedHosts, true))) { | ||
$this->terminate($event); | ||
} | ||
} | ||
|
||
if ($allowIps = ($this->protection['allow_ips'] ?? null)) { | ||
$allowIps = array_map('trim', explode(',', $allowIps)); | ||
if (false === IpUtils::checkIp($request->getClientIp() ?? '', $allowIps)) { | ||
$this->terminate($event); | ||
} | ||
} | ||
} | ||
|
||
private function terminate(RequestEvent $event): void | ||
{ | ||
$route = $event->getRequest()->attributes->get('_route'); | ||
|
||
$response = new JsonResponse(['error' => 'Not Found'], 404); | ||
if ($route === 'home' && ($customPage = $this->protection['custom_page'] ?? null)) { | ||
$customPage = is_file($customPage) ? file_get_contents($customPage) : $customPage; | ||
$response = new Response($customPage, $this->protection['status_code'] ?? 200); | ||
if ($contentType = $this->protection['content_type'] ?? null) { | ||
$response->headers->set('content-type', $contentType); | ||
} | ||
|
||
$event->getRequest()->attributes->set('_format', 'X-Debug'); | ||
} | ||
|
||
$event->setResponse($response); | ||
} | ||
} |