mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-06 23:33:13 +08:00
Restrict interaction with orphan visits when API key has that role
This commit is contained in:
@@ -9,11 +9,15 @@ use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
|
||||
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
|
||||
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
|
||||
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
|
||||
{
|
||||
public function __construct(private readonly VisitRepositoryInterface $repo, private readonly VisitsParams $params)
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VisitRepositoryInterface $repo,
|
||||
private readonly VisitsParams $params,
|
||||
private readonly ?ApiKey $apiKey,
|
||||
) {
|
||||
}
|
||||
|
||||
protected function doCount(): int
|
||||
@@ -21,6 +25,7 @@ class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapte
|
||||
return $this->repo->countOrphanVisits(new VisitsCountFiltering(
|
||||
dateRange: $this->params->dateRange,
|
||||
excludeBots: $this->params->excludeBots,
|
||||
apiKey: $this->apiKey,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -29,6 +34,7 @@ class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapte
|
||||
return $this->repo->findOrphanVisits(new VisitsListFiltering(
|
||||
dateRange: $this->params->dateRange,
|
||||
excludeBots: $this->params->excludeBots,
|
||||
apiKey: $this->apiKey,
|
||||
limit: $length,
|
||||
offset: $offset,
|
||||
));
|
||||
|
||||
@@ -17,6 +17,7 @@ use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
|
||||
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
|
||||
use Shlinkio\Shlink\Core\Visit\Spec\CountOfNonOrphanVisits;
|
||||
use Shlinkio\Shlink\Core\Visit\Spec\CountOfOrphanVisits;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
@@ -139,6 +140,10 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
|
||||
|
||||
public function findOrphanVisits(VisitsListFiltering $filtering): array
|
||||
{
|
||||
if ($filtering->apiKey?->hasRole(Role::NO_ORPHAN_VISITS)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$qb = $this->createAllVisitsQueryBuilder($filtering);
|
||||
$qb->andWhere($qb->expr()->isNull('v.shortUrl'));
|
||||
return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
|
||||
@@ -146,6 +151,10 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
|
||||
|
||||
public function countOrphanVisits(VisitsCountFiltering $filtering): int
|
||||
{
|
||||
if ($filtering->apiKey?->hasRole(Role::NO_ORPHAN_VISITS)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) $this->matchSingleScalarResult(new CountOfOrphanVisits($filtering));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\Core\Visit;
|
||||
|
||||
use Shlinkio\Shlink\Core\Model\BulkDeleteResult;
|
||||
use Shlinkio\Shlink\Core\Visit\Repository\VisitDeleterRepositoryInterface;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class VisitsDeleter implements VisitsDeleterInterface
|
||||
@@ -16,7 +17,7 @@ class VisitsDeleter implements VisitsDeleterInterface
|
||||
|
||||
public function deleteOrphanVisits(?ApiKey $apiKey = null): BulkDeleteResult
|
||||
{
|
||||
// TODO Check API key has permissions for orphan visits
|
||||
return new BulkDeleteResult($this->repository->deleteOrphanVisits());
|
||||
$affectedItems = $apiKey?->hasRole(Role::NO_ORPHAN_VISITS) ? 0 : $this->repository->deleteOrphanVisits();
|
||||
return new BulkDeleteResult($affectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,13 @@ class VisitsStatsHelper implements VisitsStatsHelperInterface
|
||||
|
||||
return new VisitsStats(
|
||||
nonOrphanVisitsTotal: $visitsRepo->countNonOrphanVisits(VisitsCountFiltering::withApiKey($apiKey)),
|
||||
orphanVisitsTotal: $visitsRepo->countOrphanVisits(new VisitsCountFiltering()),
|
||||
orphanVisitsTotal: $visitsRepo->countOrphanVisits(VisitsCountFiltering::withApiKey($apiKey)),
|
||||
nonOrphanVisitsNonBots: $visitsRepo->countNonOrphanVisits(
|
||||
new VisitsCountFiltering(excludeBots: true, apiKey: $apiKey),
|
||||
),
|
||||
orphanVisitsNonBots: $visitsRepo->countOrphanVisits(new VisitsCountFiltering(excludeBots: true)),
|
||||
orphanVisitsNonBots: $visitsRepo->countOrphanVisits(
|
||||
new VisitsCountFiltering(excludeBots: true, apiKey: $apiKey),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,12 +116,12 @@ class VisitsStatsHelper implements VisitsStatsHelperInterface
|
||||
/**
|
||||
* @return Visit[]|Paginator
|
||||
*/
|
||||
public function orphanVisits(VisitsParams $params): Paginator
|
||||
public function orphanVisits(VisitsParams $params, ?ApiKey $apiKey = null): Paginator
|
||||
{
|
||||
/** @var VisitRepositoryInterface $repo */
|
||||
$repo = $this->em->getRepository(Visit::class);
|
||||
|
||||
return $this->createPaginator(new OrphanVisitsPaginatorAdapter($repo, $params), $params);
|
||||
return $this->createPaginator(new OrphanVisitsPaginatorAdapter($repo, $params, $apiKey), $params);
|
||||
}
|
||||
|
||||
public function nonOrphanVisits(VisitsParams $params, ?ApiKey $apiKey = null): Paginator
|
||||
|
||||
@@ -43,7 +43,7 @@ interface VisitsStatsHelperInterface
|
||||
/**
|
||||
* @return Visit[]|Paginator
|
||||
*/
|
||||
public function orphanVisits(VisitsParams $params): Paginator;
|
||||
public function orphanVisits(VisitsParams $params, ?ApiKey $apiKey = null): Paginator;
|
||||
|
||||
/**
|
||||
* @return Visit[]|Paginator
|
||||
|
||||
Reference in New Issue
Block a user