Skip to content

Commit

Permalink
Allow public access to some packages via a sub-repository
Browse files Browse the repository at this point in the history
  • Loading branch information
vtsykun committed Nov 7, 2024
1 parent 2b39336 commit 55382f1
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 11 deletions.
6 changes: 6 additions & 0 deletions src/Controller/ZipballController.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public function zipballList(Request $request): Response
requirements: ['package' => '%package_name_regex%', 'hash' => '[a-f0-9]{40}(\.?[A-Za-z\.]+?)?'],
methods: ['GET']
)]
#[Route(
'/{slug}/zipball/{package}/{hash}',
name: 'download_dist_package_slug',
requirements: ['package' => '%package_name_regex%', 'hash' => '[a-f0-9]{40}(\.?[A-Za-z\.]+?)?'],
methods: ['GET']
)]
public function zipballAction(#[Vars('name')] Package $package, string $hash): Response
{
if ((false === $this->dm->isEnabled() && false === RepTypes::isBuildInDist($package->getRepoType()))
Expand Down
14 changes: 14 additions & 0 deletions src/Entity/SubRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class SubRepository
#[ORM\Column(type: 'json', nullable: true)]
private ?array $packages = null;

#[ORM\Column(name: 'public_access', type: 'boolean', nullable: true)]
private ?bool $publicAccess = null;

/** @internal */
private ?array $cachedIds = null;

Expand Down Expand Up @@ -129,4 +132,15 @@ public function setCachedIds(?array $cachedIds): static
$this->cachedIds = $cachedIds;
return $this;
}

public function isPublicAccess(): ?bool
{
return $this->publicAccess;
}

public function setPublicAccess(?bool $publicAccess): static
{
$this->publicAccess = $publicAccess;
return $this;
}
}
5 changes: 3 additions & 2 deletions src/EventListener/ProtectHostListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ class ProtectHostListener
'root_package_v2' => 1,
'download_dist_package' => 1,
'track_download' => 1,
'track_download_batch' =>1,
'root_packages_slug' =>1,
'track_download_batch' => 1,
'root_packages_slug' => 1,
'root_providers_slug' => 1,
'root_package_slug' => 1,
'root_package_v2_slug' => 1,
'download_dist_package_slug' => 1,
'mirror_root' => 1,
'mirror_metadata_v2' => 1,
'mirror_metadata_v1' => 1,
Expand Down
5 changes: 5 additions & 0 deletions src/Form/Type/SubRepositoryType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Packeton\Entity\Package;
use Packeton\Entity\SubRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
Expand Down Expand Up @@ -52,6 +53,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'label' => 'Subdomain or separate hostname',
'attr' => ['placeholder' => "e.g.: repo1.example.com\nrepo2.example.com", 'rows' => 4]
])
->add('publicAccess', CheckboxType::class, [
'required' => false,
'label' => 'Allow public access',
])
->add('packages', ChoiceType::class, [
'required' => false,
'multiple' => true,
Expand Down
2 changes: 1 addition & 1 deletion src/Model/PatUserScores.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class PatUserScores
'metadata' => [
'root_packages', 'root_providers', 'metadata_changes', 'root_package', 'root_package_v2', 'download_dist_package',
'track_download', 'track_download_batch',
'root_packages_slug', 'root_providers_slug', 'root_package_slug', 'root_package_v2_slug',
'root_packages_slug', 'root_providers_slug', 'root_package_slug', 'root_package_v2_slug', 'download_dist_package_slug'
],
'mirror:read' => ['mirror_root', 'mirror_metadata_v2', 'mirror_metadata_v1', 'mirror_zipball', 'mirror_provider_includes'],
'mirror:all' => ['@mirror:read'],
Expand Down
2 changes: 1 addition & 1 deletion src/Package/InMemoryDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private function dumpRootPackages(?UserInterface $user = null, ?int $apiVersion

if ($this->distConfig->mirrorEnabled()) {
$ref = '0000000000000000000000000000000000000000.zip';
$zipball = $this->router->generate('download_dist_package', ['package' => 'VND/PKG', 'hash' => $ref]);
$zipball = $slug . $this->router->generate('download_dist_package', ['package' => 'VND/PKG', 'hash' => $ref]);
$rootFile['mirrors'][] = ['dist-url' => \str_replace(['VND/PKG', $ref], ['%package%', '%reference%.%type%'], $zipball), 'preferred' => true];
}

Expand Down
2 changes: 1 addition & 1 deletion src/Repository/SubEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public function getSubRepositoryData(): array
{
$data = $this->createQueryBuilder('s')
->resetDQLPart('select')
->select(['s.id', 's.slug', 's.urls', 's.name'])
->select(['s.id', 's.slug', 's.urls', 's.name', 's.publicAccess as public'])
->getQuery()
->getArrayResult();

Expand Down
31 changes: 28 additions & 3 deletions src/Security/Acl/SubRepoGrantVoter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Packeton\Security\Acl;

use Packeton\Service\SubRepositoryHelper;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface;
Expand All @@ -15,15 +16,21 @@ class SubRepoGrantVoter implements CacheableVoterInterface
'root_providers_slug' => 1,
'root_package_slug' => 1,
'root_package_v2_slug' => 1,
'download_dist_package_slug' => 1,
];

public function __construct(
private readonly SubRepositoryHelper $helper
) {
}

/**
* {@inheritdoc}
*/
public function vote(TokenInterface $token, mixed $subject, array $attributes): int
public function vote(TokenInterface $token, mixed $request, array $attributes): int
{
if ($subject instanceof Request && isset(self::$subRoutes[$subject->attributes->get('_route')])) {
return $token->getUser() ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
if ($request instanceof Request && isset(self::$subRoutes[$request->attributes->get('_route')])) {
return $token->getUser() || $this->isPublicSubRepo($request) ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
}

return self::ACCESS_ABSTAIN;
Expand All @@ -44,4 +51,22 @@ public function supportsType(string $subjectType): bool
{
return $subjectType === Request::class;
}

private function isPublicSubRepo(Request $request): bool
{
if (null !== ($subRepo = $this->getSubRepoForRequest($request))) {
return $this->helper->isPublicAccess($subRepo);
}
return false;
}

private function getSubRepoForRequest(Request $request): ?int
{
$route = (string) $request->attributes->get('_route');
if ($request->attributes->has('slug') && (SubRepoGrantVoter::$subRoutes[$route] ?? null)) {
return $this->helper->getBySlug($request->attributes->get('slug'));
}

return $this->helper->getByHost($request->getHost());
}
}
10 changes: 7 additions & 3 deletions src/Service/SubRepositoryHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ public function isAutoHost(): bool
return $req->attributes->get('_sub_repo_type') === SubRepository::AUTO_HOST;
}

public function isPublicAccess(?int $subRepo = null): bool
{
$subRepo ??= $this->getSubrepositoryId();
return $this->getData()[$subRepo]['public'] ?? false;
}

public static function applyCondition(QueryBuilder $qb, ?array $allowed): QueryBuilder
{
if ($allowed === null) {
Expand Down Expand Up @@ -161,8 +167,6 @@ public function getTwigData(?UserInterface $user = null): array

protected function getData(): array
{
return $this->cache->get('sub_repos_list', function () {
return $this->registry->getRepository(SubRepository::class)->getSubRepositoryData();
});
return $this->cache->get('sub_repos_list', fn () => $this->registry->getRepository(SubRepository::class)->getSubRepositoryData());
}
}

0 comments on commit 55382f1

Please sign in to comment.