p96 follow up

This commit is contained in:
team 1
2026-05-11 15:39:00 +02:00
parent 3e678c659c
commit f098a1a244
25 changed files with 790 additions and 131 deletions

View File

@@ -336,6 +336,26 @@ final readonly class AgentRunner
$optimizedShopQuery = '';
}
$weakReferentialHistoryModelShopSearchQuery = $this->guardWeakReferentialShopQueryWithHistoryModelAnchor(
prompt: $originalPrompt,
shopSearchQuery: $shopSearchQuery,
commerceHistoryContext: $commerceHistoryContext
);
if ($weakReferentialHistoryModelShopSearchQuery !== $shopSearchQuery) {
$this->agentLogger->info('Replaced weak referential shop query with history model anchor', [
'userId' => $userId,
'prompt' => $prompt,
'routingPrompt' => $routingPrompt,
'optimizedShopQuery' => $optimizedShopQuery,
'shopSearchQuery' => $shopSearchQuery,
'weakReferentialHistoryModelShopSearchQuery' => $weakReferentialHistoryModelShopSearchQuery,
]);
$shopSearchQuery = $weakReferentialHistoryModelShopSearchQuery;
$optimizedShopQuery = '';
}
$productListAnchoredShopSearchQuery = $this->guardReferentialProductListShopQueryWithHistoryAnchors(
prompt: $originalPrompt,
shopSearchQuery: $shopSearchQuery,
@@ -4148,6 +4168,84 @@ final readonly class AgentRunner
: $modelAnchor;
}
private function guardWeakReferentialShopQueryWithHistoryModelAnchor(
string $prompt,
string $shopSearchQuery,
string $commerceHistoryContext
): string {
$shopSearchQuery = trim($shopSearchQuery);
if (
$shopSearchQuery === ''
|| trim($commerceHistoryContext) === ''
|| $this->referenceAnchorExtractor->extractFirstProductModelAnchor($prompt) !== ''
|| $this->referenceAnchorExtractor->extractFirstProductModelAnchor($shopSearchQuery) !== ''
|| $this->isReferentialProductListShopFollowUpPrompt($prompt)
) {
return $shopSearchQuery;
}
if (!$this->shouldUseCommerceHistoryForShopQuery($prompt)) {
return $shopSearchQuery;
}
// Accessory and consumable follow-ups have their own detail-anchor guard.
if ($this->containsConfiguredShopQueryAnchorTrigger(trim($prompt . ' ' . $shopSearchQuery))) {
return $shopSearchQuery;
}
if (!$this->isWeakReferentialHistoryModelShopQuery($shopSearchQuery)) {
return $shopSearchQuery;
}
$modelAnchor = $this->normalizeShopQueryAnchor(
$this->extractLatestHistoryProductModelAnchor($commerceHistoryContext)
);
if ($modelAnchor === '') {
return $shopSearchQuery;
}
return $this->queryAlreadyContainsAllAnchorTokens($shopSearchQuery, $modelAnchor)
? $shopSearchQuery
: $modelAnchor;
}
private function isWeakReferentialHistoryModelShopQuery(string $shopSearchQuery): bool
{
if ($this->referenceAnchorExtractor->extractFirstProductModelAnchor($shopSearchQuery) !== '') {
return false;
}
$tokens = $this->tokenizeShopQueryCandidate($shopSearchQuery);
if ($tokens === []) {
return true;
}
$weakTokens = $this->buildShopQueryTokenSet($this->mergeUniqueStrings(
$this->mergeUniqueStrings(
$this->getShopQueryMetaGuardTerms(),
$this->getShopQueryContextFallbackFilterTerms()
),
$this->mergeUniqueStrings(
$this->agentRunnerConfig->getShopQueryProductListFollowUpNoiseTerms(),
$this->agentRunnerConfig->getShopQueryStopwordCleanupTerms()
)
));
if ($weakTokens === []) {
return false;
}
foreach ($tokens as $token) {
if (!isset($weakTokens[$token])) {
return false;
}
}
return true;
}
private function isMainDeviceReferentialShopQueryPrompt(string $prompt): bool
{
$tokens = $this->tokenizeShopQueryCandidate($prompt);
@@ -6864,15 +6962,27 @@ final readonly class AgentRunner
return $this->buildFollowUpActionProductQuery($numberedProducts[0]);
}
if (count($numberedProducts) > 1) {
return $this->buildFollowUpActionProductListQuery($numberedProducts);
}
$focusedProducts = $this->filterFollowUpActionPrimaryDisplayedProducts($displayedProducts);
if (count($focusedProducts) === 1) {
return $this->buildFollowUpActionProductQuery($focusedProducts[0]);
}
if (count($focusedProducts) > 1) {
return $this->buildFollowUpActionProductListQuery($focusedProducts);
}
if (count($displayedProducts) === 1) {
return $this->buildFollowUpActionProductQuery($displayedProducts[0]);
}
if (count($displayedProducts) > 1) {
return $this->buildFollowUpActionProductListQuery($displayedProducts);
}
$answerAnchor = $this->buildFollowUpActionAnswerAnchor($answerText);
if ($answerAnchor !== '') {
return $answerAnchor;
@@ -6941,6 +7051,37 @@ final readonly class AgentRunner
return $this->normalizeOneLine(implode(' ', array_filter($parts, static fn(string $part): bool => trim($part) !== '')));
}
/**
* @param ShopProductResult[] $products
*/
private function buildFollowUpActionProductListQuery(array $products): string
{
$queries = [];
$seen = [];
$maxProducts = max(1, $this->agentRunnerConfig->getShopQueryProductListFollowUpMaxAnchors());
foreach ($products as $product) {
if (!$product instanceof ShopProductResult) {
continue;
}
$query = $this->buildFollowUpActionProductQuery($product);
$key = mb_strtolower($query, 'UTF-8');
if ($query === '' || isset($seen[$key])) {
continue;
}
$seen[$key] = true;
$queries[] = $query;
if (count($queries) >= $maxProducts) {
break;
}
}
return $this->normalizeOneLine(implode('; ', $queries));
}
private function buildFollowUpActionAnswerAnchor(string $answerText): string
{
$anchors = [];

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -21,7 +22,7 @@ final class AdminSystemLogController extends AbstractController
#[Route('', name: 'admin_system_logs_index')]
public function index(): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
$files = [];
@@ -47,7 +48,7 @@ final class AdminSystemLogController extends AbstractController
)]
public function view(string $date): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if ($date === 'current') {
$safeFilename = 'system.log';

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -14,7 +15,7 @@ final class AdminVectorLogController extends AbstractController
#[Route('/log', name: 'admin_vector_log')]
public function view(): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
// ⚠️ Pfad ggf. anpassen falls bei dir anders konfiguriert
$logFile = \dirname(__DIR__, 3) . '/var/log/vector_service.log';

View File

@@ -8,6 +8,7 @@ use App\Entity\Document;
use App\Entity\DocumentVersion;
use App\Entity\IngestJob;
use App\Entity\User;
use App\Security\ApplicationRoles;
use App\Service\DocumentService;
use App\Service\FormatText;
use App\Service\IngestJobService;
@@ -33,6 +34,8 @@ final class DocumentController extends AbstractController
#[Route('', name: 'admin_documents', methods: ['GET'])]
public function index(EntityManagerInterface $em): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
$documents = $em->getRepository(Document::class)
->createQueryBuilder('d')
->leftJoin('d.versions', 'v')
@@ -56,6 +59,8 @@ final class DocumentController extends AbstractController
)]
public function show(string $id, EntityManagerInterface $em): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
return $this->render('admin/document/show.html.twig', [
'document' => $this->findDocument($id, $em),
]);
@@ -70,6 +75,8 @@ final class DocumentController extends AbstractController
ParameterBagInterface $params,
EntityManagerInterface $em,
): Response {
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
if (!$request->isMethod('POST')) {
return $this->render('admin/document/new.html.twig');
}
@@ -150,6 +157,8 @@ final class DocumentController extends AbstractController
ParameterBagInterface $params,
FormatText $formatText,
): Response {
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
$document = $this->findDocument($id, $em);
if (!$request->isMethod('POST')) {
@@ -202,6 +211,8 @@ final class DocumentController extends AbstractController
DocumentService $documentService,
IngestJobService $jobService,
): RedirectResponse {
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
if (!$this->isCsrfTokenValid('activate_version_' . $versionId, (string) $request->request->get('_token'))) {
throw $this->createAccessDeniedException();
}
@@ -260,6 +271,8 @@ final class DocumentController extends AbstractController
EntityManagerInterface $em,
IngestJobService $jobService,
): RedirectResponse {
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
if (!$this->isCsrfTokenValid('ingest_version_' . $versionId, (string) $request->request->get('_token'))) {
throw $this->createAccessDeniedException();
}
@@ -329,7 +342,7 @@ final class DocumentController extends AbstractController
ParameterBagInterface $params,
Connection $connection,
): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid('system_reset', (string) $request->request->get('_token'))) {
$this->addFlash('danger', 'Ungültiges CSRF-Token.');
@@ -400,7 +413,7 @@ SQL;
IngestJobService $jobService,
LockService $lockService,
): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid('delete_document_' . $id, (string) $request->request->get('_token'))) {
throw $this->createAccessDeniedException();

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\TagRebuildJob;
use App\Security\ApplicationRoles;
use App\Service\Admin\DocumentTagAdminService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -19,6 +20,8 @@ final class DocumentTagController extends AbstractController
#[Route('/{id}/tags', name: 'admin_document_tags_edit', methods: ['GET'])]
public function edit(string $id, DocumentTagAdminService $svc): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
$id = trim($id);
try {
@@ -38,6 +41,8 @@ final class DocumentTagController extends AbstractController
#[Route('/{id}/tags/save', name: 'admin_document_tags_save', methods: ['POST'])]
public function save(string $id, Request $request, DocumentTagAdminService $svc): RedirectResponse
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
$id = trim($id);
if (!$this->isCsrfTokenValid('admin_document_tags_save_' . $id, (string) $request->request->get('_token'))) {

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use App\Entity\IngestJob;
use App\Service\IngestJobService;
use Doctrine\ORM\EntityManagerInterface;
@@ -50,7 +51,7 @@ final class IngestJobController extends AbstractController
)]
public function status(string $id, EntityManagerInterface $em): JsonResponse
{
$this->denyAccessUnlessGranted('ROLE_ADMIN_AREA');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_ADMIN_AREA);
$job = $this->findJob($id, $em);
@@ -71,7 +72,7 @@ final class IngestJobController extends AbstractController
IngestJobService $jobService,
EntityManagerInterface $em,
): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid('global_reindex', (string) $request->request->get('_token'))) {
$this->addFlash('danger', 'Ungültiges CSRF-Token.');

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\IngestProfile;
use App\Security\ApplicationRoles;
use App\Service\Admin\IngestProfileAdminService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
@@ -17,6 +18,8 @@ final class IngestProfileController extends AbstractController
#[Route('/', name: 'admin_ingest_profile_list')]
public function list(IngestProfileAdminService $svc): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_KNOWLEDGE_ADMIN);
$data = $svc->listData();
return $this->render('admin/ingest_profile/list.html.twig', $data);
@@ -25,6 +28,8 @@ final class IngestProfileController extends AbstractController
#[Route('/create', name: 'admin_ingest_profile_create', methods: ['GET', 'POST'])]
public function create(Request $request, IngestProfileAdminService $svc): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if ($request->isMethod('POST')) {
try {
$svc->create([
@@ -49,8 +54,15 @@ final class IngestProfileController extends AbstractController
#[Route('/activate/{id}', name: 'admin_ingest_profile_activate', methods: ['POST'])]
public function activate(
IngestProfile $profile,
IngestProfileAdminService $svc
IngestProfileAdminService $svc,
Request $request
): Response {
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid('activate_ingest_profile_' . $profile->getId(), (string) $request->request->get('_token'))) {
throw $this->createAccessDeniedException();
}
try {
$svc->activate($profile);
$this->addFlash('success', 'Ingest-Profil wurde aktiviert.');
@@ -61,9 +73,15 @@ final class IngestProfileController extends AbstractController
return $this->redirectToRoute('admin_ingest_profile_list');
}
#[Route('/remove/{id}', name: 'admin_ingest_profile_remove', methods: ['POST', 'GET'])]
public function remove(string $id, IngestProfileAdminService $svc): Response
#[Route('/remove/{id}', name: 'admin_ingest_profile_remove', methods: ['POST'])]
public function remove(string $id, IngestProfileAdminService $svc, Request $request): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid('delete_ingest_profile_' . $id, (string) $request->request->get('_token'))) {
throw $this->createAccessDeniedException();
}
try {
$svc->remove($id);
$this->addFlash('success', 'Ingest-Profil wurde entfernt.');

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use App\Entity\ModelGenerationConfig;
use App\Service\Admin\ModelGenerationConfigAdminService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -17,7 +18,7 @@ final class ModelGenerationConfigController extends AbstractController
#[Route('/', name: 'admin_model_config_list')]
public function list(ModelGenerationConfigAdminService $svc): Response
{
$this->denyAccessUnlessGranted('ROLE_KNOWLEDGE_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_KNOWLEDGE_ADMIN);
return $this->render('admin/model_config/list.html.twig', [
'configs' => $svc->list(),
@@ -29,7 +30,7 @@ final class ModelGenerationConfigController extends AbstractController
Request $request,
ModelGenerationConfigAdminService $svc
): Response {
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if ($request->isMethod('POST')) {
try {
@@ -50,7 +51,7 @@ final class ModelGenerationConfigController extends AbstractController
ModelGenerationConfig $config,
ModelGenerationConfigAdminService $svc
): Response {
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
$svc->activate($config);
@@ -63,7 +64,7 @@ final class ModelGenerationConfigController extends AbstractController
Request $request,
ModelGenerationConfigAdminService $svc
): Response {
$this->denyAccessUnlessGranted('ROLE_KNOWLEDGE_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_KNOWLEDGE_ADMIN);
$prompt = '';
$results = [];
@@ -86,7 +87,7 @@ final class ModelGenerationConfigController extends AbstractController
Request $request,
ModelGenerationConfigAdminService $svc
): Response {
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid(
'delete_model_config_'.$config->getId(),

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use App\Service\Admin\IndexNdjsonInspector;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
@@ -12,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_SUPER_ADMIN')]
#[IsGranted(ApplicationRoles::ROLE_SUPER_ADMIN)]
final class SystemAgentController extends AbstractController
{
#[Route('/admin/system/agent', name: 'admin_system_agent', methods: ['GET'])]

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use App\Entity\SystemPrompt;
use App\Service\Admin\SystemPromptAdminService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -12,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_SUPER_ADMIN')]
#[IsGranted(ApplicationRoles::ROLE_SUPER_ADMIN)]
final class SystemPromptController extends AbstractController
{
#[Route('/admin/system/prompt', name: 'admin_system_prompt', methods: ['GET', 'POST'])]

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\TagRebuildJob;
use App\Security\ApplicationRoles;
use App\Service\Admin\TagAdminService;
use App\Tag\TagTypes;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -19,6 +20,8 @@ final class TagController extends AbstractController
#[Route('', name: 'admin_tags_index', methods: ['GET'])]
public function index(TagAdminService $svc): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
return $this->render('admin/tag/index.html.twig', [
...$svc->getIndexData(),
...$this->buildJobStatusViewData(),
@@ -28,6 +31,8 @@ final class TagController extends AbstractController
#[Route('/create', name: 'admin_tags_create', methods: ['POST'])]
public function create(Request $request, TagAdminService $svc): RedirectResponse
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
if (!$this->isCsrfTokenValid('admin_tag_create', (string) $request->request->get('_token'))) {
$this->addFlash('danger', 'Ungültiges CSRF-Token.');
@@ -53,6 +58,8 @@ final class TagController extends AbstractController
#[Route('/{id}/delete', name: 'admin_tags_delete', methods: ['POST'])]
public function delete(string $id, Request $request, TagAdminService $svc): RedirectResponse
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
if (!$this->isCsrfTokenValid('admin_tag_delete_' . $id, (string) $request->request->get('_token'))) {
$this->addFlash('danger', 'Ungültiges CSRF-Token.');
@@ -72,6 +79,8 @@ final class TagController extends AbstractController
#[Route('/{id}/assign', name: 'admin_tags_assign', methods: ['GET', 'POST'])]
public function assign(string $id, Request $request, TagAdminService $svc): Response
{
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_EDITOR);
$id = trim($id);
if ($request->isMethod('POST')) {

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Admin;
use App\Security\ApplicationRoles;
use App\Entity\User;
use App\Service\Admin\UserAdminService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -19,7 +20,7 @@ final class UserController extends AbstractController
#[Route('', name: 'admin_users_index', methods: ['GET'])]
public function index(Request $request, UserAdminService $users): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
$query = trim((string) $request->query->get('q', ''));
$status = (string) $request->query->get('status', 'all');
@@ -44,7 +45,7 @@ final class UserController extends AbstractController
#[Route('/create', name: 'admin_users_create', methods: ['GET', 'POST'])]
public function create(Request $request, UserAdminService $users): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$request->isMethod('POST')) {
return $this->render('admin/user/create.html.twig', [
@@ -80,7 +81,7 @@ final class UserController extends AbstractController
#[Route('/{id}/edit', name: 'admin_users_edit', requirements: ['id' => '[0-9a-fA-F\-]{36}'], methods: ['GET', 'POST'])]
public function edit(string $id, Request $request, UserAdminService $users): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
try {
$user = $users->requireUser($id);
@@ -127,7 +128,7 @@ final class UserController extends AbstractController
#[Route('/{id}/toggle-active', name: 'admin_users_toggle_active', requirements: ['id' => '[0-9a-fA-F\-]{36}'], methods: ['POST'])]
public function toggleActive(string $id, Request $request, UserAdminService $users): RedirectResponse
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
$this->denyAccessUnlessGranted(ApplicationRoles::ROLE_SUPER_ADMIN);
if (!$this->isCsrfTokenValid('admin_user_toggle_active_' . $id, (string) $request->request->get('_token'))) {
$this->addFlash('danger', 'Ungültiges CSRF-Token.');

View File

@@ -17,6 +17,11 @@ final class ApplicationRoles
public const ROLE_USER = 'ROLE_USER';
/**
* Roles that can be assigned from the Admin UI or console.
*
* Effective access is backed by config/packages/security.yaml and by
* controller-level guards for action-specific permissions.
*
* @return array<string, string>
*/
public static function assignableChoices(): array