release 2.0 beta 2 (#3757)

* do not traverse into invoice template subdirectories (#3735)
* fix security open api definition
* fix currency can be null, removed fluent interface
* merged release 1.30.3
* allow to pre-fill timesheet metafields via URL
* fix api description
* added test accounts with simpler names and password
* upgrade to Symfony 6.2
* removed FrameworkExtraBundle (by Sensio) and replaced with new native SF annotations
* fixed symfony 6.2 deprecations
* fixed #3768
This commit is contained in:
Kevin Papst
2023-01-18 14:47:48 +01:00
committed by GitHub
parent 6e0500972e
commit 0e91dd886e
141 changed files with 1600 additions and 1415 deletions

View File

@@ -35,18 +35,19 @@ use App\Repository\TeamRepository;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to manage activities.
*/
#[Route(path: '/admin/activity')]
#[Security("is_granted('view_activity') or is_granted('view_teamlead_activity') or is_granted('view_team_activity')")]
#[IsGranted(new Expression("is_granted('view_activity') or is_granted('view_teamlead_activity') or is_granted('view_team_activity')"))]
final class ActivityController extends AbstractController
{
public function __construct(private ActivityRepository $repository, private SystemConfiguration $configuration, private EventDispatcherInterface $dispatcher, private ActivityService $activityService)
@@ -122,7 +123,7 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/{id}/details', name: 'activity_details', methods: ['GET', 'POST'])]
#[Security("is_granted('view', activity)")]
#[IsGranted('view', 'activity')]
public function detailsAction(Activity $activity, TeamRepository $teamRepository, ActivityRateRepository $rateRepository, ActivityStatisticService $statisticService)
{
$event = new ActivityMetaDefinitionEvent($activity);
@@ -172,14 +173,14 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/{id}/rate/{rate}', name: 'admin_activity_rate_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', activity)")]
#[IsGranted('edit', 'activity')]
public function editRateAction(Activity $activity, ActivityRate $rate, Request $request, ActivityRateRepository $repository): Response
{
return $this->rateFormAction($activity, $rate, $request, $repository, $this->generateUrl('admin_activity_rate_edit', ['id' => $activity->getId(), 'rate' => $rate->getId()]));
}
#[Route(path: '/{id}/rate', name: 'admin_activity_rate_add', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', activity)")]
#[IsGranted('edit', 'activity')]
public function addRateAction(Activity $activity, Request $request, ActivityRateRepository $repository): Response
{
$rate = new ActivityRate();
@@ -216,14 +217,14 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/create/{project}', name: 'admin_activity_create_with_project', methods: ['GET', 'POST'])]
#[Security("is_granted('create_activity')")]
#[IsGranted('create_activity')]
public function createWithProjectAction(Request $request, Project $project): Response
{
return $this->createActivity($request, $project);
}
#[Route(path: '/create', name: 'admin_activity_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_activity')")]
#[IsGranted('create_activity')]
public function createAction(Request $request): Response
{
return $this->createActivity($request, null);
@@ -258,7 +259,7 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/{id}/permissions', name: 'admin_activity_permissions', methods: ['GET', 'POST'])]
#[Security("is_granted('permissions', activity)")]
#[IsGranted('permissions', 'activity')]
public function teamPermissionsAction(Activity $activity, Request $request): Response
{
$form = $this->createForm(ActivityTeamPermissionForm::class, $activity, [
@@ -291,7 +292,8 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/{id}/create_team', name: 'activity_team_create', methods: ['GET'])]
#[Security("is_granted('create_team') and is_granted('permissions', activity)")]
#[IsGranted('create_team')]
#[IsGranted('permissions', 'activity')]
public function createDefaultTeamAction(Activity $activity, TeamRepository $teamRepository): Response
{
$defaultTeam = $teamRepository->findOneBy(['name' => $activity->getName()]);
@@ -315,7 +317,7 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/{id}/edit', name: 'admin_activity_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', activity)")]
#[IsGranted('edit', 'activity')]
public function editAction(Activity $activity, Request $request): Response
{
$event = new ActivityMetaDefinitionEvent($activity);
@@ -343,7 +345,7 @@ final class ActivityController extends AbstractController
}
#[Route(path: '/{id}/delete', name: 'admin_activity_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('delete', activity)")]
#[IsGranted('delete', 'activity')]
public function deleteAction(Activity $activity, Request $request, ActivityStatisticService $statisticService): Response
{
$stats = $statisticService->getActivityStatistics($activity);

View File

@@ -12,10 +12,10 @@ namespace App\Controller\Auth;
use App\Configuration\SamlConfigurationInterface;
use App\Saml\SamlAuthFactory;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
#[Route(path: '/saml')]
final class SamlController extends AbstractController

View File

@@ -15,16 +15,16 @@ use App\Entity\User;
use App\Form\CalendarForm;
use App\Timesheet\TrackingModeService;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to display calendars.
*/
#[Route(path: '/calendar')]
#[Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")]
#[IsGranted('IS_AUTHENTICATED_REMEMBERED')]
final class CalendarController extends AbstractController
{
public function __construct(private CalendarService $calendarService, private SystemConfiguration $configuration, private TrackingModeService $service)

View File

@@ -38,8 +38,8 @@ use App\Utils\DataTable;
use App\Utils\FileHelper;
use App\Utils\PageSetup;
use JeroenDesloovere\VCard\VCard;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -48,12 +48,13 @@ use Symfony\Component\Intl\Countries;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to manage customer in the admin part of the site.
*/
#[Route(path: '/admin/customer')]
#[Security("is_granted('view_customer') or is_granted('view_teamlead_customer') or is_granted('view_team_customer')")]
#[IsGranted(new Expression("is_granted('view_customer') or is_granted('view_teamlead_customer') or is_granted('view_team_customer')"))]
final class CustomerController extends AbstractController
{
public function __construct(private CustomerRepository $repository, private EventDispatcherInterface $dispatcher)
@@ -139,7 +140,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/create', name: 'admin_customer_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_customer')")]
#[IsGranted('create_customer')]
public function createAction(Request $request, CustomerService $customerService)
{
$customer = $customerService->createNewCustomer('');
@@ -148,7 +149,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/permissions', name: 'admin_customer_permissions', methods: ['GET', 'POST'])]
#[Security("is_granted('permissions', customer)")]
#[IsGranted('permissions', 'customer')]
public function teamPermissionsAction(Customer $customer, Request $request)
{
$form = $this->createForm(CustomerTeamPermissionForm::class, $customer, [
@@ -181,7 +182,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/comment_delete/{token}', name: 'customer_comment_delete', methods: ['GET'])]
#[Security("is_granted('edit', comment.getCustomer()) and is_granted('comments', comment.getCustomer())")]
#[IsGranted(new Expression("is_granted('edit', subject.getCustomer()) and is_granted('comments', subject.getCustomer())"), 'comment')]
public function deleteCommentAction(CustomerComment $comment, string $token, CsrfTokenManagerInterface $csrfTokenManager)
{
$customerId = $comment->getCustomer()->getId();
@@ -204,7 +205,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/comment_add', name: 'customer_comment_add', methods: ['POST'])]
#[Security("is_granted('comments', customer)")]
#[IsGranted('comments', 'customer')]
public function addCommentAction(Customer $customer, Request $request)
{
$comment = new CustomerComment($customer);
@@ -224,7 +225,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/comment_pin/{token}', name: 'customer_comment_pin', methods: ['GET'])]
#[Security("is_granted('edit', comment.getCustomer()) and is_granted('comments', comment.getCustomer())")]
#[IsGranted(new Expression("is_granted('edit', subject.getCustomer()) and is_granted('comments', subject.getCustomer())"), 'comment')]
public function pinCommentAction(CustomerComment $comment, string $token, CsrfTokenManagerInterface $csrfTokenManager)
{
$customerId = $comment->getCustomer()->getId();
@@ -248,7 +249,8 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/create_team', name: 'customer_team_create', methods: ['GET'])]
#[Security("is_granted('create_team') and is_granted('permissions', customer)")]
#[IsGranted('create_team')]
#[IsGranted('permissions', 'customer')]
public function createDefaultTeamAction(Customer $customer, TeamRepository $teamRepository)
{
$defaultTeam = $teamRepository->findOneBy(['name' => $customer->getName()]);
@@ -272,7 +274,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/projects/{page}', defaults: ['page' => 1], name: 'customer_projects', methods: ['GET', 'POST'])]
#[Security("is_granted('view', customer)")]
#[IsGranted('view', 'customer')]
public function projectsAction(Customer $customer, int $page, ProjectRepository $projectRepository)
{
$query = new ProjectQuery();
@@ -295,7 +297,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/details', name: 'customer_details', methods: ['GET', 'POST'])]
#[Security("is_granted('view', customer)")]
#[IsGranted('view', 'customer')]
public function detailsAction(Customer $customer, TeamRepository $teamRepository, CustomerRateRepository $rateRepository, CustomerStatisticService $statisticService)
{
$event = new CustomerMetaDefinitionEvent($customer);
@@ -362,7 +364,7 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/vcard', name: 'customer_vcard', methods: ['GET'])]
#[Security("is_granted('view', customer)")]
#[IsGranted('view', 'customer')]
public function downloadVCard(Customer $customer): Response
{
$vcard = new VCard();
@@ -415,14 +417,14 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/rate/{rate}', name: 'admin_customer_rate_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', customer)")]
#[IsGranted('edit', 'customer')]
public function editRateAction(Customer $customer, CustomerRate $rate, Request $request, CustomerRateRepository $repository): Response
{
return $this->rateFormAction($customer, $rate, $request, $repository, $this->generateUrl('admin_customer_rate_edit', ['id' => $customer->getId(), 'rate' => $rate->getId()]));
}
#[Route(path: '/{id}/rate', name: 'admin_customer_rate_add', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', customer)")]
#[IsGranted('edit', 'customer')]
public function addRateAction(Customer $customer, Request $request, CustomerRateRepository $repository): Response
{
$rate = new CustomerRate();
@@ -459,14 +461,14 @@ final class CustomerController extends AbstractController
}
#[Route(path: '/{id}/edit', name: 'admin_customer_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', customer)")]
#[IsGranted('edit', 'customer')]
public function editAction(Customer $customer, Request $request)
{
return $this->renderCustomerForm($customer, $request);
}
#[Route(path: '/{id}/delete', name: 'admin_customer_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('delete', customer)")]
#[IsGranted('delete', 'customer')]
public function deleteAction(Customer $customer, Request $request, CustomerStatisticService $statisticService)
{
$stats = $statisticService->getCustomerStatistics($customer);

View File

@@ -16,19 +16,19 @@ use App\Repository\BookmarkRepository;
use App\Utils\PageSetup;
use App\Widget\WidgetInterface;
use App\Widget\WidgetService;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Dashboard controller for the admin area.
*/
#[Route(path: '/dashboard')]
#[Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")]
#[IsGranted('IS_AUTHENTICATED_REMEMBERED')]
final class DashboardController extends AbstractController
{
public const BOOKMARK_TYPE = 'dashboard';

View File

@@ -13,16 +13,16 @@ use App\Utils\FileHelper;
use App\Utils\PageSetup;
use App\Utils\ReleaseVersion;
use Composer\InstalledVersions;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
#[Route(path: '/doctor')]
#[Security("is_granted('system_information')")]
#[IsGranted('system_information')]
final class DoctorController extends AbstractController
{
/**
@@ -51,7 +51,7 @@ final class DoctorController extends AbstractController
}
#[Route(path: '/flush-log/{token}', name: 'doctor_flush_log', methods: ['GET'])]
#[Security("is_granted('system_configuration')")]
#[IsGranted('system_configuration')]
public function deleteLogfileAction(string $token, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('doctor.flush_log', $token))) {

View File

@@ -16,17 +16,17 @@ use App\Export\TooManyItemsExportException;
use App\Form\Toolbar\ExportToolbarForm;
use App\Repository\Query\ExportQuery;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to export timesheet data.
*/
#[Route(path: '/export')]
#[Security("is_granted('create_export')")]
#[IsGranted('create_export')]
final class ExportController extends AbstractController
{
public function __construct(private ServiceExport $export)

View File

@@ -11,23 +11,23 @@ namespace App\Controller;
use App\Entity\Timesheet;
use App\Timesheet\FavoriteRecordService;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/favorite')]
#[Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")]
#[IsGranted('IS_AUTHENTICATED_REMEMBERED')]
final class FavoriteController extends AbstractController
{
#[Route(path: '/timesheet/', name: 'favorites_timesheets', methods: ['GET'])]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
public function favoriteAction(): Response
{
return $this->render('partials/recent-activities.html.twig');
}
#[Route(path: '/timesheet/add/{id}', name: 'favorites_timesheets_add', methods: ['GET'])]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
public function add(Timesheet $timesheet, FavoriteRecordService $favoriteRecordService): Response
{
$favoriteRecordService->addFavorite($timesheet);
@@ -36,7 +36,7 @@ final class FavoriteController extends AbstractController
}
#[Route(path: '/timesheet/remove/{id}', name: 'favorites_timesheets_remove', methods: ['GET'])]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
public function remove(Timesheet $timesheet, FavoriteRecordService $favoriteRecordService): Response
{
$favoriteRecordService->removeFavorite($timesheet);

View File

@@ -11,16 +11,16 @@ namespace App\Controller;
use App\Configuration\LocaleService;
use App\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Homepage controller is a redirect controller with user specific logic.
*/
#[Route(path: '/homepage')]
#[Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")]
#[IsGranted('IS_AUTHENTICATED_REMEMBERED')]
final class HomepageController extends AbstractController
{
public const DEFAULT_ROUTE = 'timesheet';

View File

@@ -37,7 +37,7 @@ use App\Repository\Query\InvoiceQuery;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -46,13 +46,15 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* Controller used to create invoices and manage invoice templates.
*/
#[Route(path: '/invoice')]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('view_invoice')")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('view_invoice')]
final class InvoiceController extends AbstractController
{
public function __construct(
@@ -64,7 +66,7 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/', name: 'invoice', methods: ['GET', 'POST'])]
#[Security("is_granted('create_invoice')")]
#[IsGranted('create_invoice')]
public function indexAction(Request $request, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$this->templateRepository->hasTemplate()) {
@@ -127,7 +129,8 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/preview/{customer}/{token}', name: 'invoice_preview', methods: ['GET'])]
#[Security("is_granted('access', customer) and is_granted('create_invoice')")]
#[IsGranted('create_invoice')]
#[IsGranted('access', 'customer')]
public function previewAction(Customer $customer, string $token, Request $request): Response
{
if (!$this->templateRepository->hasTemplate()) {
@@ -167,7 +170,8 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/save-invoice/{customer}/{token}', name: 'invoice_create', methods: ['GET'])]
#[Security("is_granted('access', customer) and is_granted('create_invoice')")]
#[IsGranted('create_invoice')]
#[IsGranted('access', 'customer')]
public function createInvoiceAction(Customer $customer, string $token, Request $request, CustomerRepository $customerRepository): Response
{
if (!$this->templateRepository->hasTemplate()) {
@@ -214,7 +218,8 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/change-status/{id}/{status}/{token}', name: 'admin_invoice_status', methods: ['GET', 'POST'])]
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('create_invoice')")]
#[IsGranted('create_invoice')]
#[IsGranted(new Expression("is_granted('access', subject.getCustomer())"), 'invoice')]
public function changeStatusAction(Invoice $invoice, string $status, string $token, Request $request, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.status', $token))) {
@@ -250,7 +255,8 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/edit/{id}', name: 'admin_invoice_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('create_invoice')")]
#[IsGranted('create_invoice')]
#[IsGranted(new Expression("is_granted('access', subject.getCustomer())"), 'invoice')]
public function editAction(Invoice $invoice, Request $request): Response
{
$form = $this->createInvoiceEditForm($invoice);
@@ -275,7 +281,8 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/delete/{id}/{token}', name: 'admin_invoice_delete', methods: ['GET'])]
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('delete_invoice')")]
#[IsGranted('delete_invoice')]
#[IsGranted(new Expression("is_granted('access', subject.getCustomer())"), 'invoice')]
public function deleteInvoiceAction(Invoice $invoice, string $token, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.status', $token))) {
@@ -297,7 +304,8 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/download/{id}', name: 'admin_invoice_download', methods: ['GET'])]
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('view_invoice')")]
#[IsGranted('view_invoice')]
#[IsGranted(new Expression("is_granted('access', subject.getCustomer())"), 'invoice')]
public function downloadAction(Invoice $invoice): Response
{
$file = $this->service->getInvoiceFile($invoice);
@@ -312,7 +320,7 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/show/{page}', defaults: ['page' => 1], requirements: ['page' => '[1-9]\d*'], name: 'admin_invoice_list', methods: ['GET'])]
#[Security("is_granted('view_invoice')")]
#[IsGranted('view_invoice')]
public function showInvoicesAction(Request $request, int $page): Response
{
$invoice = null;
@@ -371,7 +379,7 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/export', name: 'invoice_export', methods: ['GET'])]
#[Security("is_granted('view_invoice')")]
#[IsGranted('view_invoice')]
public function exportAction(Request $request, EntityWithMetaFieldsExporter $exporter)
{
$query = new InvoiceArchiveQuery();
@@ -394,7 +402,7 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/template/{page}', requirements: ['page' => '[1-9]\d*'], defaults: ['page' => 1], name: 'admin_invoice_template', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_invoice_template')")]
#[IsGranted('manage_invoice_template')]
public function listTemplateAction(int $page): Response
{
$query = new BaseQuery();
@@ -431,14 +439,14 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/template/{id}/edit', name: 'admin_invoice_template_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_invoice_template')")]
#[IsGranted('manage_invoice_template')]
public function editTemplateAction(InvoiceTemplate $template, Request $request): Response
{
return $this->renderTemplateForm($template, $request);
}
#[Route(path: '/document_upload', name: 'admin_invoice_document_upload', methods: ['GET', 'POST'])]
#[Security("is_granted('upload_invoice_template')")]
#[IsGranted('upload_invoice_template')]
public function uploadDocumentAction(Request $request, string $projectDirectory, InvoiceDocumentRepository $documentRepository)
{
$dir = $documentRepository->getUploadDirectory();
@@ -538,7 +546,7 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/document/{id}/delete/{token}', name: 'invoice_document_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_invoice_template')")]
#[IsGranted('manage_invoice_template')]
public function deleteDocument(string $id, string $token, CsrfTokenManagerInterface $csrfTokenManager, InvoiceDocumentRepository $documentRepository): Response
{
$document = $documentRepository->findByName($id);
@@ -581,14 +589,14 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/template/create/{id}', name: 'admin_invoice_template_copy', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_invoice_template')")]
#[IsGranted('manage_invoice_template')]
public function copyTemplateAction(Request $request, InvoiceTemplate $copyFrom): Response
{
return $this->createTemplate($request, $copyFrom);
}
#[Route(path: '/template/create', name: 'admin_invoice_template_create', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_invoice_template')")]
#[IsGranted('manage_invoice_template')]
public function createTemplateAction(Request $request): Response
{
return $this->createTemplate($request, null);
@@ -608,7 +616,7 @@ final class InvoiceController extends AbstractController
}
#[Route(path: '/template/{id}/delete/{csrfToken}', name: 'admin_invoice_template_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_invoice_template')")]
#[IsGranted('manage_invoice_template')]
public function deleteTemplate(InvoiceTemplate $template, string $csrfToken, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.delete_template', $csrfToken))) {

View File

@@ -22,19 +22,20 @@ use App\Repository\UserRepository;
use App\Security\RolePermissionManager;
use App\Security\RoleService;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to manage user roles and role permissions.
*/
#[Route(path: '/admin/permissions')]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('role_permissions')")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('role_permissions')]
final class PermissionController extends AbstractController
{
public const TOKEN_NAME = 'user_role_permissions';
@@ -44,7 +45,7 @@ final class PermissionController extends AbstractController
}
#[Route(path: '', name: 'admin_user_permissions', methods: ['GET', 'POST'])]
#[Security("is_granted('role_permissions')")]
#[IsGranted('role_permissions')]
public function permissions(EventDispatcherInterface $dispatcher, CsrfTokenManagerInterface $csrfTokenManager, RoleService $roleService)
{
$all = $this->roleRepository->findAll();
@@ -161,7 +162,7 @@ final class PermissionController extends AbstractController
}
#[Route(path: '/roles/create', name: 'admin_user_roles', methods: ['GET', 'POST'])]
#[Security("is_granted('role_permissions')")]
#[IsGranted('role_permissions')]
public function createRole(Request $request): Response
{
$role = new Role();
@@ -195,7 +196,7 @@ final class PermissionController extends AbstractController
}
#[Route(path: '/roles/{id}/delete/{csrfToken}', name: 'admin_user_role_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('role_permissions')")]
#[IsGranted('role_permissions')]
public function deleteRole(Role $role, string $csrfToken, UserRepository $userRepository, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$this->isCsrfTokenValid(self::TOKEN_NAME, $csrfToken)) {
@@ -225,7 +226,7 @@ final class PermissionController extends AbstractController
}
#[Route(path: '/roles/{id}/{name}/{value}/{csrfToken}', name: 'admin_user_permission_save', methods: ['POST'])]
#[Security("is_granted('role_permissions')")]
#[IsGranted('role_permissions')]
public function savePermission(Role $role, string $name, bool $value, string $csrfToken, RolePermissionRepository $rolePermissionRepository, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$this->isCsrfTokenValid(self::TOKEN_NAME, $csrfToken)) {

View File

@@ -11,15 +11,15 @@ namespace App\Controller;
use App\Plugin\PluginManager;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
#[Route(path: '/admin/plugins')]
#[Security("is_granted('plugins')")]
#[IsGranted('plugins')]
final class PluginController extends AbstractController
{
#[Route(path: '/', name: 'plugins', methods: ['GET'])]

View File

@@ -32,18 +32,19 @@ use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
use Endroid\QrCode\Writer\PngWriter;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Totp\TotpAuthenticatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* User profile controller
*/
#[Route(path: '/profile')]
#[Security("(is_granted('view_own_profile') or is_granted('view_other_profile'))")]
#[IsGranted(new Expression("is_granted('view_own_profile') or is_granted('view_other_profile')"))]
final class ProfileController extends AbstractController
{
#[Route(path: '/', name: 'my_profile', methods: ['GET'])]
@@ -53,7 +54,7 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}', name: 'user_profile', methods: ['GET'])]
#[Security("is_granted('view', profile)")]
#[IsGranted('view', 'profile')]
public function indexAction(User $profile, TimesheetRepository $repository, TimesheetStatisticService $statisticService): Response
{
$dateFactory = $this->getDateTimeFactory();
@@ -79,7 +80,7 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/edit', name: 'user_profile_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', profile)")]
#[IsGranted('edit', 'profile')]
public function editAction(User $profile, Request $request, UserRepository $userRepository): Response
{
$form = $this->createEditForm($profile);
@@ -101,7 +102,8 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/password', name: 'user_profile_password', methods: ['GET', 'POST'])]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('password', profile)")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('password', 'profile')]
public function passwordAction(User $profile, Request $request, UserService $userService): Response
{
$form = $this->createPasswordForm($profile);
@@ -123,7 +125,8 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/api-token', name: 'user_profile_api_token', methods: ['GET', 'POST'])]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('api-token', profile)")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('api-token', 'profile')]
public function apiTokenAction(User $profile, Request $request, UserService $userService): Response
{
$form = $this->createApiTokenForm($profile);
@@ -145,7 +148,8 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/roles', name: 'user_profile_roles', methods: ['GET', 'POST'])]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('roles', profile)")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('roles', 'profile')]
public function rolesAction(User $profile, Request $request, UserRepository $userRepository): Response
{
$isSuperAdmin = $profile->isSuperAdmin();
@@ -175,7 +179,7 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/teams', name: 'user_profile_teams', methods: ['GET', 'POST'])]
#[Security("is_granted('teams', profile)")]
#[IsGranted('teams', 'profile')]
public function teamsAction(User $profile, Request $request, UserRepository $userRepository, TeamRepository $teamRepository): Response
{
$originalMembers = new ArrayCollection();
@@ -210,7 +214,7 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/prefs', name: 'user_profile_preferences', methods: ['GET', 'POST'])]
#[Security("is_granted('preferences', profile)")]
#[IsGranted('preferences', 'profile')]
public function preferencesAction(User $profile, Request $request, EventDispatcherInterface $dispatcher, UserRepository $userRepository): Response
{
// we need to prepare the user preferences, which is done via an EventSubscriber
@@ -336,7 +340,8 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/2fa', name: 'user_profile_2fa', methods: ['GET', 'POST'])]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('2fa', profile)")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('2fa', 'profile')]
public function twoFactorAction(User $profile, Request $request, UserService $userService, TotpAuthenticatorInterface $totpAuthenticator): Response
{
if (!$profile->hasTotpSecret()) {
@@ -382,7 +387,8 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/2fa_deactivate', name: 'user_profile_2fa_deactivate', methods: ['POST'])]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('2fa', profile)")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('2fa', 'profile')]
public function deactivateTwoFactorAction(User $profile, Request $request, UserService $userService, TotpAuthenticatorInterface $totpAuthenticator): Response
{
if ($profile->hasTotpSecret()) {
@@ -401,7 +407,7 @@ final class ProfileController extends AbstractController
}
#[Route(path: '/{username}/totp.png', name: 'user_profile_2fa_image', methods: ['GET'])]
#[Security("is_granted('2fa', profile)")]
#[IsGranted('2fa', 'profile')]
public function displayTotpQrCode(User $profile, TotpAuthenticatorInterface $totpAuthenticator): Response
{
if (!$profile->hasTotpSecret()) {

View File

@@ -40,20 +40,21 @@ use App\Repository\TeamRepository;
use App\Utils\Context;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to manage projects.
*/
#[Route(path: '/admin/project')]
#[Security("is_granted('view_project') or is_granted('view_teamlead_project') or is_granted('view_team_project')")]
#[IsGranted(new Expression("is_granted('view_project') or is_granted('view_teamlead_project') or is_granted('view_team_project')"))]
final class ProjectController extends AbstractController
{
public function __construct(private ProjectRepository $repository, private SystemConfiguration $configuration, private EventDispatcherInterface $dispatcher, private ProjectService $projectService)
@@ -132,7 +133,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/permissions', name: 'admin_project_permissions', methods: ['GET', 'POST'])]
#[Security("is_granted('permissions', project)")]
#[IsGranted('permissions', 'project')]
public function teamPermissions(Project $project, Request $request)
{
$form = $this->createForm(ProjectTeamPermissionForm::class, $project, [
@@ -165,14 +166,14 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/create/{customer}', name: 'admin_project_create_with_customer', methods: ['GET', 'POST'])]
#[Security("is_granted('create_project')")]
#[IsGranted('create_project')]
public function createWithCustomerAction(Request $request, Customer $customer)
{
return $this->createProject($request, $customer);
}
#[Route(path: '/create', name: 'admin_project_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_project')")]
#[IsGranted('create_project')]
public function createAction(Request $request)
{
return $this->createProject($request, null);
@@ -204,7 +205,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/comment_delete/{token}', name: 'project_comment_delete', methods: ['GET'])]
#[Security("is_granted('edit', comment.getProject()) and is_granted('comments', comment.getProject())")]
#[IsGranted(new Expression("is_granted('edit', subject.getProject()) and is_granted('comments', subject.getProject())"), 'comment')]
public function deleteCommentAction(ProjectComment $comment, string $token, CsrfTokenManagerInterface $csrfTokenManager)
{
$projectId = $comment->getProject()->getId();
@@ -227,7 +228,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/comment_add', name: 'project_comment_add', methods: ['POST'])]
#[Security("is_granted('comments', project)")]
#[IsGranted('comments', 'project')]
public function addCommentAction(Project $project, Request $request)
{
$comment = new ProjectComment($project);
@@ -247,7 +248,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/comment_pin/{token}', name: 'project_comment_pin', methods: ['GET'])]
#[Security("is_granted('edit', comment.getProject()) and is_granted('comments', comment.getProject())")]
#[IsGranted(new Expression("is_granted('edit', subject.getProject()) and is_granted('comments', subject.getProject())"), 'comment')]
public function pinCommentAction(ProjectComment $comment, string $token, CsrfTokenManagerInterface $csrfTokenManager)
{
$projectId = $comment->getProject()->getId();
@@ -271,7 +272,8 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/create_team', name: 'project_team_create', methods: ['GET'])]
#[Security("is_granted('create_team') and is_granted('permissions', project)")]
#[IsGranted('create_team')]
#[IsGranted('permissions', 'project')]
public function createDefaultTeamAction(Project $project, TeamRepository $teamRepository)
{
$defaultTeam = $teamRepository->findOneBy(['name' => $project->getName()]);
@@ -295,7 +297,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/activities/{page}', defaults: ['page' => 1], name: 'project_activities', methods: ['GET', 'POST'])]
#[Security("is_granted('view', project)")]
#[IsGranted('view', 'project')]
public function activitiesAction(Project $project, int $page, ActivityRepository $activityRepository)
{
$query = new ActivityQuery();
@@ -319,7 +321,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/details', name: 'project_details', methods: ['GET', 'POST'])]
#[Security("is_granted('view', project)")]
#[IsGranted('view', 'project')]
public function detailsAction(Project $project, TeamRepository $teamRepository, ProjectRateRepository $rateRepository, ProjectStatisticService $statisticService, CsrfTokenManagerInterface $csrfTokenManager)
{
$event = new ProjectMetaDefinitionEvent($project);
@@ -380,14 +382,14 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/rate/{rate}', name: 'admin_project_rate_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', project)")]
#[IsGranted('edit', 'project')]
public function editRateAction(Project $project, ProjectRate $rate, Request $request, ProjectRateRepository $repository): Response
{
return $this->rateFormAction($project, $rate, $request, $repository, $this->generateUrl('admin_project_rate_edit', ['id' => $project->getId(), 'rate' => $rate->getId()]));
}
#[Route(path: '/{id}/rate', name: 'admin_project_rate_add', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', project)")]
#[IsGranted('edit', 'project')]
public function addRateAction(Project $project, Request $request, ProjectRateRepository $repository): Response
{
$rate = new ProjectRate();
@@ -424,7 +426,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/edit', name: 'admin_project_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', project)")]
#[IsGranted('edit', 'project')]
public function editAction(Project $project, Request $request)
{
$editForm = $this->createEditForm($project);
@@ -449,7 +451,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/duplicate/{token}', name: 'admin_project_duplicate', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', project)")]
#[IsGranted('edit', 'project')]
public function duplicateAction(Project $project, string $token, ProjectDuplicationService $projectDuplicationService, CsrfTokenManagerInterface $csrfTokenManager)
{
if (!$csrfTokenManager->isTokenValid(new CsrfToken('project.duplicate', $token))) {
@@ -468,7 +470,7 @@ final class ProjectController extends AbstractController
}
#[Route(path: '/{id}/delete', name: 'admin_project_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('delete', project)")]
#[IsGranted('delete', 'project')]
public function deleteAction(Project $project, Request $request, ProjectStatisticService $statisticService)
{
$stats = $statisticService->getProjectStatistics($project);

View File

@@ -17,15 +17,15 @@ use App\Repository\Query\TimesheetQuery;
use App\Repository\TimesheetRepository;
use App\Timesheet\TimesheetService;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to enter times in weekly form.
*/
#[Route(path: '/quick_entry')]
#[Security("is_granted('quick-entry')")]
#[IsGranted('quick-entry')]
final class QuickEntryController extends AbstractController
{
public function __construct(private SystemConfiguration $configuration, private TimesheetService $timesheetService, private TimesheetRepository $repository)

View File

@@ -18,13 +18,14 @@ use App\Reporting\CustomerMonthlyProjects\CustomerMonthlyProjectsRepository;
use App\Repository\Query\UserQuery;
use App\Repository\UserRepository;
use PhpOffice\PhpSpreadsheet\Reader\Html;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/customer/monthly_projects')]
#[Security("is_granted('report:customer') and is_granted('report:other')")]
#[IsGranted('report:customer')]
#[IsGranted('report:other')]
final class CustomerMonthlyProjectsController extends AbstractController
{
#[Route(path: '/view', name: 'report_customer_monthly_projects', methods: ['GET', 'POST'])]

View File

@@ -14,14 +14,16 @@ use App\Form\Model\DateRange;
use App\Project\ProjectStatisticService;
use App\Reporting\ProjectDateRange\ProjectDateRangeForm;
use App\Reporting\ProjectDateRange\ProjectDateRangeQuery;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
final class ProjectDateRangeController extends AbstractController
{
#[Route(path: '/reporting/project_daterange', name: 'report_project_daterange', methods: ['GET', 'POST'])]
#[Security("is_granted('report:project') and is_granted('budget_any', 'project')")]
#[IsGranted('report:project')]
#[IsGranted(new Expression("is_granted('budget_any', 'project')"))]
public function __invoke(Request $request, ProjectStatisticService $service)
{
$dateFactory = $this->getDateTimeFactory();

View File

@@ -14,14 +14,16 @@ use App\Project\ProjectStatisticService;
use App\Reporting\ProjectDetails\ProjectDetailsForm;
use App\Reporting\ProjectDetails\ProjectDetailsQuery;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
final class ProjectDetailsController extends AbstractController
{
#[Route(path: '/reporting/project_details', name: 'report_project_details', methods: ['GET'])]
#[Security("is_granted('report:project') and is_granted('details', 'project')")]
#[IsGranted('report:project')]
#[IsGranted(new Expression("is_granted('details', 'project')"))]
public function __invoke(Request $request, ProjectStatisticService $service)
{
$dateFactory = $this->getDateTimeFactory();

View File

@@ -13,14 +13,16 @@ use App\Controller\AbstractController;
use App\Project\ProjectStatisticService;
use App\Reporting\ProjectInactive\ProjectInactiveForm;
use App\Reporting\ProjectInactive\ProjectInactiveQuery;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
final class ProjectInactiveController extends AbstractController
{
#[Route(path: '/reporting/project_inactive', name: 'report_project_inactive', methods: ['GET', 'POST'])]
#[Security("is_granted('report:project') and is_granted('budget_any', 'project')")]
#[IsGranted('report:project')]
#[IsGranted(new Expression("is_granted('budget_any', 'project')"))]
public function __invoke(Request $request, ProjectStatisticService $service)
{
$dateFactory = $this->getDateTimeFactory();

View File

@@ -13,14 +13,16 @@ use App\Controller\AbstractController;
use App\Project\ProjectStatisticService;
use App\Reporting\ProjectView\ProjectViewForm;
use App\Reporting\ProjectView\ProjectViewQuery;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
final class ProjectViewController extends AbstractController
{
#[Route(path: '/reporting/project_view', name: 'report_project_view', methods: ['GET', 'POST'])]
#[Security("is_granted('report:project') and is_granted('budget_any', 'project')")]
#[IsGranted('report:project')]
#[IsGranted(new Expression("is_granted('budget_any', 'project')"))]
public function __invoke(Request $request, ProjectStatisticService $service)
{
$dateFactory = $this->getDateTimeFactory();

View File

@@ -19,13 +19,13 @@ use App\Repository\Query\UserQuery;
use App\Repository\UserRepository;
use App\Timesheet\TimesheetStatisticService;
use PhpOffice\PhpSpreadsheet\Reader\Html;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/users')]
#[Security("is_granted('report:other')")]
#[IsGranted('report:other')]
final class ReportUsersMonthController extends AbstractController
{
#[Route(path: '/month', name: 'report_monthly_users', methods: ['GET', 'POST'])]

View File

@@ -19,13 +19,13 @@ use App\Repository\Query\UserQuery;
use App\Repository\UserRepository;
use App\Timesheet\TimesheetStatisticService;
use PhpOffice\PhpSpreadsheet\Reader\Html;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/users')]
#[Security("is_granted('report:other')")]
#[IsGranted('report:other')]
final class ReportUsersWeekController extends AbstractController
{
#[Route(path: '/week', name: 'report_weekly_users', methods: ['GET', 'POST'])]

View File

@@ -21,13 +21,13 @@ use App\Repository\UserRepository;
use App\Timesheet\TimesheetStatisticService;
use Exception;
use PhpOffice\PhpSpreadsheet\Reader\Html;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/users')]
#[Security("is_granted('report:other')")]
#[IsGranted('report:other')]
final class ReportUsersYearController extends AbstractController
{
/**

View File

@@ -13,14 +13,14 @@ use App\Model\DailyStatistic;
use App\Reporting\MonthByUser\MonthByUser;
use App\Reporting\MonthByUser\MonthByUserForm;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/user')]
#[Security("is_granted('report:user')")]
#[IsGranted('report:user')]
final class UserMonthController extends AbstractUserReportController
{
/**

View File

@@ -13,14 +13,14 @@ use App\Model\DailyStatistic;
use App\Reporting\WeekByUser\WeekByUser;
use App\Reporting\WeekByUser\WeekByUserForm;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/user')]
#[Security("is_granted('report:user')")]
#[IsGranted('report:user')]
final class UserWeekController extends AbstractUserReportController
{
/**

View File

@@ -17,14 +17,14 @@ use App\Reporting\YearByUser\YearByUser;
use App\Reporting\YearByUser\YearByUserForm;
use DateTime;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/reporting/user')]
#[Security("is_granted('report:user')")]
#[IsGranted('report:user')]
final class UserYearController extends AbstractUserReportController
{
/**

View File

@@ -11,15 +11,15 @@ namespace App\Controller;
use App\Reporting\ReportingService;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to render reports.
*/
#[Route(path: '/reporting')]
#[Security("is_granted('view_reporting')")]
#[IsGranted('view_reporting')]
final class ReportingController extends AbstractController
{
#[Route(path: '/', name: 'reporting', methods: ['GET'])]

View File

@@ -36,7 +36,6 @@ use App\Utils\PageSetup;
use App\Validator\Constraints\ColorChoices;
use App\Validator\Constraints\DateTimeFormat;
use App\Validator\Constraints\TimeFormat;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
@@ -47,6 +46,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
@@ -57,7 +57,8 @@ use Symfony\Component\Validator\Constraints\Regex;
* Controller used for executing system relevant tasks.
*/
#[Route(path: '/admin/system-config')]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('system_configuration')")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('system_configuration')]
final class SystemConfigurationController extends AbstractController
{
public function __construct(private EventDispatcherInterface $eventDispatcher, private ConfigurationRepository $repository, private SystemConfiguration $systemConfiguration, private LockdownService $lockdownService)

View File

@@ -18,14 +18,14 @@ use App\Repository\Query\TagQuery;
use App\Repository\TagRepository;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/admin/tags')]
#[Security("is_granted('view_tag')")]
#[IsGranted('view_tag')]
final class TagController extends AbstractController
{
/**
@@ -76,7 +76,7 @@ final class TagController extends AbstractController
}
#[Route(path: '/{id}/edit', name: 'tags_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_tag')")]
#[IsGranted('manage_tag')]
public function editAction(Tag $tag, TagRepository $repository, Request $request)
{
$editForm = $this->createForm(TagEditForm::class, $tag, [
@@ -108,7 +108,7 @@ final class TagController extends AbstractController
}
#[Route(path: '/create', name: 'tags_create', methods: ['GET', 'POST'])]
#[Security("is_granted('manage_tag')")]
#[IsGranted('manage_tag')]
public function createAction(TagRepository $repository, Request $request)
{
$tag = new Tag();
@@ -142,7 +142,7 @@ final class TagController extends AbstractController
}
#[Route(path: '/multi-delete', name: 'tags_multi_delete', methods: ['POST'])]
#[Security("is_granted('delete_tag')")]
#[IsGranted('delete_tag')]
public function multiDelete(TagRepository $repository, Request $request)
{
$form = $this->getMultiUpdateForm($repository);

View File

@@ -18,14 +18,14 @@ use App\Repository\Query\TeamQuery;
use App\Repository\TeamRepository;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/admin/teams')]
#[Security("is_granted('view_team')")]
#[IsGranted('view_team')]
final class TeamController extends AbstractController
{
public function __construct(private TeamRepository $repository)
@@ -81,14 +81,15 @@ final class TeamController extends AbstractController
* @return Response
*/
#[Route(path: '/create', name: 'admin_team_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_team')")]
#[IsGranted('create_team')]
public function createTeam(Request $request): Response
{
return $this->renderEditScreen(new Team(''), $request, true);
}
#[Route(path: '/{id}/duplicate', name: 'team_duplicate', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', team) and is_granted('create_team')")]
#[IsGranted('create_team')]
#[IsGranted('edit', 'team')]
public function duplicateTeam(Team $team, Request $request)
{
$newTeam = clone $team;
@@ -103,14 +104,14 @@ final class TeamController extends AbstractController
}
#[Route(path: '/{id}/edit', name: 'admin_team_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', team)")]
#[IsGranted('edit', 'team')]
public function editAction(Team $team, Request $request)
{
return $this->renderEditScreen($team, $request);
}
#[Route(path: '/{id}/edit_member', name: 'admin_team_member', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', team)")]
#[IsGranted('edit', 'team')]
public function editMemberAction(Team $team, Request $request)
{
$editForm = $this->createForm(TeamEditForm::class, $team, [

View File

@@ -167,14 +167,13 @@ abstract class TimesheetAbstractController extends AbstractController
protected function create(Request $request): Response
{
$entry = $this->service->createNewTimesheet($this->getUser());
$entry = $this->service->createNewTimesheet($this->getUser(), $request);
$preForm = $this->createFormForGetRequest(TimesheetPreCreateForm::class, $entry, [
'include_user' => $this->includeUserInForms('create'),
]);
$preForm->submit($request->query->all(), false);
$this->service->prepareNewTimesheet($entry, $request);
$createForm = $this->getCreateForm($entry);
$createForm->handleRequest($request);

View File

@@ -13,19 +13,19 @@ use App\Entity\Timesheet;
use App\Event\TimesheetMetaDisplayEvent;
use App\Export\ServiceExport;
use App\Form\TimesheetEditForm;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/timesheet')]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
final class TimesheetController extends TimesheetAbstractController
{
#[Route(path: '/', defaults: ['page' => 1], name: 'timesheet', methods: ['GET'])]
#[Route(path: '/page/{page}', requirements: ['page' => '[1-9]\d*'], name: 'timesheet_paginated', methods: ['GET'])]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
public function indexAction(int $page, Request $request): Response
{
$query = $this->createDefaultQuery();
@@ -35,42 +35,42 @@ final class TimesheetController extends TimesheetAbstractController
}
#[Route(path: '/export/', name: 'timesheet_export', methods: ['GET', 'POST'])]
#[Security("is_granted('export_own_timesheet')")]
#[IsGranted('export_own_timesheet')]
public function exportAction(Request $request, ServiceExport $serviceExport): Response
{
return $this->export($request, $serviceExport);
}
#[Route(path: '/{id}/edit', name: 'timesheet_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', entry)")]
#[IsGranted('edit', 'entry')]
public function editAction(Timesheet $entry, Request $request): Response
{
return $this->edit($entry, $request);
}
#[Route(path: '/{id}/duplicate', name: 'timesheet_duplicate', methods: ['GET', 'POST'])]
#[Security("is_granted('duplicate', entry)")]
#[IsGranted('duplicate', 'entry')]
public function duplicateAction(Timesheet $entry, Request $request): Response
{
return $this->duplicate($entry, $request);
}
#[Route(path: '/multi-update', name: 'timesheet_multi_update', methods: ['POST'])]
#[Security("is_granted('edit_own_timesheet')")]
#[IsGranted('edit_own_timesheet')]
public function multiUpdateAction(Request $request): Response
{
return $this->multiUpdate($request);
}
#[Route(path: '/multi-delete', name: 'timesheet_multi_delete', methods: ['POST'])]
#[Security("is_granted('delete_own_timesheet')")]
#[IsGranted('delete_own_timesheet')]
public function multiDeleteAction(Request $request): Response
{
return $this->multiDelete($request);
}
#[Route(path: '/create', name: 'timesheet_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_own_timesheet')")]
#[IsGranted('create_own_timesheet')]
public function createAction(Request $request): Response
{
return $this->create($request);

View File

@@ -21,19 +21,19 @@ use App\Form\TimesheetMultiUserEditForm;
use App\Repository\Query\TimesheetQuery;
use App\Utils\PageSetup;
use Doctrine\Common\Collections\ArrayCollection;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/team/timesheet')]
#[Security("is_granted('view_other_timesheet')")]
#[IsGranted('view_other_timesheet')]
final class TimesheetTeamController extends TimesheetAbstractController
{
#[Route(path: '/', defaults: ['page' => 1], name: 'admin_timesheet', methods: ['GET'])]
#[Route(path: '/page/{page}', requirements: ['page' => '[1-9]\d*'], name: 'admin_timesheet_paginated', methods: ['GET'])]
#[Security("is_granted('view_other_timesheet')")]
#[IsGranted('view_other_timesheet')]
public function indexAction(int $page, Request $request): Response
{
$query = $this->createDefaultQuery();
@@ -43,35 +43,35 @@ final class TimesheetTeamController extends TimesheetAbstractController
}
#[Route(path: '/export/', name: 'admin_timesheet_export', methods: ['GET', 'POST'])]
#[Security("is_granted('export_other_timesheet')")]
#[IsGranted('export_other_timesheet')]
public function exportAction(Request $request, ServiceExport $serviceExport): Response
{
return $this->export($request, $serviceExport);
}
#[Route(path: '/{id}/edit', name: 'admin_timesheet_edit', methods: ['GET', 'POST'])]
#[Security("is_granted('edit', entry)")]
#[IsGranted('edit', 'entry')]
public function editAction(Timesheet $entry, Request $request): Response
{
return $this->edit($entry, $request);
}
#[Route(path: '/{id}/duplicate', name: 'admin_timesheet_duplicate', methods: ['GET', 'POST'])]
#[Security("is_granted('duplicate', entry)")]
#[IsGranted('duplicate', 'entry')]
public function duplicateAction(Timesheet $entry, Request $request): Response
{
return $this->duplicate($entry, $request);
}
#[Route(path: '/create', name: 'admin_timesheet_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_other_timesheet')")]
#[IsGranted('create_other_timesheet')]
public function createAction(Request $request): Response
{
return $this->create($request);
}
#[Route(path: '/create_mu', name: 'admin_timesheet_create_multiuser', methods: ['GET', 'POST'])]
#[Security("is_granted('create_other_timesheet')")]
#[IsGranted('create_other_timesheet')]
public function createForMultiUserAction(Request $request): Response
{
$entry = new MultiUserTimesheet();
@@ -150,14 +150,14 @@ final class TimesheetTeamController extends TimesheetAbstractController
}
#[Route(path: '/multi-update', name: 'admin_timesheet_multi_update', methods: ['POST'])]
#[Security("is_granted('edit_other_timesheet')")]
#[IsGranted('edit_other_timesheet')]
public function multiUpdateAction(Request $request): Response
{
return $this->multiUpdate($request);
}
#[Route(path: '/multi-delete', name: 'admin_timesheet_multi_delete', methods: ['POST'])]
#[Security("is_granted('delete_other_timesheet')")]
#[IsGranted('delete_other_timesheet')]
public function multiDeleteAction(Request $request): Response
{
return $this->multiDelete($request);

View File

@@ -25,19 +25,20 @@ use App\Repository\UserRepository;
use App\User\UserService;
use App\Utils\DataTable;
use App\Utils\PageSetup;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller used to manage users in the admin part of the site.
*/
#[Route(path: '/admin/user')]
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('view_user')")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
#[IsGranted('view_user')]
final class UserController extends AbstractController
{
public function __construct(private UserPasswordHasherInterface $passwordHasher, private UserRepository $repository, private EventDispatcherInterface $dispatcher)
@@ -110,7 +111,7 @@ final class UserController extends AbstractController
}
#[Route(path: '/create', name: 'admin_user_create', methods: ['GET', 'POST'])]
#[Security("is_granted('create_user')")]
#[IsGranted('create_user')]
public function createAction(Request $request, SystemConfiguration $config, UserRepository $userRepository): Response
{
$user = $this->createNewDefaultUser($config);
@@ -139,7 +140,7 @@ final class UserController extends AbstractController
}
#[Route(path: '/{id}/delete', name: 'admin_user_delete', methods: ['GET', 'POST'])]
#[Security("is_granted('delete', userToDelete)")]
#[IsGranted('delete', 'userToDelete')]
public function deleteAction(User $userToDelete, Request $request, TimesheetRepository $repository, UserService $userService): Response
{
// $userToDelete MUST not be called $user, as $user is always the current user!
@@ -191,7 +192,7 @@ final class UserController extends AbstractController
}
#[Route(path: '/export', name: 'user_export', methods: ['GET'])]
#[Security("is_granted('view_user')")]
#[IsGranted('view_user')]
public function exportAction(Request $request, UserExporter $exporter)
{
$query = new UserQuery();

View File

@@ -9,16 +9,16 @@
namespace App\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/widgets')]
#[Security("is_granted('IS_AUTHENTICATED_REMEMBERED')")]
#[IsGranted('IS_AUTHENTICATED_REMEMBERED')]
final class WidgetController extends AbstractController
{
#[Route(path: '/working-time/{year}/{week}', requirements: ['year' => '[1-9]\d*', 'week' => '[0-9]\d*'], name: 'widgets_working_time_chart', methods: ['GET'])]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
public function workingtimechartAction($year, $week): Response
{
return $this->render('widget/paginatedworkingtimechart.html.twig', [

View File

@@ -15,18 +15,18 @@ use App\Form\Type\LanguageType;
use App\Form\Type\SkinType;
use App\Form\Type\TimezoneType;
use App\User\UserService;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route(path: '/wizard')]
#[Security("is_granted('IS_AUTHENTICATED_FULLY')")]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
final class WizardController extends AbstractController
{
#[Route(path: '/{wizard}', name: 'wizard', methods: ['GET', 'POST'])]
#[Security("is_granted('view_own_timesheet')")]
#[IsGranted('view_own_timesheet')]
public function wizard(Request $request, UserService $userService, string $wizard): Response
{
$user = $this->getUser();