• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FluidTYPO3 / vhs / 28589194938

02 Jul 2026 12:14PM UTC coverage: 70.533% (+0.2%) from 70.321%
28589194938

push

github

NamelessCoder
[TASK] Migrate security ViewHelpers away from legacy supports

5 of 6 new or added lines in 1 file covered. (83.33%)

4847 of 6872 relevant lines covered (70.53%)

4.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.93
/Classes/ViewHelpers/Security/AbstractSecurityViewHelper.php
1
<?php
2
namespace FluidTYPO3\Vhs\ViewHelpers\Security;
3

4
/*
5
 * This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use FluidTYPO3\Vhs\Utility\ContextUtility;
12
use FluidTYPO3\Vhs\Utility\RequestResolver;
13
use TYPO3\CMS\Core\Context\Context;
14
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
15
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
16
use TYPO3\CMS\Core\Utility\GeneralUtility;
17
use TYPO3\CMS\Extbase\Domain\Model\BackendUser;
18
use TYPO3\CMS\Extbase\Domain\Model\FrontendUser;
19
use TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup;
20
use TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository;
21
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
22
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
23
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
24

25
/**
26
 * Base class: Security ViewHelpers
27
 */
28
abstract class AbstractSecurityViewHelper extends AbstractConditionViewHelper
29
{
30
    /**
31
     * @var FrontendUserRepository
32
     */
33
    protected $frontendUserRepository;
34

35
    public function __construct()
36
    {
37
        if (!ExtensionManagementUtility::isLoaded('feuserextrafields')) {
×
38
            throw new \Exception('On TYPO3v12, v:security.* requires EXT:feuserextrafields', 1670521759);
×
39
        }
40
        /** @var FrontendUserRepository $frontendUserRepository */
41
        $frontendUserRepository = GeneralUtility::makeInstance(FrontendUserRepository::class);
×
42
        $this->frontendUserRepository = $frontendUserRepository;
×
43
        $query = $this->frontendUserRepository->createQuery();
×
44
        $querySettings = $query->getQuerySettings();
×
45
        $querySettings->setRespectStoragePage(false);
×
46
        $querySettings->setRespectSysLanguage(false);
×
47
        $this->frontendUserRepository->setDefaultQuerySettings($querySettings);
×
48
    }
49

50
    public function initializeArguments(): void
51
    {
52
        parent::initializeArguments();
8✔
53
        $this->registerArgument(
8✔
54
            'anyFrontendUser',
8✔
55
            'boolean',
8✔
56
            'If TRUE, allows any FrontendUser unless other arguments disallows each specific FrontendUser',
8✔
57
            false,
8✔
58
            false
8✔
59
        );
8✔
60
        $this->registerArgument(
8✔
61
            'anyFrontendUserGroup',
8✔
62
            'boolean',
8✔
63
            'If TRUE, allows any FrontendUserGroup unless other arguments disallows each specific FrontendUser',
8✔
64
            false,
8✔
65
            false
8✔
66
        );
8✔
67
        $this->registerArgument(
8✔
68
            'frontendUser',
8✔
69
            FrontendUser::class,
8✔
70
            'The FrontendUser to allow/deny'
8✔
71
        );
8✔
72
        $this->registerArgument(
8✔
73
            'frontendUsers',
8✔
74
            '<TYPO3\CMS\Extbase\Persistence\ObjectStorage>\TYPO3\CMS\Extbase\Domain\Model\FrontendUser',
8✔
75
            'The FrontendUsers ObjectStorage to allow/deny'
8✔
76
        );
8✔
77
        $this->registerArgument(
8✔
78
            'frontendUserGroup',
8✔
79
            FrontendUserGroup::class,
8✔
80
            'The FrontendUserGroup to allow/deny'
8✔
81
        );
8✔
82
        $this->registerArgument(
8✔
83
            'frontendUserGroups',
8✔
84
            '<TYPO3\CMS\Extbase\Persistence\ObjectStorage>\TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup',
8✔
85
            'The FrontendUserGroups ObjectStorage to allow/deny'
8✔
86
        );
8✔
87
        $this->registerArgument(
8✔
88
            'anyBackendUser',
8✔
89
            'boolean',
8✔
90
            'If TRUE, allows any backend user unless other arguments disallows each specific backend user',
8✔
91
            false,
8✔
92
            false
8✔
93
        );
8✔
94
        $this->registerArgument(
8✔
95
            'backendUser',
8✔
96
            'integer',
8✔
97
            'The uid of a backend user to allow/deny'
8✔
98
        );
8✔
99
        $this->registerArgument(
8✔
100
            'backendUsers',
8✔
101
            'mixed',
8✔
102
            'The backend users list to allow/deny. If string, CSV of uids assumed, if array, array of uids assumed'
8✔
103
        );
8✔
104
        $this->registerArgument(
8✔
105
            'backendUserGroup',
8✔
106
            'integer',
8✔
107
            'The uid of the backend user group to allow/deny'
8✔
108
        );
8✔
109
        $this->registerArgument(
8✔
110
            'backendUserGroups',
8✔
111
            'mixed',
8✔
112
            'The backend user groups list to allow/deny. If string, CSV of uids is assumed, if array, ' .
8✔
113
            'array of uids is assumed'
8✔
114
        );
8✔
115
        $this->registerArgument(
8✔
116
            'admin',
8✔
117
            'boolean',
8✔
118
            'If TRUE, a backend user which is also an admin is required'
8✔
119
        );
8✔
120
        $this->registerArgument(
8✔
121
            'evaluationType',
8✔
122
            'string',
8✔
123
            'Specify AND or OR (case sensitive) to determine how arguments must be processed. Default is AND, ' .
8✔
124
            'requiring all arguments to be satisfied if used',
8✔
125
            false,
8✔
126
            'AND'
8✔
127
        );
8✔
128
    }
129

130
    public static function verdict(array $arguments, RenderingContextInterface $renderingContext): bool
131
    {
132
        /** @var static $proxy */
133
        $proxy = GeneralUtility::makeInstance(static::class);
2✔
134
        $proxy->setArguments((array) $arguments);
2✔
135
        return $proxy->evaluateArguments();
2✔
136
    }
137

138

139
    /**
140
     * Returns TRUE if all conditions from arguments are satisfied. The
141
     * type of evaluation (AND or OR) can be set using argument "evaluationType".
142
     */
143
    public function evaluateArguments(): bool
144
    {
145
        /** @var FrontendUser|null $frontendUser */
146
        $frontendUser = $this->arguments['frontendUser'] ?? null;
2✔
147

148
        /** @var ObjectStorage|null $frontendUsers */
149
        $frontendUsers = $this->arguments['frontendUsers'] ?? null;
2✔
150

151
        /** @var BackendUser|null $backendUser */
152
        $backendUser = $this->arguments['backendUser'] ?? null;
2✔
153

154
        $evaluationType = $this->arguments['evaluationType'];
2✔
155
        $evaluations = [];
2✔
156
        if ($this->arguments['anyFrontendUser'] ?? false) {
2✔
157
            $evaluations['anyFrontendUser'] = intval($this->assertFrontendUserLoggedIn());
×
158
        }
159
        if ($this->arguments['anyFrontendUserGroup'] ?? false) {
2✔
160
            $evaluations['anyFrontendUserGroup'] = intval($this->assertFrontendUserGroupLoggedIn());
×
161
        }
162
        if ($frontendUser) {
2✔
163
            $evaluations['frontendUser'] = intval($this->assertFrontendUserLoggedIn($frontendUser));
×
164
        }
165
        if ($frontendUsers) {
2✔
166
            $evaluations['frontendUsers'] = intval($this->assertFrontendUsersLoggedIn($frontendUsers));
×
167
        }
168
        if (isset($this->arguments['frontendUserGroup'])) {
2✔
169
            $evaluations['frontendUserGroup'] =
×
170
                intval($this->assertFrontendUserGroupLoggedIn($this->arguments['frontendUserGroup']));
×
171
        }
172
        if (isset($this->arguments['frontendUserGroups'])) {
2✔
173
            $evaluations['frontendUserGroups'] =
×
174
                intval($this->assertFrontendUserGroupLoggedIn($this->arguments['frontendUserGroups']));
×
175
        }
176
        if ($this->arguments['anyBackendUser'] ?? false) {
2✔
177
            $evaluations['anyBackendUser'] = intval($this->assertBackendUserLoggedIn());
2✔
178
        }
179
        if ($this->arguments['anyBackendUserGroup'] ?? false) {
2✔
180
            $evaluations['anyBackendUserGroup'] = intval($this->assertBackendUserGroupLoggedIn());
×
181
        }
182
        if (isset($this->arguments['backendUser'])) {
2✔
183
            $evaluations['backendUser'] = intval($this->assertBackendUserLoggedIn($backendUser));
×
184
        }
185
        if (isset($this->arguments['backendUsers'])) {
2✔
186
            $evaluations['backendUsers'] = intval($this->assertBackendUserLoggedIn($backendUser));
×
187
        }
188
        if (isset($this->arguments['backendUserGroup'])) {
2✔
189
            $evaluations['backendUserGroup'] =
×
190
                intval($this->assertBackendUserGroupLoggedIn($this->arguments['backendUserGroup']));
×
191
        }
192
        if (isset($this->arguments['backendUserGroups'])) {
2✔
193
            $evaluations['backendUserGroups'] =
×
194
                intval($this->assertBackendUserGroupLoggedIn($this->arguments['backendUserGroups']));
×
195
        }
196
        if ($this->arguments['admin'] ?? false) {
2✔
197
            $evaluations['admin'] = intval($this->assertAdminLoggedIn());
×
198
        }
199
        $sum = array_sum($evaluations);
2✔
200
        return 'AND' === $evaluationType ? count($evaluations) === $sum : $sum > 0;
2✔
201
    }
202

203
    /**
204
     * Returns TRUE only if a FrontendUser is currently logged in. Use argument
205
     * to return TRUE only if the FrontendUser logged in must be that specific user.
206
     *
207
     * @param int|FrontendUser|null $frontendUser
208
     */
209
    public function assertFrontendUserLoggedIn($frontendUser = null): bool
210
    {
211
        $currentFrontendUser = $this->getCurrentFrontendUser();
×
212
        if (!$currentFrontendUser instanceof FrontendUser) {
×
213
            return false;
×
214
        }
215
        if ($frontendUser instanceof FrontendUser) {
×
216
            if ($currentFrontendUser->getUid() === $frontendUser->getUid()) {
×
217
                return true;
×
218
            } else {
219
                return false;
×
220
            }
221
        }
222
        return is_object($currentFrontendUser);
×
223
    }
224

225
    /**
226
     * Returns TRUE only if currently logged in frontend user is in list.
227
     */
228
    public function assertFrontendUsersLoggedIn(?ObjectStorage $frontendUsers = null): bool
229
    {
230
        if ($frontendUsers === null) {
×
231
            return false;
×
232
        }
233
        /** @var FrontendUser[] $frontendUsers */
234
        foreach ($frontendUsers as $frontendUser) {
×
235
            if ($this->assertFrontendUserLoggedIn($frontendUser)) {
×
236
                return true;
×
237
            }
238
        }
239
        return false;
×
240
    }
241

242
    /**
243
     * Returns TRUE if a FrontendUserGroup (specific given argument, else not) is logged in
244
     *
245
     * @param mixed $groups One \TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup or ObjectStorage containing same
246
     */
247
    public function assertFrontendUserGroupLoggedIn($groups = null): bool
248
    {
249
        $currentFrontendUser = $this->getCurrentFrontendUser();
×
250
        if (!$currentFrontendUser instanceof FrontendUser) {
×
251
            return false;
×
252
        }
253
        $currentFrontendUserGroups = $currentFrontendUser->getUsergroup();
×
254
        if (!$groups) {
×
255
            return (0 < $currentFrontendUserGroups->count());
×
256
        } elseif ($groups instanceof FrontendUserGroup) {
×
257
            return $currentFrontendUserGroups->contains($groups);
×
258
        } elseif ($groups instanceof ObjectStorage) {
×
259
            $currentFrontendUserGroupsClone = clone $currentFrontendUserGroups;
×
260
            $currentFrontendUserGroupsClone->removeAll($groups);
×
261
            return ($currentFrontendUserGroups->count() !== $currentFrontendUserGroupsClone->count());
×
262
        }
263
        return false;
×
264
    }
265

266
    /**
267
     * Returns TRUE only if a backend user is currently logged in. If used,
268
     * argument specifies that the logged in user must be that specific user
269
     *
270
     * @param int|BackendUser|null $backendUser
271
     */
272
    public function assertBackendUserLoggedIn($backendUser = null): bool
273
    {
274
        if ($backendUser instanceof BackendUser) {
34✔
275
            $backendUser = $backendUser->getUid();
×
276
        }
277
        $currentBackendUser = $this->getCurrentBackendUser();
34✔
278
        if (null !== $backendUser) {
34✔
279
            return ((int) ($currentBackendUser['uid'] ?? 0) === $backendUser);
10✔
280
        }
281
        return is_array($currentBackendUser);
24✔
282
    }
283

284
    /**
285
     * Returns TRUE only if a backend user is logged in and either has any group
286
     * (if param left out) or is a member of the group $groups or a group in
287
     * the array/CSV $groups
288
     *
289
     * @param mixed $groups Array of group uids or CSV of group uids or one group uid
290
     */
291
    public function assertBackendUserGroupLoggedIn($groups = null): bool
292
    {
293
        if (!$this->assertBackendUserLoggedIn()) {
20✔
294
            return false;
2✔
295
        }
296
        $currentBackendUser = $this->getCurrentBackendUser();
18✔
297
        $currentUserGroups = trim($currentBackendUser['usergroup'] ?? '', ',');
18✔
298
        /** @var array $userGroups */
299
        $userGroups = !empty($currentUserGroups) ? explode(',', $currentUserGroups) : [];
18✔
300
        if (0 === count($userGroups)) {
18✔
301
            return false;
2✔
302
        }
303
        if (is_string($groups)) {
16✔
304
            $groups = trim($groups, ',');
4✔
305
            /** @var array $groups */
306
            $groups = !empty($groups) ? explode(',', $groups) : [];
4✔
307
        }
308
        /** @var array $groups */
309
        if (count($groups) > 0) {
16✔
310
            return count(array_intersect($userGroups, (array) $groups)) > 0;
14✔
311
        }
312
        return false;
2✔
313
    }
314

315
    /**
316
     * Returns TRUE only if there is a current user logged in and this user
317
     * is an admin class backend user.
318
     */
319
    public function assertAdminLoggedIn(): bool
320
    {
321
        /** @var Context $context */
322
        $context = GeneralUtility::makeInstance(Context::class);
×
323
        try {
324
            return (bool) $context->getPropertyFromAspect('backend.user', 'isAdmin');
×
325
        } catch (AspectNotFoundException $e) {
×
326
            return false;
×
327
        }
328
    }
329

330
    /**
331
     * Gets the currently logged in Frontend User.
332
     */
333
    public function getCurrentFrontendUser(): ?FrontendUser
334
    {
335
        $frontendUserAuthentication = RequestResolver::getFrontendUser();
2✔
336

337
        if (!$frontendUserAuthentication) {
2✔
338
            return null;
2✔
339
        }
340

341
        /** @var FrontendUser|null $frontendUser */
NEW
342
        $frontendUser = $this->frontendUserRepository->findByUid($frontendUserAuthentication?->user['uid'] ?? 0);
×
343
        return $frontendUser;
×
344
    }
345

346
    /**
347
     * Returns a be_user record as lowerCamelCase indexed array if a BE user is
348
     * currently logged in.
349
     */
350
    public function getCurrentBackendUser(): ?array
351
    {
352
        return RequestResolver::getBackendUser()?->user;
14✔
353
    }
354

355
    /**
356
     * Override: forcibly disables page caching - a TRUE condition
357
     * in this ViewHelper means page content would be depending on
358
     * the current visitor's session/cookie/auth etc.
359
     *
360
     * Returns value of "then" attribute.
361
     * If then attribute is not set, iterates through child nodes and renders ThenViewHelper.
362
     * If then attribute is not set and no ThenViewHelper and no ElseViewHelper is found, all child nodes are rendered
363
     *
364
     * @return mixed rendered ThenViewHelper or contents of <f:if> if no ThenViewHelper was found
365
     */
366
    protected function renderThenChild(): mixed
367
    {
368
        if ($this->isFrontendContext()) {
2✔
369
            RequestResolver::disableFrontendCache('EXT:vhs: Security ViewHelper disables caches.');
2✔
370
        }
371
        return parent::renderThenChild();
2✔
372
    }
373

374
    /**
375
     * @codeCoverageIgnore
376
     */
377
    protected function isFrontendContext(): bool
378
    {
379
        return ContextUtility::isFrontend();
380
    }
381
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc