Skip to content

Commit

Permalink
Base implementation of private packagist for vendors
Browse files Browse the repository at this point in the history
  • Loading branch information
vtsykun committed Jul 9, 2018
1 parent 359285c commit 0e933ff
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 240 deletions.
12 changes: 7 additions & 5 deletions app/Resources/FOSUserBundle/views/Profile/show.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
<hr>
{%- endif %}

{% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: true} %}
{% block content_title %}
<h3 class="font-normal">{{ 'packages.yours'|trans }}</h3>
{% endblock %}
{% endembed %}
{% if is_granted('ROLE_ADMIN') %}
{% embed "PackagistWebBundle:Web:list.html.twig" with {noLayout: 'true', showAutoUpdateWarning: true} %}
{% block content_title %}
<h3 class="font-normal">{{ 'packages.yours'|trans }}</h3>
{% endblock %}
{% endembed %}
{% endif %}
</section>
{% endblock %}
4 changes: 2 additions & 2 deletions app/config/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ security:

firewalls:
packages:
pattern: (^(/packages.json$|/p/|/zipball/|/downloads/|))+
pattern: (^(/packages.json$|/p/|/zipball/|/downloads/))+
api_basic: true
remember_me:
secret: '%remember_me.secret%'
Expand Down Expand Up @@ -58,7 +58,7 @@ security:
- { path: ^/login/, role: IS_AUTHENTICATED_ANONYMOUSLY }

# Packagist
- { path: ^/profile/, role: ROLE_USER }
- { path: (^(/change-password|/profile/))+, role: ROLE_USER }
- { path: (^(/packages.json$|/p/|/zipball/|/downloads/))+, role: ROLE_USER }

