UserController.php 108 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

/**
* Copyright Maarch since 2008 under licence GPLv3.
* See LICENCE.txt file at the root folder for more details.
* This file is part of Maarch software.
*
*/

/**
* @brief User Controller
* @author dev@maarch.org
*/

Damien's avatar
Damien committed
15
namespace User\controllers;
16

Damien's avatar
Damien committed
17
use Basket\models\BasketModel;
18
use Basket\models\GroupBasketModel;
Damien's avatar
Damien committed
19
use Basket\models\RedirectBasketModel;
20
use Configuration\models\ConfigurationModel;
21
use Contact\models\ContactGroupListModel;
22
use Contact\models\ContactGroupModel;
23
use ContentManagement\controllers\DocumentEditorController;
24
use ContentManagement\controllers\MergeController;
25
use Docserver\controllers\DocserverController;
Damien's avatar
Damien committed
26
use Docserver\models\DocserverModel;
27
use Email\controllers\EmailController;
Damien's avatar
Damien committed
28
use Entity\models\EntityModel;
29
use Entity\models\ListInstanceModel;
30
use Entity\models\ListTemplateItemModel;
Damien's avatar
Damien committed
31
use Entity\models\ListTemplateModel;
32
33
use Firebase\JWT\JWT;
use Group\controllers\PrivilegeController;
Damien's avatar
Damien committed
34
use Group\models\GroupModel;
Damien's avatar
Damien committed
35
36
use History\controllers\HistoryController;
use History\models\HistoryModel;
Nathan Cheval's avatar
Nathan Cheval committed
37
38
use Notification\controllers\NotificationsEventsController;
use Parameter\models\ParameterModel;
Damien's avatar
Damien committed
39
use Resource\controllers\ResController;
40
use Resource\controllers\StoreController;
41
use Resource\models\ResModel;
42
use Resource\models\UserFollowedResourceModel;
Alex ORLUC's avatar
Alex ORLUC committed
43
use Respect\Validation\Validator;
44
45
use Slim\Http\Request;
use Slim\Http\Response;
46
use SrcCore\controllers\AuthenticationController;
Damien's avatar
Damien committed
47
use SrcCore\controllers\PasswordController;
48
use SrcCore\controllers\UrlController;
Damien's avatar
Damien committed
49
use SrcCore\models\AuthenticationModel;
Damien's avatar
Damien committed
50
use SrcCore\models\CoreConfigModel;
Damien's avatar
Damien committed
51
use SrcCore\models\DatabaseModel;
Damien's avatar
Damien committed
52
use SrcCore\models\PasswordModel;
53
use SrcCore\models\ValidatorModel;
54
use Template\models\TemplateModel;
Damien's avatar
Damien committed
55
use User\models\UserBasketPreferenceModel;
56
use User\models\UserEmailSignatureModel;
57
use User\models\UserEntityModel;
58
use User\models\UserGroupModel;
Damien's avatar
Damien committed
59
use User\models\UserModel;
Damien's avatar
Damien committed
60
use User\models\UserSignatureModel;
61

