Issues (257)

src/Controller/UserController.php (2 issues)

Severity
1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
namespace App\Controller;
24
25
use App\DataTables\LogDataTable;
26
use App\Entity\Attachments\UserAttachment;
27
use App\Entity\Base\AbstractNamedDBElement;
28
use App\Entity\Parameters\AbstractParameter;
29
use App\Entity\UserSystem\User;
30
use App\Events\SecurityEvent;
31
use App\Events\SecurityEvents;
32
use App\Form\Permissions\PermissionsType;
33
use App\Form\UserAdminForm;
34
use App\Services\ImportExportSystem\EntityExporter;
35
use App\Services\ImportExportSystem\EntityImporter;
36
use App\Services\Trees\StructuralElementRecursionHelper;
37
use App\Services\UserSystem\PermissionPresetsHelper;
38
use App\Services\UserSystem\PermissionSchemaUpdater;
39
use Doctrine\ORM\EntityManagerInterface;
40
use Exception;
41
use InvalidArgumentException;
42
use Omines\DataTablesBundle\DataTableFactory;
43
use Symfony\Component\Asset\Packages;
44
use Symfony\Component\Form\FormInterface;
45
use Symfony\Component\HttpFoundation\RedirectResponse;
46
use Symfony\Component\HttpFoundation\Request;
47
use Symfony\Component\HttpFoundation\Response;
48
use Symfony\Component\Routing\Annotation\Route;
49
50
/**
51
 * @Route("/user")
52
 * Class UserController
53
 */
54
class UserController extends AdminPages\BaseAdminController
55
{
56
    protected string $entity_class = User::class;
57
    protected string $twig_template = 'admin/user_admin.html.twig';
58
    protected string $form_class = UserAdminForm::class;
59
    protected string $route_base = 'user';
60
    protected string $attachment_class = UserAttachment::class;
61
    protected ?string $parameter_class = null;
62
63
    protected function additionalActionEdit(FormInterface $form, AbstractNamedDBElement $entity): bool
64
    {
65
        //Check if we editing a user and if we need to change the password of it
66
        if ($entity instanceof User && !empty($form['new_password']->getData())) {
67
            $password = $this->passwordEncoder->hashPassword($entity, $form['new_password']->getData());
68
            $entity->setPassword($password);
69
            //By default the user must change the password afterwards
70
            $entity->setNeedPwChange(true);
71
72
            $event = new SecurityEvent($entity);
73
            $this->eventDispatcher->dispatch($event, SecurityEvents::PASSWORD_CHANGED);
74
        }
75
76
        return true;
77
    }
78
79
    /**
80
     * @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="user_edit")
81
     * @Route("/{id}/", requirements={"id"="\d+"})
82
     *
83
     * @throws Exception
84
     */
85
    public function edit(User $entity, Request $request, EntityManagerInterface $em,  PermissionPresetsHelper $permissionPresetsHelper, PermissionSchemaUpdater $permissionSchemaUpdater, ?string $timestamp = null): Response
86
    {
87
        //Do an upgrade of the permission schema if needed (so the user can see the permissions a user get on next request (even if it was not done yet)
88
        $permissionSchemaUpdater->userUpgradeSchemaRecursively($entity);
89
90
        //Handle 2FA disabling
91
        if ($request->request->has('reset_2fa')) {
92
            //Check if the admin has the needed permissions
93
            $this->denyAccessUnlessGranted('set_password', $entity);
94
            if ($this->isCsrfTokenValid('reset_2fa'.$entity->getId(), $request->request->get('_token'))) {
95
                //Disable Google authenticator
96
                $entity->setGoogleAuthenticatorSecret(null);
97
                $entity->setBackupCodes([]);
98
                //Remove all U2F keys
99
                foreach ($entity->getLegacyU2FKeys() as $key) {
100
                    $em->remove($key);
101
                }
102
                foreach ($entity->getWebAuthnKeys() as $key) {
103
                    $em->remove($key);
104
                }
105
                //Invalidate trusted devices
106
                $entity->invalidateTrustedDeviceTokens();
107
                $em->flush();
108
109
                $event = new SecurityEvent($entity);
110
                $this->eventDispatcher->dispatch($event, SecurityEvents::TFA_ADMIN_RESET);
111
112
                $this->addFlash('success', 'user.edit.reset_success');
113
            } else {
114
                $this->addFlash('danger', 'csfr_invalid');
115
            }
116
        }
117
118
        //Handle permissions presets
119
        if ($request->request->has('permission_preset')) {
120
            $this->denyAccessUnlessGranted('edit_permissions', $entity);
121
            if ($this->isCsrfTokenValid('reset_2fa'.$entity->getId(), $request->request->get('_token'))) {
122
                $preset = $request->request->get('permission_preset');
123
124
                $permissionPresetsHelper->applyPreset($entity, $preset);
125
126
                $em->flush();
127
128
                $this->addFlash('success', 'user.edit.permission_success');
129
130
                //We need to stop the execution here, or our permissions changes will be overwritten by the form values
131
                return $this->redirectToRoute('user_edit', ['id' => $entity->getID()]);
132
            } else {
133
                $this->addFlash('danger', 'csfr_invalid');
134
            }
135
        }
136
137
        return $this->_edit($entity, $request, $em, $timestamp);
138
    }
139
140
    protected function additionalActionNew(FormInterface $form, AbstractNamedDBElement $entity): bool
141
    {
142
        if ($entity instanceof User && !empty($form['new_password']->getData())) {
143
            $password = $this->passwordEncoder->hashPassword($entity, $form['new_password']->getData());
144
            $entity->setPassword($password);
145
            //By default the user must change the password afterwards
146
            $entity->setNeedPwChange(true);
147
        }
148
149
        return true;
150
    }
151
152
    /**
153
     * @Route("/new", name="user_new")
154
     * @Route("/{id}/clone", name="user_clone")
155
     * @Route("/")
156
     */
157
    public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?User $entity = null): Response