# Secured part of the site
Expand Down
68 changes: 60 additions & 8 deletions src/Packagist/WebBundle/Controller/ProviderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,66 @@
class ProviderController extends Controller
{
/**
* @Route("/packages.json", name="packages", defaults={"_format" = "json"})
* @Route("/packages.json", name="root_packages", defaults={"_format" = "json"})
* @Method({"GET"})
*/
public function packagesAction()
{
if (!$user = $this->getUser()) {
$manager = $this->container->get('packagist.package_manager');
$rootPackages = $manager->getRootPackagesJson($this->getUser());

return new JsonResponse($rootPackages);
}

/**
* @Route(
* "/p/providers${hash}.json",
* requirements={"hash"="[a-f0-9]+"},
* name="root_providers", defaults={"_format" = "json"}
* )
*
* @param string $hash
* @return Response
*
* @Method({"GET"})
*/
public function providersAction($hash)
{
$manager = $this->container->get('packagist.package_manager');
$providers = $manager->getProvidersJson($this->getUser(), $hash);
if (!$providers) {
return $this->createNotFound();
}

return new JsonResponse(['status' => 'success']);
return new JsonResponse($providers);
}

/**
* @Route(
* "/p/{package}.json",
* requirements={"package"="[\w+\/\-\$]+"},
* name="root_package", defaults={"_format" = "json"}
* )
*
* @param string $package
* @return Response
*
* @Method({"GET"})
*/
public function packageAction($package)
{
$package = \explode('$', $package);
if (\count($package) !== 2) {
return $this->createNotFound();
}

$manager = $this->container->get('packagist.package_manager');
$package = $manager->getPackageJson($this->getUser(), $package[0], $package[1]);
if (!$package) {
return $this->createNotFound();
}

return new JsonResponse($package);
}

/**
Expand All @@ -42,11 +92,8 @@ public function packagesAction()
public function zipballAction(Package $package, $hash)
{
$config = $this->container->get('packagist.dist_config');
if (false === $this->isGranted('ROLE_USER', $package) || $config->isEnable() === false) {
return new JsonResponse(['status' => 'error', 'message' => ''], 403);
}
if (false === \preg_match('{[a-f0-9]{40}}i', $hash, $match)) {
return new JsonResponse([], 404);
return new JsonResponse(['status' => 'error', 'message' => 'Not Found'], 404);
}

$versions = $package->getVersions()->filter(
Expand All @@ -70,6 +117,11 @@ function (Version $version) use ($match) {
}
}

return new JsonResponse([], 404);
return new JsonResponse(['status' => 'error', 'message' => 'Not Found'], 404);
}

protected function createNotFound()
{
return new JsonResponse(['status' => 'error', 'message' => 'Not Found'], 404);
}
}
63 changes: 3 additions & 60 deletions src/Packagist/WebBundle/Controller/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public function listAction()
{
$qb = $this->getDoctrine()->getRepository('PackagistWebBundle:User')
->createQueryBuilder('u');
$qb->orderBy('u.id', 'DESC');
$qb->where("u.roles NOT LIKE '%ADMIN%'")
->orderBy('u.id', 'DESC');

$paginator = new Pagerfanta(new DoctrineORMAdapter($qb, false));
$paginator->setMaxPerPage(6);
Expand All @@ -69,7 +70,7 @@ public function listAction()
public function updateAction(Request $request, User $user)
{
$token = $this->get('security.token_storage')->getToken();
if ($token && $token->getUsername() !== $user->getUsername()) {
if ($token && $token->getUsername() !== $user->getUsername() && !$user->isAdmin()) {
return $this->handleUpdate($request, $user, 'User has been saved.');
}

Expand All @@ -89,64 +90,6 @@ public function createAction(Request $request)
return $this->handleUpdate($request, $user, 'User has been saved.');
}

/**
* @Route("/users/{name}/disable", name="user_disable")
* @ParamConverter("user", options={"mapping": {"name": "username"}})
*
* @param Request $request
* @param User $user
* @return mixed
*/
public function disableAction(Request $request, User $user)
{
$token = $this->get('security.token_storage')->getToken();
if (!$token || $token->getUsername() !== $user->getUsername()) {
throw new AccessDeniedHttpException('You can not update yourself');
}

$form = $this->createFormBuilder([])->getForm();

$form->submit($request->get('form'));
if ($form->isValid()) {
$user->setEnabled(false);
$this->get('fos_user.user_manager')->updateUser($user);
$this->addFlash('success', $user->getUsername().' has been disable');
}

return $this->redirect(
$this->generateUrl("user_profile", ["name" => $user->getUsername()])
);
}

/**
* @Route("/users/{name}/enable", name="user_enable")
* @ParamConverter("user", options={"mapping": {"name": "username"}})
*
* @param Request $request
* @param User $user
* @return mixed
*/
public function enableAction(Request $request, User $user)
{
$token = $this->get('security.token_storage')->getToken();
if (!$token || $token->getUsername() !== $user->getUsername()) {
throw new AccessDeniedHttpException('You can not update yourself');
}

$form = $this->createFormBuilder([])->getForm();

$form->submit($request->get('form'));
if ($form->isValid()) {
$user->setEnabled(true);
$this->get('fos_user.user_manager')->updateUser($user);
$this->addFlash('success', $user->getUsername().' has been mark as active');
}

return $this->redirect(
$this->generateUrl("user_profile", ["name" => $user->getUsername()])
);
}

protected function handleUpdate(Request $request, User $user, $flashMessage)
{
$form = $this->createForm(CustomerUserType::class, $user);
Expand Down
133 changes: 12 additions & 121 deletions src/Packagist/WebBundle/Controller/WebController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

use Packagist\WebBundle\Form\Model\SearchQuery;
use Packagist\WebBundle\Form\Type\SearchQueryType;
use Pagerfanta\Adapter\ArrayAdapter;
use Pagerfanta\Pagerfanta;
use Predis\Connection\ConnectionException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
Expand Down Expand Up @@ -66,137 +68,26 @@ public function searchFormAction(Request $req)
*/
public function searchAction(Request $req)
{
$packages = [];
$form = $this->createForm(SearchQueryType::class, new SearchQuery());

$filteredOrderBys = $this->getFilteredOrderedBys($req);
$normalizedOrderBys = $this->getNormalizedOrderBys($filteredOrderBys);

$this->computeSearchQuery($req, $filteredOrderBys);

$typeFilter = str_replace('%type%', '', $req->query->get('type'));
$tagsFilter = $req->query->get('tags');

if ($req->getRequestFormat() !== 'json') {
return $this->render('PackagistWebBundle:Web:search.html.twig', [
'packages' => [],
]);
}

if (!$req->query->has('search_query') && !$typeFilter && !$tagsFilter) {
return JsonResponse::create(array(
'error' => 'Missing search query, example: ?q=example'
), 400)->setCallback($req->query->get('callback'));
}

$indexName = $this->container->getParameter('algolia.index_name');
$algolia = $this->get('packagist.algolia.client');
$index = $algolia->initIndex($indexName);
$query = '';
$queryParams = [];

// filter by type
if ($typeFilter) {
$queryParams['filters'][] = 'type:'.$typeFilter;
}

// filter by tags
if ($tagsFilter) {

$tags = array();
foreach ((array) $tagsFilter as $tag) {
$tags[] = 'tags:'.$tag;
}
$queryParams['filters'][] = '(' . implode(' OR ', $tags) . ')';
}

if (!empty($filteredOrderBys)) {
return JsonResponse::create(array(
'status' => 'error',
'message' => 'Search sorting is not available anymore',
), 400)->setCallback($req->query->get('callback'));
}

$form->handleRequest($req);
if ($form->isValid()) {
$query = $form->getData()->getQuery();
}

$perPage = $req->query->getInt('per_page', 15);
if ($perPage <= 0 || $perPage > 100) {
if ($req->getRequestFormat() === 'json') {
return JsonResponse::create(array(
'status' => 'error',
'message' => 'The optional packages per_page parameter must be an integer between 1 and 100 (default: 15)',
), 400)->setCallback($req->query->get('callback'));
$perPage = $req->query->getInt('per_page', 15);
if ($perPage <= 0 || $perPage > 100) {
$perPage = max(0, min(100, $perPage));
}

$perPage = max(0, min(100, $perPage));
$page = $req->query->get('page', 1) - 1;
$packages = $this->getDoctrine()->getRepository('PackagistWebBundle:Package')
->searchPackage($query, $perPage, $page);
}

if (isset($queryParams['filters'])) {
$queryParams['filters'] = implode(' AND ', $queryParams['filters']);
}
$queryParams['hitsPerPage'] = $perPage;
$queryParams['page'] = $req->query->get('page', 1) - 1;

try {
$results = $index->search($query, $queryParams);
} catch (\Throwable $e) {
return JsonResponse::create(array(
'status' => 'error',
'message' => 'Could not connect to the search server',
), 500)->setCallback($req->query->get('callback'));
}

$result = array(
'results' => array(),
'total' => $results['nbHits'],
);

foreach ($results['hits'] as $package) {
if (ctype_digit((string) $package['id'])) {
$url = $this->generateUrl('view_package', array('name' => $package['name']), UrlGeneratorInterface::ABSOLUTE_URL);
} else {
$url = $this->generateUrl('view_providers', array('name' => $package['name']), UrlGeneratorInterface::ABSOLUTE_URL);
}

$row = array(
'name' => $package['name'],
'description' => $package['description'] ?: '',
'url' => $url,
'repository' => $package['repository'],
);
if (ctype_digit((string) $package['id'])) {
$row['downloads'] = $package['meta']['downloads'];
$row['favers'] = $package['meta']['favers'];
} else {
$row['virtual'] = true;
}
if (!empty($package['abandoned'])) {
$row['abandoned'] = $package['replacementPackage'] ?? true;
}
$result['results'][] = $row;
}

if ($results['nbPages'] > $results['page'] + 1) {
$params = array(
'_format' => 'json',
'q' => $form->getData()->getQuery(),
'page' => $results['page'] + 2,
);
if ($tagsFilter) {
$params['tags'] = (array) $tagsFilter;
}
if ($typeFilter) {
$params['type'] = $typeFilter;
}
if ($perPage !== 15) {
$params['per_page'] = $perPage;
}
$result['next'] = $this->generateUrl('search', $params, UrlGeneratorInterface::ABSOLUTE_URL);
}
$paginator = new Pagerfanta(new ArrayAdapter($packages));
$paginator->setMaxPerPage(10);

return JsonResponse::create($result)->setCallback($req->query->get('callback'));
return $this->render('PackagistWebBundle:Web:search.html.twig', ['packages' => $paginator]);
}

/**
Expand Down
13 changes: 11 additions & 2 deletions src/Packagist/WebBundle/Entity/GroupRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function getAllowedPackagesForUser(User $user)
{
$qb = $this->_em->createQueryBuilder();
$qb
->select('p')
->select('p.id')
->distinct(true)
->from('PackagistWebBundle:User', 'u')
->innerJoin('u.groups', 'g')
Expand All @@ -55,7 +55,16 @@ public function getAllowedPackagesForUser(User $user)
->where($qb->expr()->eq('u.id', $user->getId()));

$result = $qb->getQuery()->getResult();
if ($result) {
$qb = $this->_em->createQueryBuilder();
$qb->select('p')
->from('PackagistWebBundle:Package', 'p')
->where('p.id IN (:ids)')
->setParameter('ids', array_column($result, 'id'));

return $result;
return $qb->getQuery()->getResult();
}

return [];
}
}
Loading

0 comments on commit 0e933ff

Please sign in to comment.