62
63
class UserController
{
64
    const ALTERNATIVES_CONNECTIONS_METHODS = ['sso', 'cas', 'ldap', 'keycloak', 'shibboleth', 'azure_saml'];
65

Damien's avatar
Damien committed
66
67
    public function get(Request $request, Response $response)
    {
68
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_users', 'userId' => $GLOBALS['id']])) {
Damien's avatar
Damien committed
69
70
71
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

72
        if (UserController::isRoot(['id' => $GLOBALS['id']])) {
Damien's avatar
Damien committed
73
            $users = UserModel::get([
74
                'select'    => ['id', 'user_id', 'firstname', 'lastname', 'status', 'mail', 'mode'],
75
76
                'where'     => ['status != ?'],
                'data'      => ['DEL']
Damien's avatar
Damien committed
77
78
            ]);
        } else {
79
            $entities = EntityModel::getAllEntitiesByUserId(['userId' => $GLOBALS['id']]);
80
81
            $users = [];
            if (!empty($entities)) {
Damien's avatar
Damien committed
82
                $users = UserEntityModel::getWithUsers([
83
                    'select'    => ['DISTINCT users.id', 'users.user_id', 'firstname', 'lastname', 'status', 'mail', 'mode'],
84
85
                    'where'     => ['users_entities.entity_id in (?)', 'status != ?'],
                    'data'      => [$entities, 'DEL']
86
87
                ]);
            }
88
            $usersNoEntities = UserEntityModel::getUsersWithoutEntities(['select' => ['id', 'users.user_id', 'firstname', 'lastname', 'status', 'mail', 'mode']]);
89
            $users = array_merge($users, $usersNoEntities);
Damien's avatar
Damien committed
90
91
        }

Nathan Cheval's avatar
Nathan Cheval committed
92
93
94
        $quota = [];
        $userQuota = ParameterModel::getById(['id' => 'user_quota', 'select' => ['param_value_int']]);
        if (!empty($userQuota['param_value_int'])) {
Damien's avatar
Damien committed
95
96
            $activeUser = UserModel::get(['select' => ['count(1)'], 'where' => ['status = ?', 'mode != ?'], 'data' => ['OK', 'root_invisible']]);
            $inactiveUser = UserModel::get(['select' => ['count(1)'], 'where' => ['status = ?', 'mode != ?'], 'data' => ['SPD', 'root_invisible']]);
Nathan Cheval's avatar
Nathan Cheval committed
97
98
99
100
            $quota = ['actives' => $activeUser[0]['count'], 'inactives' => $inactiveUser[0]['count'], 'userQuota' => $userQuota['param_value_int']];
        }

        return $response->withJson(['users' => $users, 'quota' => $quota]);
Damien's avatar
Damien committed
101
102
    }

103
104
    public function getById(Request $request, Response $response, array $args)
    {
105
        $user = UserModel::getById(['id' => $args['id'], 'select' => ['id', 'firstname', 'lastname', 'status', 'mail', 'phone']]);
106
107
108
        if (empty($user)) {
            return $response->withStatus(400)->withJson(['errors' => 'User does not exist']);
        }
109
110
        $user['enabled'] = $user['status'] != 'SPD';
        unset($user['status']);
111

112
113
114
115
116
117
118
        $primaryEntity = UserModel::getPrimaryEntityById(['id' => $args['id'], 'select' => ['entity_label']]);
        $user['department'] = $primaryEntity['entity_label'];

        if ($GLOBALS['id'] != $args['id'] && !PrivilegeController::hasPrivilege(['privilegeId' => 'view_personal_data', 'userId' => $GLOBALS['id']])) {
            unset($user['phone']);
        }

119
120
121
        return $response->withJson($user);
    }

122
    public function getDetailledById(Request $request, Response $response, array $args)
Damien's avatar
Damien committed
123
    {
124
        $error = $this->hasUsersRights(['id' => $args['id']]);
Damien's avatar
Damien committed
125
126
127
128
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
        }

129
        $user = UserModel::getById(['id' => $args['id'], 'select' => ['id', 'user_id', 'firstname', 'lastname', 'status', 'phone', 'mail', 'initials', 'mode', 'authorized_api', 'external_id', 'absence']]);
130
        $user['external_id']        = json_decode($user['external_id'], true);
Damien's avatar
Damien committed
131
        $user['authorizedApi']      = json_decode($user['authorized_api'], true);
132
        $user['absence']            = json_decode($user['absence'], true);
Damien's avatar
Damien committed
133
        unset($user['authorized_api']);
134

135
136
        if ($GLOBALS['id'] == $args['id'] || PrivilegeController::hasPrivilege(['privilegeId' => 'view_personal_data', 'userId' => $GLOBALS['id']])) {
            $user['signatures'] = UserSignatureModel::getByUserSerialId(['userSerialid' => $args['id']]);
137
            $user['emailSignatures'] = UserEmailSignatureModel::getByUserId(['userId' => $user['id']]);
138
139
140
141
142
143
        } else {
            $user['signatures'] = [];
            $user['emailSignatures'] = [];
            unset($user['phone']);
        }

144
        $user['groups']             = UserModel::getGroupsById(['id' => $args['id']]);
145
        $user['allGroups']          = GroupModel::getAvailableGroupsByUserId(['userId' => $user['id'], 'administratorId' => $GLOBALS['id']]);
146
        $user['entities']           = UserModel::getEntitiesById(['id' => $args['id'], 'select' => ['entities.id', 'users_entities.entity_id', 'entities.entity_label', 'users_entities.user_role', 'users_entities.primary_entity'], 'orderBy' => ['users_entities.primary_entity DESC']]);
147
        $user['allEntities']        = EntityModel::getAvailableEntitiesForAdministratorByUserId(['userId' => $user['user_id'], 'administratorUserId' => $GLOBALS['login']]);
148
        $user['baskets']            = BasketModel::getBasketsByLogin(['login' => $user['user_id']]);
Damien's avatar
Damien committed
149
150
        $user['assignedBaskets']    = RedirectBasketModel::getAssignedBasketsByUserId(['userId' => $user['id']]);
        $user['redirectedBaskets']  = RedirectBasketModel::getRedirectedBasketsByUserId(['userId' => $user['id']]);
151
        $user['history']            = HistoryModel::get(['select' => ['record_id', 'event_date', 'info', 'remote_ip'], 'where' => ['user_id = ?'], 'data' => [$args['id']], 'orderBy' => ['event_date DESC'], 'limit' => 500]);
152
153
154
        $user['canModifyPassword']              = false;
        $user['canSendActivationNotification']  = false;
        $user['canCreateMaarchParapheurUser']   = false;
Florian Azizian's avatar
Florian Azizian committed
155

156
        if ($user['mode'] == 'rest') {
Damien's avatar
Damien committed
157
            $user['canModifyPassword'] = true;
158
        }
159
        $loggingMethod = CoreConfigModel::getLoggingMethod();
160
        if ($user['mode'] != 'rest' && $loggingMethod['id'] == 'standard') {
161
162
163
            $user['canSendActivationNotification'] = true;
        }

164
        $loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'modules/visa/xml/remoteSignatoryBooks.xml']);
165
        if ((string)$loadedXml->signatoryBookEnabled == 'maarchParapheur' && $user['mode'] != 'rest' && empty($user['external_id']['maarchParapheur'])) {
166
167
            $user['canCreateMaarchParapheurUser'] = true;
        }
Damien's avatar
Damien committed
168
169
170
171

        return $response->withJson($user);
    }

172
    public function create(Request $request, Response $response)