158
    {
159
        return $this->_new($request, $em, $importer, $entity);
160
    }
161
162
    /**
163
     * @Route("/{id}", name="user_delete", methods={"DELETE"}, requirements={"id"="\d+"})
164
     */
165
    public function delete(Request $request, User $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse
166
    {
167
        if (User::ID_ANONYMOUS === $entity->getID()) {
168
            throw new InvalidArgumentException('You can not delete the anonymous user! It is needed for permission checking without a logged in user');
169
        }
170
171
        return $this->_delete($request, $entity, $recursionHelper);
172
    }
173
174
    /**
175
     * @Route("/export", name="user_export_all")
176
     */
177
    public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response
178
    {
179
        return $this->_exportAll($em, $exporter, $request);
180
    }
181
182
    /**
183
     * @Route("/{id}/export", name="user_export")
184
     */
185
    public function exportEntity(User $entity, EntityExporter $exporter, Request $request): Response
186
    {
187
        return $this->_exportEntity($entity, $exporter, $request);
188
    }
189
190
    /**
191
     * @Route("/info", name="user_info_self")
192
     * @Route("/{id}/info", name="user_info")
193
     */
194
    public function userInfo(?User $user, Packages $packages, Request $request, DataTableFactory $dataTableFactory): Response
0 ignored issues
show
The parameter $packages is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

194
    public function userInfo(?User $user, /** @scrutinizer ignore-unused */ Packages $packages, Request $request, DataTableFactory $dataTableFactory): Response

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $dataTableFactory is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

194
    public function userInfo(?User $user, Packages $packages, Request $request, /** @scrutinizer ignore-unused */ DataTableFactory $dataTableFactory): Response

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
195
    {
196
        //If no user id was passed, then we show info about the current user
197
        if (null === $user) {
198
            $tmp = $this->getUser();
199
            if (!$tmp instanceof User) {
200
                throw new InvalidArgumentException('Userinfo only works for database users!');
201
            }
202
            $user = $tmp;
203
        } else {
204
            //Else we must check, if the current user is allowed to access $user
205
            $this->denyAccessUnlessGranted('read', $user);
206
        }
207
208
        $table = $this->dataTableFactory->createFromType(
209
            LogDataTable::class,
210
            [
211
                'filter_elements' => $user,
212
                'mode' => 'element_history',
213
            ],
214
            ['pageLength' => 10]
215
        )
216
            ->handleRequest($request);
217
218
        if ($table->isCallback()) {
219
            return $table->getResponse();
220
        }
221
222
        //Show permissions to user
223
        $builder = $this->createFormBuilder()->add('permissions', PermissionsType::class, [
224
            'mapped' => false,
225
            'disabled' => true,
226
            'inherit' => true,
227
            'data' => $user,
228
        ]);
229
230
        return $this->renderForm('users/user_info.html.twig', [
231
            'user' => $user,
232
            'form' => $builder->getForm(),
233
            'datatable' => $table,
234
        ]);
235
    }
236
}
237