173
    {
174
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_users', 'userId' => $GLOBALS['id']])) {
175
176
177
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
        $body = $request->getParsedBody();

        if (empty($body)) {
            return $response->withStatus(400)->withJson(['errors' => 'Body is empty']);
        } elseif (!Validator::stringType()->length(1, 128)->notEmpty()->validate($body['userId'] ?? null) || !preg_match("/^[\w.@-]*$/", $body['userId'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Body userId is empty, not a string or not valid']);
        } elseif (!Validator::stringType()->length(1, 255)->notEmpty()->validate($body['firstname'] ?? null)) {
            return $response->withStatus(400)->withJson(['errors' => 'Body firstname is empty or not a string']);
        } elseif (!Validator::stringType()->length(1, 255)->notEmpty()->validate($body['lastname'] ?? null)) {
            return $response->withStatus(400)->withJson(['errors' => 'Body lastname is empty or not a string']);
        } elseif (!Validator::stringType()->length(0, 32)->validate($body['initials'] ?? '')) {
            return $response->withStatus(400)->withJson(['errors' => 'Body initials is too long']);
        } elseif (!Validator::stringType()->length(1, 255)->notEmpty()->validate($body['mail'] ?? null) || !filter_var($body['mail'], FILTER_VALIDATE_EMAIL)) {
            return $response->withStatus(400)->withJson(['errors' => 'Body mail is empty or not valid']);
        } elseif (PrivilegeController::hasPrivilege(['privilegeId' => 'manage_personal_data', 'userId' => $GLOBALS['id']]) && !empty($body['phone']) && (!preg_match("/\+?((|\ |\.|\(|\)|\-)?(\d)*)*\d$/", $body['phone']) || !Validator::stringType()->length(0, 32)->validate($body['phone'] ?? ''))) {
            return $response->withStatus(400)->withJson(['errors' => 'Body phone is not valid']);
194
195
        }

196
        $loggingMethod = CoreConfigModel::getLoggingMethod();
197
        $existingUser = UserModel::getByLowerLogin(['login' => $body['userId'], 'select' => ['id', 'status', 'mail']]);
198

Damien's avatar
Damien committed
199
        if (!empty($existingUser) && $existingUser['status'] == 'DEL') {
Damien's avatar
Damien committed
200
201
            UserModel::update([
                'set'   => [
202
203
                    'status'    => 'OK',
                    'password'  => AuthenticationModel::getPasswordHash(AuthenticationModel::generatePassword()),
Damien's avatar
Damien committed
204
205
206
207
                ],
                'where' => ['id = ?'],
                'data'  => [$existingUser['id']]
            ]);
Damien's avatar
Damien committed
208

209
            if ($loggingMethod['id'] == 'standard') {
210
                AuthenticationController::sendAccountActivationNotification(['userId' => $existingUser['id'], 'userEmail' => $existingUser['mail']]);
211
212
213
            }

            return $response->withJson(['id' => $existingUser['id']]);
Damien's avatar
Damien committed
214
215
        } elseif (!empty($existingUser)) {
            return $response->withStatus(400)->withJson(['errors' => _USER_ID_ALREADY_EXISTS]);
Damien's avatar
Damien committed
216
217
        }

218
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'manage_personal_data', 'userId' => $GLOBALS['id']])) {
219
            $body['phone'] = null;
220
221
        }

222
        $modes = ['standard', 'rest', 'root_visible', 'root_invisible'];
223
224
        if (empty($body['mode']) || !in_array($body['mode'], $modes)) {
            $body['mode'] = 'standard';
225
226
        }

227
        if (in_array($body['mode'], ['root_visible', 'root_invisible']) && !UserController::isRoot(['id' => $GLOBALS['id']])) {
228
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
Damien's avatar
Damien committed
229
230
        }

231
232
233
234
235
        $preferences = ['documentEdition' => 'java'];
        $allowedMethods = DocumentEditorController::getAllowedMethods();
        if (in_array('onlyoffice', $allowedMethods)) {
            $preferences = ['documentEdition' => 'onlyoffice'];
        }
236
        $body['preferences'] = json_encode($preferences);
237

238
        $id = UserModel::create(['user' => $body]);
Damien's avatar
Damien committed
239

Nathan Cheval's avatar
Nathan Cheval committed
240
241
        $userQuota = ParameterModel::getById(['id' => 'user_quota', 'select' => ['param_value_int']]);
        if (!empty($userQuota['param_value_int'])) {
Damien's avatar
Damien committed
242
            $activeUser = UserModel::get(['select' => ['count(1)'], 'where' => ['status = ?', 'mode != ?'], 'data' => ['OK', 'root_invisible']]);
Nathan Cheval's avatar
Nathan Cheval committed
243
            if ($activeUser[0]['count'] > $userQuota['param_value_int']) {
244
                NotificationsEventsController::fillEventStack(['eventId' => 'user_quota', 'tableName' => 'users', 'recordId' => 'quota_exceed', 'userId' => $GLOBALS['id'], 'info' => _QUOTA_EXCEEDED]);
Nathan Cheval's avatar
Nathan Cheval committed
245
246
247
            }
        }

248
        if ($loggingMethod['id'] == 'standard') {
249
            AuthenticationController::sendAccountActivationNotification(['userId' => $id, 'userEmail' => $body['mail']]);
250
251
        }

Damien's avatar
Damien committed
252
253
        HistoryController::add([
            'tableName'    => 'users',
254
            'recordId'     => $GLOBALS['id'],
Damien's avatar
Damien committed
255
256
            'eventType'    => 'ADD',
            'eventId'      => 'userCreation',
257
            'info'         => _USER_CREATED . " {$body['userId']}"
Damien's avatar
Damien committed
258
259
        ]);

260
        return $response->withJson(['id' => $id]);
261
262
    }

263
    public function update(Request $request, Response $response, array $aArgs)
264
    {
Damien's avatar
Damien committed
265
        $error = $this->hasUsersRights(['id' => $aArgs['id']]);
266
267
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
Damien's avatar
Damien committed
268
        }
269

Damien's avatar
Damien committed
270
        $body = $request->getParsedBody();
271

272
        if (!Validator::stringType()->length(1, 255)->notEmpty()->validate($body['firstname'])) {
Damien's avatar
Damien committed
273
            return $response->withStatus(400)->withJson(['errors' => 'Body firstname is empty or not a string']);
274
        } elseif (!Validator::stringType()->length(1, 255)->notEmpty()->validate($body['lastname'])) {
Damien's avatar
Damien committed
275
            return $response->withStatus(400)->withJson(['errors' => 'Body lastname is empty or not a string']);
276
        } elseif (!Validator::stringType()->length(0, 32)->validate($body['initials'] ?? '')) {
277
278
            return $response->withStatus(400)->withJson(['errors' => 'Body initials is too long']);
        } elseif (!empty($body['mail']) && !filter_var($body['mail'], FILTER_VALIDATE_EMAIL) && Validator::stringType()->length(1, 255)->notEmpty()->validate($body['mail'])) {
Damien's avatar
Damien committed
279
            return $response->withStatus(400)->withJson(['errors' => 'Body mail is not correct']);
280
        } elseif (PrivilegeController::hasPrivilege(['privilegeId' => 'manage_personal_data', 'userId' => $GLOBALS['id']]) && !empty($body['phone']) && (!preg_match("/\+?((|\ |\.|\(|\)|\-)?(\d)*)*\d$/", $body['phone']) || !Validator::stringType()->length(0, 32)->validate($body['phone'] ?? ''))) {
Damien's avatar
Damien committed
281
            return $response->withStatus(400)->withJson(['errors' => 'Body phone is not correct']);
282
283
        }

Damien's avatar
Damien committed
284
285
        $user = UserModel::getById(['id' => $aArgs['id'], 'select' => ['status', 'mode']]);

Damien's avatar
Damien committed
286
        $set = [
Damien's avatar
Damien committed
287
288
289
290
            'firstname' => $body['firstname'],
            'lastname'  => $body['lastname'],
            'mail'      => $body['mail'],
            'initials'  => $body['initials'],
Damien's avatar
Damien committed
291
        ];
292
293

        if (PrivilegeController::hasPrivilege(['privilegeId' => 'manage_personal_data', 'userId' => $GLOBALS['id']])) {
Damien's avatar
Damien committed
294
            $set['phone'] = $body['phone'];
295
296
        }

Damien's avatar
Damien committed
297
        if (!empty($body['status']) && $body['status'] == 'OK') {
298
            $set['status'] = 'OK';
Damien's avatar
Damien committed
299
        }
Damien's avatar
Damien committed
300
301
        if (!empty($body['mode']) && $user['mode'] != $body['mode']) {
            if ((in_array($body['mode'], ['root_visible', 'root_invisible']) || in_array($user['mode'], ['root_visible', 'root_invisible'])) && !UserController::isRoot(['id' => $GLOBALS['id']])) {
302
303
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
Damien's avatar
Damien committed
304
            $set['mode'] = $body['mode'];
305
        }
Damien's avatar
Damien committed
306

307
308
        if ($body['mode'] == 'rest' && isset($body['authorizedApi']) && is_array($body['authorizedApi'])) {
            foreach ($body['authorizedApi'] as $value) {
309
                if (strpos($value, 'GET/') !== 0 && strpos($value, 'POST/') !== 0 && strpos($value, 'PUT/') !== 0 && strpos($value, 'DELETE/') !== 0) {
310
                    return $response->withStatus(400)->withJson(['errors' => 'Body authorizedApi is not well formatted', 'lang' => 'authorizedRoutesNotWellFormatted']);
311
312
                }
            }
Damien's avatar
Damien committed
313
314
            $set['authorized_api'] = json_encode($body['authorizedApi']);
        }
Nathan Cheval's avatar
Nathan Cheval committed
315

316
317
        $userQuota = ParameterModel::getById(['id' => 'user_quota', 'select' => ['param_value_int']]);

Damien's avatar
Damien committed
318
319
320
321
322
        UserModel::update([
            'set'   => $set,
            'where' => ['id = ?'],
            'data'  => [$aArgs['id']]
        ]);
323

Damien's avatar
Damien committed
324
        if (!empty($userQuota['param_value_int'])) {
Damien's avatar
Damien committed
325
326
            if ($user['status'] == 'SPD' && $body['status'] == 'OK') {
                $activeUser = UserModel::get(['select' => ['count(1)'], 'where' => ['status = ?', 'mode != ?'], 'data' => ['OK', 'root_invisible']]);
Damien's avatar
Damien committed
327
                if ($activeUser[0]['count'] > $userQuota['param_value_int']) {
328
                    NotificationsEventsController::fillEventStack(['eventId' => 'user_quota', 'tableName' => 'users', 'recordId' => 'quota_exceed', 'userId' => $GLOBALS['id'], 'info' => _QUOTA_EXCEEDED]);
Damien's avatar
Damien committed
329
                }
Nathan Cheval's avatar
Nathan Cheval committed
330
331
332
            }
        }

Damien's avatar
Damien committed
333
334
        HistoryController::add([
            'tableName'    => 'users',
335
            'recordId'     => $GLOBALS['id'],
336
337
            'eventType'    => 'UP',
            'eventId'      => 'userModification',
Damien's avatar
Damien committed
338
            'info'         => _USER_UPDATED . " {$body['firstname']} {$body['lastname']}"
Damien's avatar
Damien committed
339
340
        ]);

Damien's avatar
Damien committed
341
        return $response->withStatus(204);
342
343
    }

Damien's avatar
Damien committed
344
345
346
347
348
349
350
351
352
353
354
355
    public function isDeletable(Request $request, Response $response, array $aArgs)
    {
        $error = $this->hasUsersRights(['id' => $aArgs['id'], 'delete' => true, 'himself' => true]);
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
        }

        $isListInstanceDeletable = true;
        $isListTemplateDeletable = true;

        $listInstanceEntities = [];
        $listInstanceResIds = [];
356
        $listInstances = ListInstanceModel::getWhenOpenMailsByUserId(['select' => ['listinstance.res_id', 'res_letterbox.destination'], 'userId' => $aArgs['id'], 'itemMode' => 'dest']);
Damien's avatar
Damien committed
357
        foreach ($listInstances as $listInstance) {
358
            if (!ResController::hasRightByResId(['resId' => [$listInstance['res_id']], 'userId' => $GLOBALS['id']])) {
Damien's avatar
Damien committed
359
360
361
                $isListInstanceDeletable = false;
            }
            $listInstanceResIds[] = $listInstance['res_id'];
Damien's avatar
Damien committed
362
363
364
            if (!empty($listInstance['destination'])) {
                $listInstanceEntities[] = $listInstance['destination'];
            }
Damien's avatar
Damien committed
365
366
367
        }

        $listTemplateEntities = [];
368
369
370
371
        $listTemplates = ListTemplateModel::getWithItems([
            'select'    => ['entity_id', 'title'],
            'where'     => ['item_id = ?', 'type = ?', 'item_mode = ?', 'item_type = ?', 'entity_id is not null'],
            'data'      => [$aArgs['id'], 'diffusionList', 'dest', 'user']
Damien's avatar
Damien committed
372
        ]);
373
        $allEntities = EntityModel::getAllEntitiesByUserId(['userId' => $GLOBALS['id']]);
374
375
376
377
        if (!empty($allEntities)) {
            $allEntities = EntityModel::get(['select' => ['id'], 'where' => ['entity_id in (?)'], 'data' => [$allEntities]]);
            $allEntities = array_column($allEntities, 'id');
        }
Damien's avatar
Damien committed
378
        foreach ($listTemplates as $listTemplate) {
379
            if (!in_array($listTemplate['entity_id'], $allEntities)) {
Damien's avatar
Damien committed
380
381
                $isListTemplateDeletable = false;
            }
382
            $listTemplateEntities[] = $listTemplate['entity_id'];
Damien's avatar
Damien committed
383
384
385
386
387
388
        }

        if (!$isListInstanceDeletable || !$isListTemplateDeletable) {
            $formattedLIEntities = [];
            $listInstanceEntities = array_unique($listInstanceEntities);
            foreach ($listInstanceEntities as $listInstanceEntity) {
389
                $entity = EntityModel::getByEntityId(['select' => ['short_label'], 'entityId' => $listInstanceEntity]);
Damien's avatar
Damien committed
390
391
392
393
394
                $formattedLIEntities[] = $entity['short_label'];
            }
            $formattedLTEntities = [];
            $listTemplateEntities = array_unique($listTemplateEntities);
            foreach ($listTemplateEntities as $listTemplateEntity) {
395
                $entity = EntityModel::getById(['select' => ['short_label'], 'id' => $listTemplateEntity]);
Damien's avatar
Damien committed
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
                $formattedLTEntities[] = $entity['short_label'];
            }

            return $response->withJson(['isDeletable' => false, 'listInstanceEntities' => $formattedLIEntities, 'listTemplateEntities' => $formattedLTEntities]);
        }

        $listInstances = [];
        foreach ($listInstanceResIds as $listInstanceResId) {
            $rawListInstances = ListInstanceModel::get([
                'select'    => ['*'],
                'where'     => ['res_id = ?', 'difflist_type = ?'],
                'data'      => [$listInstanceResId, 'entity_id'],
                'orderBy'   => ['listinstance_id']
            ]);
            $listInstances[] = [
                'resId'         => $listInstanceResId,
                'listInstances' => $rawListInstances
            ];
        }

416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
        $rawWorkflowListInstances = ListInstanceModel::get([
            'select'    => ['res_id'],
            'where'     => ['item_id = ?', 'difflist_type = ?', 'process_date is null'],
            'data'      => [$aArgs['id'], 'VISA_CIRCUIT'],
            'groupBy'   => ['res_id']
        ]);
        $workflowListInstances = [];
        foreach ($rawWorkflowListInstances as $rawWorkflowListInstance) {
            $rawListInstances = ListInstanceModel::get([
                'select'    => ['*'],
                'where'     => ['res_id = ?', 'difflist_type = ?'],
                'data'      => [$rawWorkflowListInstance['res_id'], 'VISA_CIRCUIT'],
                'orderBy'   => ['listinstance_id']
            ]);
            $workflowListInstances[] = [
                'resId'         => $rawWorkflowListInstance['res_id'],
                'listInstances' => $rawListInstances
            ];
        }

        return $response->withJson(['isDeletable' => true, 'listTemplates' => $listTemplates, 'listInstances' => $listInstances, 'workflowListInstances' => $workflowListInstances]);
Damien's avatar
Damien committed
437
438
    }

439
    public function suspend(Request $request, Response $response, array $args)
Damien's avatar
Damien committed
440
    {
441
        $error = $this->hasUsersRights(['id' => $args['id'], 'delete' => true, 'himself' => true]);
Damien's avatar
Damien committed
442
443
444
445
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
        }

446
        $user = UserModel::getById(['id' => $args['id'], 'select' => ['firstname', 'lastname', 'user_id', 'mode']]);
447
448
449
        if (in_array($user['mode'], ['root_visible', 'root_invisible']) && !UserController::isRoot(['id' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }
Damien's avatar
Damien committed
450

451
        $listInstances = ListInstanceModel::getWhenOpenMailsByUserId(['select' => [1], 'userId' => $args['id'], 'itemMode' => 'dest']);
Damien's avatar
Damien committed
452
453
454
455
        if (!empty($listInstances)) {
            return $response->withStatus(403)->withJson(['errors' => 'User is still present in listInstances']);
        }

456
        $listTemplates = ListTemplateModel::getWithItems([
Damien's avatar
Damien committed
457
            'select'    => [1],
458
            'where'     => ['item_id = ?', 'type = ?', 'item_mode = ?', 'item_type = ?', 'entity_id is not null'],
459
            'data'      => [$args['id'], 'diffusionList', 'dest', 'user']
Damien's avatar
Damien committed
460
461
462
463
464
465
        ]);
        if (!empty($listTemplates)) {
            return $response->withStatus(403)->withJson(['errors' => 'User is still present in listTemplates']);
        }

        ListInstanceModel::delete([
Damien's avatar
Damien committed
466
            'where' => ['item_id = ?', 'difflist_type = ?', 'item_type = ?', 'item_mode != ?'],
467
            'data'  => [$args['id'], 'entity_id', 'user_id', 'dest']
Damien's avatar
Damien committed
468
469
470
        ]);
        RedirectBasketModel::delete([
            'where' => ['owner_user_id = ? OR actual_user_id = ?'],
471
            'data'  => [$args['id'], $args['id']]
Damien's avatar
Damien committed
472
        ]);
473
474
        ListTemplateItemModel::delete([
            'where' => ['item_id = ?', 'item_type = ?'],
475
            'data'  => [$args['id'], 'user']
476
477
        ]);
        ListTemplateModel::deleteNoItemsOnes();
478
479
480
481
482
483
484

        $contactGroupsToDelete = ContactGroupModel::get(['select' => ['id'], 'where' => ['owner = ?', 'entities = ?'], 'data' => [$args['id'], '{}']]);
        if (!empty($contactGroupsToDelete)) {
            $contactGroupsToDelete = array_column($contactGroupsToDelete, 'id');
            ContactGroupModel::delete(['where' => ['id in (?)'], 'data' => [$contactGroupsToDelete]]);
            ContactGroupListModel::delete(['where' => ['contacts_groups_id in (?)'], 'data' => [$contactGroupsToDelete]]);
        }
485
        ContactGroupListModel::delete(['where' => ['correspondent_id = ?', 'correspondent_type = ?'], 'data' => [$args['id'], 'user']]);
Damien's avatar
Damien committed
486
487
488

        UserModel::update([
            'set'   => [
489
                'status'   => 'SPD'
Damien's avatar
Damien committed
490
491
            ],
            'where' => ['id = ?'],
492
            'data'  => [$args['id']]
Damien's avatar
Damien committed
493
494
495
496
        ]);

        HistoryController::add([
            'tableName'    => 'users',
497
            'recordId'     => $GLOBALS['id'],
Damien's avatar
Damien committed
498
499
500
501
502
503
504
505
            'eventType'    => 'DEL',
            'eventId'      => 'userSuppression',
            'info'         => _USER_SUSPENDED . " {$user['firstname']} {$user['lastname']}"
        ]);

        return $response->withStatus(204);
    }

506
    public function delete(Request $request, Response $response, array $args)
507
    {
508
        $error = $this->hasUsersRights(['id' => $args['id'], 'delete' => true, 'himself' => true]);
509
510
511
512
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
        }

513
        $user = UserModel::getById(['id' => $args['id'], 'select' => ['firstname', 'lastname', 'user_id', 'mode']]);
Damien's avatar
Damien committed
514
515
516
        if (in_array($user['mode'], ['root_visible', 'root_invisible']) && !UserController::isRoot(['id' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }
Damien's avatar
Damien committed
517

518
        $listInstances = ListInstanceModel::getWhenOpenMailsByUserId(['select' => [1], 'userId' => $args['id'], 'itemMode' => 'dest']);
Damien's avatar
Damien committed
519
520
521
522
        if (!empty($listInstances)) {
            return $response->withStatus(403)->withJson(['errors' => 'User is still present in listInstances']);
        }

523
        $listTemplates = ListTemplateModel::getWithItems([
Damien's avatar
Damien committed
524
            'select'    => [1],
525
            'where'     => ['item_id = ?', 'type = ?', 'item_mode = ?', 'item_type = ?', 'entity_id is not null'],
526
            'data'      => [$args['id'], 'diffusionList', 'dest', 'user']
Damien's avatar
Damien committed
527
528
529
530
531
532
        ]);
        if (!empty($listTemplates)) {
            return $response->withStatus(403)->withJson(['errors' => 'User is still present in listTemplates']);
        }

        ListInstanceModel::delete([
Damien's avatar
Damien committed
533
            'where' => ['item_id = ?', 'difflist_type = ?', 'item_type = ?', 'item_mode != ?'],
534
            'data'  => [$args['id'], 'entity_id', 'user_id', 'dest']
Damien's avatar
Damien committed
535
        ]);
536
537
        ListTemplateItemModel::delete([
            'where' => ['item_id = ?', 'item_type = ?'],
538
            'data'  => [$args['id'], 'user']
Damien's avatar
Damien committed
539
        ]);
540
        ListTemplateModel::deleteNoItemsOnes();
541
542
543
544
545
546
547

        $contactGroupsToDelete = ContactGroupModel::get(['select' => ['id'], 'where' => ['owner = ?', 'entities = ?'], 'data' => [$args['id'], '{}']]);
        if (!empty($contactGroupsToDelete)) {
            $contactGroupsToDelete = array_column($contactGroupsToDelete, 'id');
            ContactGroupModel::delete(['where' => ['id in (?)'], 'data' => [$contactGroupsToDelete]]);
            ContactGroupListModel::delete(['where' => ['contacts_groups_id in (?)'], 'data' => [$contactGroupsToDelete]]);
        }
548
        ContactGroupListModel::delete(['where' => ['correspondent_id = ?', 'correspondent_type = ?'], 'data' => [$args['id'], 'user']]);
549

Damien's avatar
Damien committed
550
551
        RedirectBasketModel::delete([
            'where' => ['owner_user_id = ? OR actual_user_id = ?'],
552
            'data'  => [$args['id'], $args['id']]
Damien's avatar
Damien committed
553
        ]);
Damien's avatar
Damien committed
554

555
        // Delete from groups
556
        UserGroupModel::delete(['where' => ['user_id = ?'], 'data' => [$args['id']]]);
557
558
        UserBasketPreferenceModel::delete([
            'where' => ['user_serial_id = ?'],
559
            'data'  => [$args['id']]
560
561
562
        ]);
        RedirectBasketModel::delete([
            'where' => ['owner_user_id = ?'],
563
            'data'  => [$args['id']]
564
565
566
567
        ]);

        UserEntityModel::delete([
            'where' => ['user_id = ?'],
568
            'data'  => [$args['id']]
569
570
        ]);

571
        UserModel::delete(['id' => $args['id']]);
572

Damien's avatar
Damien committed
573
574
        HistoryController::add([
            'tableName'    => 'users',
575
            'recordId'     => $GLOBALS['id'],
576
577
            'eventType'    => 'DEL',
            'eventId'      => 'userSuppression',
Damien's avatar
Damien committed
578
            'info'         => _USER_DELETED . " {$user['firstname']} {$user['lastname']}"
Damien's avatar
Damien committed
579
580
        ]);

Damien's avatar
Damien committed
581
        return $response->withStatus(204);
582
583
    }

584
585
    public function getProfile(Request $request, Response $response)
    {
586
        $user = UserModel::getById(['id' => $GLOBALS['id'], 'select' => ['id', 'user_id', 'firstname', 'lastname', 'phone', 'mail', 'initials', 'preferences', 'external_id', 'status', 'mode', 'feature_tour', 'absence']]);
Damien's avatar
Damien committed
587
        $user['external_id']        = json_decode($user['external_id'], true);
588
        $user['preferences']        = json_decode($user['preferences'], true);
589
        unset($user['preferences']['outlookPassword']);
590
        $user['featureTour']        = json_decode($user['feature_tour'], true);
591
        unset($user['feature_tour']);
Damien's avatar
Damien committed
592
        $user['signatures']         = UserSignatureModel::getByUserSerialId(['userSerialid' => $user['id']]);
593
        $user['groups']             = UserModel::getGroupsById(['id' => $GLOBALS['id']]);
594
        $user['entities']           = UserModel::getEntitiesById(['id' => $GLOBALS['id'], 'select' => ['entities.id', 'users_entities.entity_id', 'entities.entity_label', 'users_entities.user_role', 'users_entities.primary_entity'], 'orderBy' => ['users_entities.primary_entity DESC']]);
595
        $user['baskets']            = BasketModel::getBasketsByLogin(['login' => $user['user_id']]);
Damien's avatar
Damien committed
596
597
598
599
600
        $user['assignedBaskets']    = RedirectBasketModel::getAssignedBasketsByUserId(['userId' => $user['id']]);
        $user['redirectedBaskets']  = RedirectBasketModel::getRedirectedBasketsByUserId(['userId' => $user['id']]);
        $user['regroupedBaskets']   = BasketModel::getRegroupedBasketsByUserId(['userId' => $user['user_id']]);
        $user['passwordRules']      = PasswordModel::getEnabledRules();
        $user['canModifyPassword']  = true;
601
        $user['privileges']         = PrivilegeController::getPrivilegesByUser(['userId' => $user['id']]);
602
        $user['lockAdvancedPrivileges'] = PrivilegeController::isAdvancedPrivilegesLocked();
603
604
        $userFollowed = UserFollowedResourceModel::get(['select' => ['count(1) as nb'], 'where' => ['user_id = ?'], 'data' => [$GLOBALS['id']]]);
        $user['nbFollowedResources'] = $userFollowed[0]['nb'];
605
        $user['absence'] = json_decode($user['absence'], true);
606

607
        $loggingMethod = CoreConfigModel::getLoggingMethod();
608
        if (in_array($loggingMethod['id'], self::ALTERNATIVES_CONNECTIONS_METHODS)) {
609
610
611
            $user['canModifyPassword'] = false;
        }

612
613
614
615
        foreach ($user['baskets'] as $key => $basket) {
            if (!$basket['allowed']) {
                unset($user['baskets'][$key]);
            }
616
            unset($user['baskets'][$key]['basket_clause']);
617
618
        }
        $user['baskets'] = array_values($user['baskets']);
619
620
621
622
623
624
        foreach ($user['groups'] as $key => $group) {
            unset($user['groups'][$key]['where_clause']);
        }
        foreach ($user['assignedBaskets'] as $key => $basket) {
            unset($user['assignedBaskets'][$key]['basket_clause']);
        }
625

626
627
628
629
        return $response->withJson($user);
    }

    public function updateProfile(Request $request, Response $response)
630
    {
631
        $body = $request->getParsedBody();
632

633
634
635
636
637
638
639
640
        if (!Validator::stringType()->notEmpty()->validate($body['firstname'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Body firstname is empty or not a string']);
        } elseif (!Validator::stringType()->notEmpty()->validate($body['lastname'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Body lastname is empty or not a string']);
        } elseif (!Validator::stringType()->notEmpty()->validate($body['mail']) || !filter_var($body['mail'], FILTER_VALIDATE_EMAIL)) {
            return $response->withStatus(400)->withJson(['errors' => 'Body mail is empty or not a valid email']);
        } elseif (!empty($body['phone']) && !preg_match("/\+?((|\ |\.|\(|\)|\-)?(\d)*)*\d/", $body['phone'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Body phone is not a valid phone number']);
641
642
        }

Damien's avatar
Damien committed
643
644
        UserModel::update([
            'set'   => [
645
646
647
648
                'firstname'     => $body['firstname'],
                'lastname'      => $body['lastname'],
                'mail'          => $body['mail'],
                'phone'         => $body['phone'],
649
                'initials'      => $body['initials']
Damien's avatar
Damien committed
650
651
            ],
            'where' => ['id = ?'],
652
            'data'  => [$GLOBALS['id']]
Damien's avatar
Damien committed
653
        ]);
654

655
656
        HistoryController::add([
            'tableName'    => 'users',
657
            'recordId'     => $GLOBALS['id'],
658
659
660
661
662
            'eventType'    => 'UP',
            'eventId'      => 'userModification',
            'info'         => _USER_UPDATED . " {$body['firstname']} {$body['lastname']}"
        ]);

663
        return $response->withStatus(204);
664
665
    }

666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
    public function updateCurrentUserFeatureTour(Request $request, Response $response)
    {
        $body = $request->getParsedBody();

        if (!Validator::notEmpty()->validate($body['featureTour'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Body featureTour is empty']);
        }

        UserModel::update([
            'set'   => [
                'feature_tour' => json_encode($body['featureTour'])
            ],
            'where' => ['id = ?'],
            'data'  => [$GLOBALS['id']]
        ]);

682
        $userData = UserModel::getLabelledUserById(['id' => $GLOBALS['id'], 'select' => ['firstname', 'lastname']]);
683
684
685
686
687
        HistoryController::add([
            'tableName'    => 'users',
            'recordId'     => $GLOBALS['id'],
            'eventType'    => 'UP',
            'eventId'      => 'userModification',
688
            'info'         => _USER_FEATURE_TOUR_UPDATED . " " . $userData
689
690
691
692
693
        ]);

        return $response->withStatus(204);
    }

694
695
696
697
    public function updateCurrentUserPreferences(Request $request, Response $response)
    {
        $body = $request->getParsedBody();

698
        $user = UserModel::getById(['id' => $GLOBALS['id'], 'select' => ['preferences', 'firstname', 'lastname']]);
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
        $preferences = json_decode($user['preferences'], true);

        if (!empty($body['documentEdition'])) {
            if (!in_array($body['documentEdition'], DocumentEditorController::DOCUMENT_EDITION_METHODS)) {
                return $response->withStatus(400)->withJson(['errors' => 'Body preferences[documentEdition] is not allowed']);
            }
            $preferences['documentEdition'] = $body['documentEdition'];
        }
        if (!empty($body['homeGroups'])) {
            $preferences['homeGroups'] = $body['homeGroups'];
        }

        UserModel::update([
            'set'   => [
                'preferences'   => json_encode($preferences)
            ],
            'where' => ['id = ?'],
            'data'  => [$GLOBALS['id']]
        ]);

719
720
        HistoryController::add([
            'tableName'    => 'users',
721
            'recordId'     => $GLOBALS['id'],
722
723
724
725
726
            'eventType'    => 'UP',
            'eventId'      => 'userModification',
            'info'         => _USER_PREFERENCE_UPDATED . " {$user['firstname']} {$user['lastname']}"
        ]);

727
728
729
        return $response->withStatus(204);
    }

Damien's avatar
Damien committed
730
    public function updatePassword(Request $request, Response $response, array $aArgs)
731
    {
Damien's avatar
Damien committed
732
733
734
735
        $error = $this->hasUsersRights(['id' => $aArgs['id'], 'himself' => true]);
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
        }
736

Damien's avatar
Damien committed
737
738
739
740
741
        $body = $request->getParsedBody();

        $check = Validator::stringType()->notEmpty()->validate($body['currentPassword']);
        $check = $check && Validator::stringType()->notEmpty()->validate($body['newPassword']);
        $check = $check && Validator::stringType()->notEmpty()->validate($body['reNewPassword']);
742
743
        if (!$check) {
            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
744
745
        }

746
747
        $user = UserModel::getById(['id' => $aArgs['id'], 'select' => ['user_id', 'mode']]);
        if ($user['mode'] != 'rest' && $user['user_id'] != $GLOBALS['login']) {
Damien's avatar
Damien committed
748
749
            return $response->withStatus(403)->withJson(['errors' => 'Not allowed']);
        }
Damien's avatar
Damien committed
750

Damien's avatar
Damien committed
751
        if ($body['newPassword'] != $body['reNewPassword']) {
Damien's avatar
Damien committed
752
            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
753
        } elseif (!AuthenticationModel::authentication(['login' => $user['user_id'], 'password' => $body['currentPassword']])) {
Damien's avatar
Damien committed
754
            return $response->withStatus(401)->withJson(['errors' => _WRONG_PSW]);
Damien's avatar
Damien committed
755
        } elseif (!PasswordController::isPasswordValid(['password' => $body['newPassword']])) {
Damien's avatar
Damien committed
756
            return $response->withStatus(400)->withJson(['errors' => 'Password does not match security criteria']);
Damien's avatar
Damien committed
757
        } elseif (!PasswordModel::isPasswordHistoryValid(['password' => $body['newPassword'], 'userSerialId' => $aArgs['id']])) {
Damien's avatar
Damien committed
758
            return $response->withStatus(400)->withJson(['errors' => _ALREADY_USED_PSW]);
759
760
        }

Damien's avatar
Damien committed
761
762
        UserModel::updatePassword(['id' => $aArgs['id'], 'password' => $body['newPassword']]);
        PasswordModel::setHistoryPassword(['userSerialId' => $aArgs['id'], 'password' => $body['newPassword']]);
763

764
765
        HistoryController::add([
            'tableName'    => 'users',
766
            'recordId'     => $aArgs['id'],
767
768
769
770
771
            'eventType'    => 'UP',
            'eventId'      => 'userModification',
            'info'         => _USER_PASSWORD_UPDATED
        ]);

Damien's avatar
Damien committed
772
        return $response->withJson(['success' => 'success']);
773
774
    }

775
    public function setRedirectedBaskets(Request $request, Response $response, array $args)
776
    {
777
        $error = $this->hasUsersRights(['id' => $args['id'], 'himself' => true]);
778
779
        if (!empty($error['error'])) {
            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
780
781
        }

782
        $body = $request->getParsedBody();
783

784
785
        $result = UserController::redirectBasket(['redirectedBaskets' => $body, 'userId' => $args['id'], 'login' => $GLOBALS['login']]);
        if (!empty($result['errors'])) {
786
            return $response->withStatus(400)->withJson(['errors' => $result['errors']]);
Damien's avatar
Damien committed
787
788
        }

789
        $user = UserModel::getById(['id' => $args['id'], 'select' => ['user_id']]);
Damien's avatar
Damien committed
790

791
792
        $userBaskets = BasketModel::getBasketsByLogin(['login' => $user['user_id']]);

793
        if ($GLOBALS['login'] == $user['user_id']) {
794
795
796
797
798
799
800
801
            foreach ($userBaskets as $key => $basket) {
                if (!$basket['allowed']) {
                    unset($userBaskets[$key]);
                }
            }
            $userBaskets = array_values($userBaskets);
        }

802
        return $response->withJson([
803
            'redirectedBaskets' => RedirectBasketModel::getRedirectedBasketsByUserId(['userId' => $args['id']]),
804
            'baskets'           => $userBaskets
805
        ]);
806
807
    }

Damien's avatar
Damien committed
808
    public function deleteRedirectedBasket(Request $request, Response $response, array $aArgs)
809