serviceAccount.php 35.1 KB
Newer Older
Prosper De Laure's avatar
Prosper De Laure committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

/*
 * Copyright (C) 2015 Maarch
 *
 * This file is part of bundle auth.
 *
 * Bundle auth is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Bundle auth is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with bundle auth.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace bundle\auth\Controller;

/**
 * serviceAccount  controller
 *
 * @package Auth
 * @author  Alexandre Morin <alexandre.morin@maarch.org>
 */
class serviceAccount
{

    protected $sdoFactory;
34
    protected $csv;
Prosper De Laure's avatar
Prosper De Laure committed
35
36
    protected $passwordEncryption;
    protected $securityPolicy;
37
38
    protected $organizationController;
    protected $servicePositionController;
Alexandre Morin's avatar
Alexandre Morin committed
39
    protected $userAccountController;
40
    protected $hasSecurityLevel;
Prosper De Laure's avatar
Prosper De Laure committed
41
42
43
44
45

    /**
     * Constructor
     * @param array                   $securityPolicy     The array of security policy parameters
     * @param \dependency\sdo\Factory $sdoFactory         The dependency Sdo Factory object
46
     * @param \dependency\csv\Csv     $csv                The dependency Csv
Prosper De Laure's avatar
Prosper De Laure committed
47
48
     * @param string                  $passwordEncryption The password encryption algorythm
     */
49
    public function __construct($securityPolicy, \dependency\sdo\Factory $sdoFactory = null, \dependency\csv\Csv $csv = null, $passwordEncryption = 'md5')
Prosper De Laure's avatar
Prosper De Laure committed
50
51
    {
        $this->sdoFactory = $sdoFactory;
52
        $this->csv = $csv;
Prosper De Laure's avatar
Prosper De Laure committed
53
54
        $this->passwordEncryption = $passwordEncryption;
        $this->securityPolicy = $securityPolicy;
55
56
        $this->organizationController = \laabs::newController('organization/organization');
        $this->servicePositionController = \laabs::newController('organization/servicePosition');
Alexandre Morin's avatar
Alexandre Morin committed
57
        $this->userAccountController = \laabs::newController('auth/userAccount');
58
        $this->hasSecurityLevel = isset(\laabs::configuration('auth')['useSecurityLevel']) ? (bool) \laabs::configuration('auth')['useSecurityLevel'] : false;
Prosper De Laure's avatar
Prosper De Laure committed
59
60
61
62
63
    }

    /**
     * List all services for administration
     *
64
65
     * @param integer $limit Maximal number of results to dispay
     *
Prosper De Laure's avatar
Prosper De Laure committed
66
67
     * @return auth/account[] The array of services
     */
68
    public function index($limit = null)
Prosper De Laure's avatar
Prosper De Laure committed
69
    {
70
71
72
73
        $serviceAccounts = $this->sdoFactory->find('auth/account', "accountType='service'", null, null, null, $limit);
        $serviceAccounts = \laabs::castMessageCollection($serviceAccounts, 'auth/serviceAccountIndex');

        return $serviceAccounts;
Prosper De Laure's avatar
Prosper De Laure committed
74
75
76
77
78
    }

    /**
     * List all service to display
     *
79
80
     * @param string    $accountName    Name of account 
     * @param integer   $maxResults     Max result number to return
81
     * 
82
     * @return array The array of stdClass with dislpay name and service identifier
Prosper De Laure's avatar
Prosper De Laure committed
83
     */
84
    public function search($accountName = null, $maxResults = null)
85
    {
86
        $serviceAccounts = $this->sdoFactory->find('auth/account', $this->getSearchQuery($accountName), null, false, false, $maxResults);
87
88
89
90
        
        return $serviceAccounts;
    }

91
    public function getSearchQuery($accountName = null)
Prosper De Laure's avatar
Prosper De Laure committed
92
    {
93
94
95
        $accountId = \laabs::getToken("AUTH")->accountId;
        $account = $this->sdoFactory->read("auth/account", array("accountId" => $accountId));

96
97
        $userAccountController = \laabs::newController("auth/userAccount");

98
99
100
        $queryAssert = [];
        $queryAssert[] = "accountType='service'";

101
102
103
104
        if (!is_null($accountName) && $accountName != "null") {
            $queryAssert[] = "accountName~'*$accountName*'";
        }

105
106
107
108
109
        if ($this->hasSecurityLevel) {
            switch ($account->getSecurityLevel()) {
                case $account::SECLEVEL_GENADMIN:
                    $queryAssert[] = "(isAdmin='TRUE' AND ownerOrgId!=null)";
                    break;
110

111
                case $account::SECLEVEL_FUNCADMIN:
112
113
114
115
116
117
118
119
120
121
122
123
                    $organization = $this->sdoFactory->read('organization/organization', $account->ownerOrgId);
                    $organizations = $this->organizationController->readDescendantOrg($organization->orgId);
                    $organizations[] = $organization;
                    $organizationsIds = [];
                    foreach ($organizations as $key => $organization) {
                        $organizationsIds[] = (string) $organization->orgId;
                    }

                    $queryAssert[] = "((ownerOrgId= ['" .
                        implode("', '", $organizationsIds) .
                        "']) OR (isAdmin!=TRUE AND ownerOrgId=null))
                        ";
124
                    break;
125

126
127
128
129
                case $account::SECLEVEL_USER:
                    $queryAssert[] = "((isAdmin!='TRUE' AND ownerOrgId='". $account->ownerOrgId."')";
                    break;
            }
130
131
        }

132
133
        return \laabs\implode(" AND ", $queryAssert);
    }
Prosper De Laure's avatar
Prosper De Laure committed
134

135
    public function searchCount($accountName = null)
136
    {
137
        $count = $this->sdoFactory->count('auth/account', $this->getSearchQuery($accountName));
Prosper De Laure's avatar
Prosper De Laure committed
138

139
        return $count;
Prosper De Laure's avatar
Prosper De Laure committed
140
141
142
143
144
145
146
147
148
149
150
    }

    /**
     *  Prepare an empty service object
     *
     * @return auth/account The service object
     */
    public function newService()
    {
        $account = \laabs::newInstance('auth/account');
        $account->accountType = 'service';
151
152
153
154
155
156
157
158
159
160
161
162
        $servicePrivilegesTmp= \laabs::configuration('auth')['servicePrivileges'];

        foreach ($servicePrivilegesTmp as $value) {
            $servicePrivilege = \laabs::newInstance('auth/servicePrivilege');
            $servicePrivilege->serviceURI = $value['serviceURI'];
            $servicePrivilege->description = $value['description'];
            $serviceAccount->servicePrivilegeOptions []  = $servicePrivilege;
        }

        $serviceAccount->servicePrivilege = null;

        return $serviceAccount;
Prosper De Laure's avatar
Prosper De Laure committed
163
164
165
166
167
168
169
170
171
172
    }

    /**
     * Enable a service Account
     * @param string $serviceAccountId The service account identifier
     *
     * @return auth/account The service object
     */
    public function enableService($serviceAccountId)
    {
173
174
        $this->userAccountController->isAuthorized(['gen_admin', 'func_admin']);

Prosper De Laure's avatar
Prosper De Laure committed
175
        $serviceAccount = $this->sdoFactory->read("auth/account", $serviceAccountId);
176
177
178
        $accountToken = \laabs::getToken('AUTH');
        $account = $this->read($accountToken->accountId);

179
180
        if ($this->hasSecurityLevel) {
            $this->checkPrivilegesAccess($account, $serviceAccount);
181
182
        }

Prosper De Laure's avatar
Prosper De Laure committed
183
184
185
186
187
188
189
190
191
192
193
194
195
        $serviceAccount->enabled = true;

        return $this->sdoFactory->update($serviceAccount, "auth/account");
    }

    /**
     * Disabled a service Account
     * @param string $serviceAccountId The service account identifier
     *
     * @return auth/account The service object
     */
    public function disableService($serviceAccountId)
    {
196
197
        $this->userAccountController->isAuthorized(['gen_admin', 'func_admin']);

Prosper De Laure's avatar
Prosper De Laure committed
198
        $serviceAccount = $this->sdoFactory->read("auth/account", $serviceAccountId);
199
200
201
        $accountToken = \laabs::getToken('AUTH');
        $account = $this->read($accountToken->accountId);

202
        if ($this->hasSecurityLevel) {
203
204
205
            if (array_search($serviceAccount->accountName, array_column($this->search(), 'accountName')) === false){
                throw new \core\Exception\UnauthorizedException("You are not allowed to modify this service account");
            }
206
            $this->checkPrivilegesAccess($account, $serviceAccount);
207
208
        }

Prosper De Laure's avatar
Prosper De Laure committed
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
        $serviceAccount->enabled = false;

        return $this->sdoFactory->update($serviceAccount, "auth/account");
    }

    /**
     * Record a new service
     * @param auth/account $serviceAccount The service object
     * @param string       $orgId          The organization identifier
     * @param array        $servicesURI    Array of service URI
     *
     * @return auth/account The service object
     */
    public function addService($serviceAccount, $orgId, $servicesURI = [])
    {
224
        $this->userAccountController->isAuthorized(['gen_admin', 'func_admin']);
225
226
227
228
229
        $organizationController = \laabs::newController("organization/organization");

        $accountToken = \laabs::getToken('AUTH');
        $account = $this->read($accountToken->accountId);

230
        if (isset($orgId) && !empty($orgId)) {
231
232
233
234
235
            try {
                $organization = $organizationController->read($orgId);
            } catch (\Exception $e) {
                throw new \core\Exception\NotFoundException("Organization unit identified by " . $orgId . " does not exist.");
            }
236
237
        }

238
        if ($this->hasSecurityLevel) {
239
            if ($account->getSecurityLevel() == $account::SECLEVEL_FUNCADMIN && array_search($account->ownerOrgId, array_column($this->organizationController->readParentOrg($orgId), 'orgId')) === false){
240
                throw new \core\Exception\ForbiddenException("You are not allowed to add user in this organization");
241
            }
242
            $this->checkPrivilegesAccess($account, $serviceAccount);
Alexandre Morin's avatar
Alexandre Morin committed
243
244
        }

245
        if (!$serviceAccount->ownerOrgId && !empty($orgId)) {
246
            if (!empty($serviceAccount->ownerOrgId) && $serviceAccount->ownerOrgId != $organization->ownerOrgId) {
247
                throw new \core\Exception\NotFoundException("Organization identified by " . $serviceAccount->ownerOrgId . " is not the owner organization of the organization identified by " . $orgId);
248
            }
Alexandre Morin's avatar
Alexandre Morin committed
249
            $serviceAccount->ownerOrgId = $organization->ownerOrgId;
250
251
252
253
254
255
        }

        if ($serviceAccount->ownerOrgId) {
            try {
                $organizationController->read($serviceAccount->ownerOrgId);
            } catch (\Exception $e) {
256
                throw new \core\Exception\NotFoundException("Organization identified by " . $serviceAccount->ownerOrgId . " does not exist.");
257
258
259
            }
        }

Prosper De Laure's avatar
Prosper De Laure committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
        $serviceAccount = \laabs::cast($serviceAccount, 'auth/account');
        $serviceAccount->accountId = \laabs::newId();

        if ($this->sdoFactory->exists('auth/account', array('accountName' => $serviceAccount->accountName))) {
            throw \laabs::newException("auth/serviceAlreadyExistException");
        }

        $transactionControl = !$this->sdoFactory->inTransaction();

        if ($transactionControl) {
            $this->sdoFactory->beginTransaction();
        }

        try {
            $this->sdoFactory->create($serviceAccount, 'auth/account');
            $this->createServicePrivilege($servicesURI, $serviceAccount->accountId);
276
277
278
            if (!$serviceAccount->isAdmin) {
                $this->organizationController->addServicePosition($orgId, $serviceAccount->accountId);
            }
Prosper De Laure's avatar
Prosper De Laure committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
        } catch (\Exception $exception) {
            if ($transactionControl) {
                $this->sdoFactory->rollback();
            }

            throw $exception;
        }

        if ($transactionControl) {
            $this->sdoFactory->commit();
        }

        return $serviceAccount;
    }

    /**
     * Prepare a service object for update
     * @param id $serviceAccountId The service unique identifier
     *
     * @return auth/account The service object
     */
    public function edit($serviceAccountId)
    {
302

Prosper De Laure's avatar
Prosper De Laure committed
303
        $serviceAccount = $this->sdoFactory->read('auth/account', $serviceAccountId);
304
        $servicePosition = $this->servicePositionController->getPosition($serviceAccountId);
305
306
        $servicePrivilegesTmp= \laabs::configuration('auth')['servicePrivileges'];

307
        foreach ($servicePrivilegesTmp as $value) {
308
309
310
311
312
            $servicePrivilege = \laabs::newInstance('auth/servicePrivilege');
            $servicePrivilege->serviceURI = $value['serviceURI'];
            $servicePrivilege->description = $value['description'];
            $serviceAccount->servicePrivilegeOptions []  = $servicePrivilege;
        }
Prosper De Laure's avatar
Prosper De Laure committed
313
314
315
316
317

        if (isset($servicePosition->organization)) {
            $serviceAccount->orgId = $servicePosition->organization->orgId;
        }

318
319
320
321
        $serviceAccount->servicePrivilege = $this->sdoFactory->find(
            'auth/servicePrivilege',
            "accountId='$serviceAccountId'"
        );
Prosper De Laure's avatar
Prosper De Laure committed
322

323
324
        $serviceAccount->securityLevel = $serviceAccount->getSecurityLevel();

Prosper De Laure's avatar
Prosper De Laure committed
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
        return $serviceAccount;
    }

    /**
     * Prepare a service object for update
     * @param id $serviceAccountId The service unique identifier
     *
     * @return auth/account The service object
     */
    public function read($serviceAccountId)
    {
        $serviceAccount = $this->sdoFactory->read('auth/account', $serviceAccountId);

        return $serviceAccount;
    }

    /**
     * Modify serviceAccount information
     * @param auth/accountInformation $serviceAccount The service object
     * @param string                  $orgId          The organization identifier
     * @param array                   $servicesURI    Array of service URI
     *
     * @return boolean The result of the request
     */
    public function updateServiceInformation($serviceAccount, $orgId = null, $servicesURI = [])
    {
351
        $this->userAccountController->isAuthorized(['gen_admin', 'func_admin']);
Alexandre Morin's avatar
Alexandre Morin committed
352

353
354
355
        $organizationController = \laabs::newController("organization/organization");
        $accountToken = \laabs::getToken('AUTH');
        $account = $this->read($accountToken->accountId);
356
357

        if ($account->accountId != $serviceAccount->accountId && $this->hasSecurityLevel) {
Arnaud Pauget's avatar
Arnaud Pauget committed
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
            if ($account->getSecurityLevel() == $account::SECLEVEL_GENADMIN) {
                if (!$serviceAccount->isAdmin) {
                    throw new \core\Exception\UnauthorizedException("You are not allowed to modify this service account");
                }
            }
            else if ($account->getSecurityLevel() == $account::SECLEVEL_FUNCADMIN) {
                $organization = $this->sdoFactory->read('organization/organization', $account->ownerOrgId);
                $organizations = $this->organizationController->readDescendantOrg($organization->orgId);
                $organizations[] = $organization;
                $organizationsIds = [];
                foreach ($organizations as $key => $organization) {
                    $organizationsIds[] = (string) $organization->orgId;
                }
                if ($serviceAccount->isAdmin || !in_array($serviceAccount->ownerOrgId, $organizationsIds)){
                    throw new \core\Exception\UnauthorizedException("You are not allowed to modify this service account");
                }
374
            }
375
            $this->checkPrivilegesAccess($account, $serviceAccount);
Alexandre Morin's avatar
Alexandre Morin committed
376
377
        }

Alexandre Morin's avatar
Alexandre Morin committed
378
        if ($orgId) {
Alexandre Morin's avatar
Alexandre Morin committed
379
380
            $organization = $organizationController->read($orgId);
            $serviceAccount->ownerOrgId = $organization->ownerOrgId;
381
382
383
        }

        $oldServiceAccount = $this->sdoFactory->read('auth/account', $serviceAccount->accountId);
Alexandre Morin's avatar
Alexandre Morin committed
384
        if (($oldServiceAccount->ownerOrgId && $oldServiceAccount->ownerOrgId != $serviceAccount->ownerOrgId)
385
386
387
388
        ) {
            throw new \core\Exception\UnauthorizedException("The owner org id cannot be modified");
        }

Prosper De Laure's avatar
Prosper De Laure committed
389
390
391
392
393
394
395
396
397
398
399
400
        $serviceAccount = \laabs::castMessage($serviceAccount, 'auth/serviceAccount');
        if (!$this->sdoFactory->exists('auth/account', array('accountId' => $serviceAccount->accountId))) {
            throw \laabs::newException("auth/unknownServiceException");
        }

        $transactionControl = !$this->sdoFactory->inTransaction();

        if ($transactionControl) {
            $this->sdoFactory->beginTransaction();
        }

        try {
401
402
403
404
405
            if (!$serviceAccount->isAdmin) {
                if ($orgId) {
                    $servicePosition = $this->servicePositionController->getPosition($serviceAccount->accountId);

                    if (isset($servicePosition->organization)) {
406
                        $this->organizationController->deleteServicePosition($servicePosition->orgId, $serviceAccount->accountId);
407
408
                    }
                    $this->organizationController->addServicePosition($orgId, $serviceAccount->accountId);
Prosper De Laure's avatar
Prosper De Laure committed
409
410
411
412
                }
            }

            $this->sdoFactory->update($serviceAccount, 'auth/account');
413

Prosper De Laure's avatar
Prosper De Laure committed
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
            $this->createServicePrivilege($servicesURI, $serviceAccount->accountId);
        } catch (\Exception $exception) {
            if ($transactionControl) {
                $this->sdoFactory->rollback();
            }

            throw $exception;
        }

        if ($transactionControl) {
            $this->sdoFactory->commit();
        }

        return true;
    }

    /**
     * Generate a service account token
     * @param string $serviceAccountId The service account identifier
     *
434
     * @return object The credential
Prosper De Laure's avatar
Prosper De Laure committed
435
436
437
438
439
440
     */
    public function generateToken($serviceAccountId)
    {
        // Check userAccount exists
        $currentDate = \laabs::newTimestamp();

441
442
443
444
        try {
            $serviceAccount = $this->sdoFactory->read('auth/account', array('accountId' => $serviceAccountId));
        } catch (\Exception $e) {
            throw new \core\Exception\NotFoundException("Account identified by " . $serviceAccountId . " does not exist.");
Prosper De Laure's avatar
Prosper De Laure committed
445
446
        }

447
448
449
        $accountToken = \laabs::getToken('AUTH');
        $ownAccount = $this->read($accountToken->accountId);

450
        if ($accountToken->accountId != $serviceAccountId && $this->hasSecurityLevel) {
451
452
453
454
            $organization = $this->sdoFactory->read('organization/organization', $serviceAccount->ownerOrgId);
            $organizations = $this->organizationController->readDescendantOrg($organization->orgId);
            $organizations[] = $organization;
            if (array_search($serviceAccount->ownerOrgId, array_column($organizations, 'orgId')) === false){
455
                throw new \core\Exception\ForbiddenException("You are not allowed to modify this service account");
456
            }
457
458
459
            $this->checkPrivilegesAccess($ownAccount, $serviceAccount);
        }

Prosper De Laure's avatar
Prosper De Laure committed
460
461
462
        $serviceAccount->salt = md5(microtime());
        $serviceAccount->tokenDate = $currentDate;

Alexis Ragot's avatar
Alexis Ragot committed
463
464
        $dataToken = new \StdClass();
        $dataToken->accountId = $serviceAccount->accountId;
465
        $dataToken->salt = $serviceAccount->salt;
Prosper De Laure's avatar
Prosper De Laure committed
466

Alexis Ragot's avatar
Alexis Ragot committed
467
        $token = new \core\token($dataToken, 0);
Prosper De Laure's avatar
Prosper De Laure committed
468
469

        $jsonToken = \json_encode($token);
Alexis Ragot's avatar
Alexis Ragot committed
470
        $cryptedToken = \laabs::encrypt($jsonToken, \laabs::getCryptKey());
Prosper De Laure's avatar
Prosper De Laure committed
471
472
        $cookieToken = base64_encode($cryptedToken);

Alexis Ragot's avatar
Alexis Ragot committed
473
474
475
476
        $serviceAccount->password = $cookieToken;

        $this->sdoFactory->update($serviceAccount, 'auth/account');

Prosper De Laure's avatar
Prosper De Laure committed
477
478
479
480
481
482
483
        return $cookieToken;
    }

    /**
     * Search service account
     * @param string $query The query
     *
484
     * @return array The list of found service
Prosper De Laure's avatar
Prosper De Laure committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
     */
    public function queryServiceAccounts($query = false)
    {
        $queryTokens = \laabs\explode(" ", $query);
        $queryTokens = array_unique($queryTokens);

        $serviceAccountQueryProperties = array("displayName");
        $serviceAccountQueryPredicats = array();
        foreach ($serviceAccountQueryProperties as $serviceAccountQueryProperty) {
            foreach ($queryTokens as $queryToken) {
                $serviceAccountQueryPredicats[] = $serviceAccountQueryProperty."="."'*".$queryToken."*'";
            }
        }
        $serviceAccountQueryString = implode(" OR ", $serviceAccountQueryPredicats);
        if (!$serviceAccountQueryString) {
            $serviceAccountQueryString = "1=1";
        }
        $serviceAccountQueryString .= "(".$serviceAccountQueryString.") AND accountType='service'";

        $serviceAccounts = $this->sdoFactory->find('auth/accountIndex', $serviceAccountQueryString);

        return $serviceAccounts;
    }

    /**
     * Get the account privileges
     * @param string $serviceAccountId The service account identifier
     *
513
     * @return array The list of privileges
Prosper De Laure's avatar
Prosper De Laure committed
514
515
516
517
518
519
     */
    public function getPrivileges($serviceAccountId)
    {
        return $this->sdoFactory->find("auth/servicePrivilege", "accountId='".$serviceAccountId."'");
    }

520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    /**
     * Search accounts for a privilege
     *
     * @param  string $serviceUri Privilege service uri
     *
     * @return array  $accounts  Array of service accounts with same privilege
     */
    public function getAccountsByPrivilege($serviceUri)
    {
        $queryAssert = null;

        if ($this->hasSecurityLevel) {
            $accountId = \laabs::getToken("AUTH")->accountId;
            $account = $this->sdoFactory->read("auth/account", array("accountId" => $accountId));
            switch ($account->getSecurityLevel()) {
                case $account::SECLEVEL_GENADMIN:
                    $queryAssert = " AND (isAdmin='TRUE' AND ownerOrgId!=null)";
                    break;

                case $account::SECLEVEL_FUNCADMIN:
                    $organization = $this->sdoFactory->read('organization/organization', $account->ownerOrgId);
                    $organizations = $this->organizationController->readDescendantOrg($organization->orgId);
                    $organizations[] = $organization;
                    $organizationsIds = [];
                    foreach ($organizations as $key => $organization) {
                        $organizationsIds[] = (string) $organization->orgId;
                    }

                    $queryAssert = " AND ((ownerOrgId= ['" .
                        implode("', '", $organizationsIds) .
                        "']) OR (isAdmin!=TRUE AND ownerOrgId=null))
                        ";
                    break;

                case $account::SECLEVEL_USER:
                    $queryAssert = " AND ((isAdmin!='TRUE' AND ownerOrgId='". $account->ownerOrgId."')";
                    break;
            }
        }

        $accounts = $this->sdoFactory->index(
            "auth/account",
            ["accountId", "accountName", "displayName"],
            "accountId = [READ auth/servicePrivilege  [accountId] (serviceURI='".$serviceUri."' OR serviceURI = :aster)] . $queryAssert",
            ['aster' => "*"]
        );

        return $accounts;
    }

Prosper De Laure's avatar
Prosper De Laure committed
570
571
572
573
574
    /**
     * create the service privileges
     * @param array  $servicesURI The service privilege
     * @param string $accountId   The service account identifier
     *
575
     * @return bool The result of the operation
Prosper De Laure's avatar
Prosper De Laure committed
576
577
578
579
580
     */
    public function createServicePrivilege(array $servicesURI, $accountId)
    {
        $this->sdoFactory->deleteChildren("auth/servicePrivilege", array("accountId" => $accountId), 'auth/account');

581
        if (!empty($servicesURI)) {
582
583
584
585
586
587
            foreach ($servicesURI as $key => $service) {
                $service = trim($service);
                if (preg_match('/\s/', $service)) {
                    throw new \bundle\auth\Exception\badValueException("The fields contain white spaces.");
                }
            }
Prosper De Laure's avatar
Prosper De Laure committed
588

589
            $servicePrivilege = new \stdClass();
Prosper De Laure's avatar
Prosper De Laure committed
590

591
592
593
594
595
596
            foreach ($servicesURI as $key => $service) {
                $servicePrivilege->serviceURI = trim($service);
                $servicePrivilege->accountId = $accountId;

                $this->sdoFactory->create($servicePrivilege, "auth/servicePrivilege");
            }
Prosper De Laure's avatar
Prosper De Laure committed
597
598
599
600
        }

        return true;
    }
601

602
603
    public function exportCsv($limit = null)
    {
604
        $serviceAccounts = $this->sdoFactory->find('auth/account', "accountType='service'", null, null, null, $limit);
605

606
        $servicePositionController = \laabs::newController('organization/servicePosition');
607
        $organizationController = \laabs::newController('organization/organization');
608
609
        foreach ($serviceAccounts as $key => $serviceAccount) {
            $accountId = $serviceAccount->accountId;
610
            $ownerOrgId = $serviceAccount->ownerOrgId;
611
            $serviceAccount = \laabs::castMessage($serviceAccount, 'auth/serviceAccountImportExport');
612
613
614
615
616
617

            if ($ownerOrgId) {
                $organization = $organizationController->read($ownerOrgId);
                $serviceAccount->ownerOrgRegNumber = $organization->registrationNumber;
            }

618
            $position = $servicePositionController->getPosition($accountId);
619
            if (!empty($position)) {
620
                $serviceAccount->organizations = $position->orgId;
621
            }
622

623
624
625
626
627
628
629
630
631
632
            $privileges = $this->getPrivileges($accountId);
            if (!empty($privileges)) {
                foreach ($privileges as $privilege) {
                    $serviceAccount->privileges .= $privilege->serviceURI;

                    if (end($privileges) !== $privilege) {
                        $serviceAccount->privileges .= ";";
                    }
                }
            }
633
            $serviceAccounts[$key] = $serviceAccount;
634
635
        }

nicolas.diril's avatar
nicolas.diril committed
636
637
638
        $handler = fopen('php://temp', 'w+');
        $this->csv->writeStream($handler, (array) $serviceAccounts, 'auth/serviceAccountImportExport', true);
        return $handler;
639
    }
640
641
642
643

    /**
     * Import Service account function and create or update them
     *
nicolas.diril's avatar
nicolas.diril committed
644
     * @param resource  $data       Array of serviceAccountImportExport Message
645
     * @param boolean   $isReset    Reset tables or not
646
     *
647
     * @return boolean              Success of operation or not
648
649
650
     */
    public function import($data, $isReset = false)
    {
nicolas.diril's avatar
nicolas.diril committed
651
        $services = $this->csv->readStream($data, 'auth/serviceAccountImportExport', $messageType = true);
Jerome Boucher's avatar
Jerome Boucher committed
652
        $organizationController = \laabs::newController('organization/organization');
653
654
655
656
657
658

        $transactionControl = !$this->sdoFactory->inTransaction();

        if ($transactionControl) {
            $this->sdoFactory->beginTransaction();
        }
659
        $isServiceAccountQueryOriginator = false;
660
661

        if ($isReset) {
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
            $accountToken = \laabs::getToken('AUTH');
            $account = $this->read($accountToken->accountId);
            if ($account->accountType == 'service') {
                // Check if service deleting is present in csv
                $sameServicePresent = false;
                foreach ($services as $service) {
                    if ($service->accountName == $account->accountName) {
                        $sameServicePresent = true;
                    }
                }

                if (!$sameServicePresent) {
                    throw new \core\Exception\BadRequestException("Service account resetting must be present in csv");
                }

                $isServiceAccountQueryOriginator = true;
                $deletingService = $this->read((string) $account->accountId);
            }
680
681
682
683
684
685
686
687
688
            try {
                $this->deleteAllServices();
            } catch (\Exception $e) {
                if ($transactionControl) {
                    $this->sdoFactory->rollback();
                }
                throw $e;
            }
        }
Jerome Boucher's avatar
Jerome Boucher committed
689
        $newService = false;
690
691
692
        foreach ($services as $key => $service) {
            if ($isReset) {
                $serviceAccount = $this->newService();
Jerome Boucher's avatar
Jerome Boucher committed
693
                $serviceAccount->accountId = \laabs::newId();
694
695
696
697
698
699
700

                if (isset($isServiceAccountQueryOriginator)
                    && $isServiceAccountQueryOriginator
                    && $service->accountName == $deletingService->accountName
                ) {
                    $serviceAccount->accountId = $deletingService->accountId;
                }
701
702
703
            } else {
                $serviceAccount = $this->sdoFactory->find('auth/account', 'accountName="' . $service->accountName . '" ');
                if (!isset($serviceAccount) || empty($serviceAccount)) {
Jerome Boucher's avatar
Jerome Boucher committed
704
705
706
                    $newService = true;
                    $serviceAccount = $this->newService();
                    $serviceAccount->accountId = \laabs::newId();
707
708
709
710
                } else {
                    $serviceAccount = $serviceAccount[0];
                }
            }
Jerome Boucher's avatar
Jerome Boucher committed
711

712
713
714
715
716
717
718
719
720
721
            if (is_null($service->password) || empty($service->password)) {
                throw new \core\Exception\BadRequestException("Password cannot be null");
            }

            if (!$service->isAdmin
                && (
                    is_null($service->organizations)
                    || empty($service->organizations)
                )
            ) {
722
                throw new \core\Exception\BadRequestException("Service account must be attached to at least one service");
723
724
725
726
            }

            $serviceAccount->accountName = $service->accountName;
            $serviceAccount->displayName = $service->displayName;
Jerome Boucher's avatar
Jerome Boucher committed
727
            $serviceAccount->emailAddress = !is_null($service->emailAddress) ? $service->emailAddress : '';
728
729
730
731
732
            $serviceAccount->password = $service->password;
            $serviceAccount->passwordChangeRequired = true;
            $serviceAccount->locked = $service->locked;
            $serviceAccount->enabled = $service->enabled;
            $serviceAccount->isAdmin = $service->isAdmin;
733
            $serviceAccount->accountType = 'service';
734

735
            if (!is_null($service->ownerOrgRegNumber) && !empty($service->ownerOrgRegNumber)) {
736
737
738
739
740
741
742
                $serviceOwnerOrg = $organizationController->getOrgByRegNumber($service->ownerOrgRegNumber);
                if (!is_null($serviceOwnerOrg) && !empty($serviceOwnerOrg)) {
                    $serviceAccount->ownerOrgId = (string) $serviceOwnerOrg->orgId;
                }
            }

            try {
Jerome Boucher's avatar
Jerome Boucher committed
743
                if ($isReset || $newService) {
744
                    $this->sdoFactory->create($serviceAccount, 'auth/account');
Jerome Boucher's avatar
Jerome Boucher committed
745
                    $newService = false;
746
747
748
749
750
751
                } else {
                    $this->sdoFactory->update($serviceAccount, 'auth/account');
                }

                if (!is_null($service->organizations) && !empty($service->organizations)) {
                    $service->organizations = explode(';', $service->organizations);
Jerome Boucher's avatar
Jerome Boucher committed
752
                    $this->deleteServicePosition((string) $serviceAccount->accountId);
753
                    $this->importServicePositions((array) $service->organizations, (string) $serviceAccount->accountId);
754
755
                }

Jerome Boucher's avatar
Jerome Boucher committed
756
                $this->importServicePrivileges((array) explode(';', $service->privileges), (string) $serviceAccount->accountId);
757
758
759
760
761
762
763
            } catch (\Exception $e) {
                if ($transactionControl) {
                    $this->sdoFactory->rollback();
                }
                throw $e;
            }
        }
Jerome Boucher's avatar
Jerome Boucher committed
764
765
766
767

        if ($transactionControl) {
            $this->sdoFactory->commit();
        }
768
769
    }

Jerome Boucher's avatar
Jerome Boucher committed
770

771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
    private function deleteAllServices()
    {
        $services = $this->index();

        foreach ($services as $key => $service) {
            $this->importDeleteService((string) $service->accountId);
        }
    }

    /**
     * Delete existing user
     *
     * @param auth/account $userAccount The user object unique identifier
     *
     * @return
     */
    public function importDeleteService($serviceAccountId)
    {
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
        // Delete service positions
        $this->deleteServicePosition($serviceAccountId);

        //delete service Privileges
        $this->sdoFactory->deleteChildren("auth/servicePrivilege", array("accountId" => $serviceAccountId), 'auth/account');

        $this->sdoFactory->delete($this->read($serviceAccountId));
    }

    /**
     * Delete service position
     *
     * @param string $serviceAccountId Unique user identifier
     *
     * @return
     */
    private function deleteServicePosition($serviceAccountId)
    {
        $organizationController = \laabs::newController('organization/organization');
808
809
        $servicePositionController = \laabs::newController('organization/servicePosition');
        $organizationSdoFactory = \laabs::dependency('sdo', 'organization')->getService('Factory')->newInstance();
810

Jerome Boucher's avatar
Jerome Boucher committed
811
        $currentServicePositions = $organizationSdoFactory->find('organization/servicePosition', "serviceAccountId = '" . $serviceAccountId . "'");
812

813
814
815
816
817
        if (!empty($currentServicePositions)) {
            foreach ($currentServicePositions as $key => $servicePosition) {
                $organizationSdoFactory->delete($servicePosition, 'organization/servicePosition');
            }
        }
818
    }
819

820
821
822
823
824
825
826
827
828
829
    /**
     * Import array of organizations org reg numbers
     *
     * @param array  $organizations    Array of orgRegNumber
     * @param string $serviceAccountId Unique user identifier
     *
     * @return [type]                [description]
     */
    private function importServicePositions($organizations, $serviceAccountId)
    {
Jerome Boucher's avatar
Jerome Boucher committed
830
        $organizationSdoFactory = \laabs::dependency('sdo', 'organization')->getService('Factory')->newInstance();
831

832
        foreach ($organizations as $key => $orgRegNumber) {
Jerome Boucher's avatar
Jerome Boucher committed
833
834
835
836
            try {
                $organization = $organizationSdoFactory->read("organization/organization", ['registrationNumber' => $orgRegNumber]);
            } catch (\Exception $e) {
                throw new \core\Exception\BadRequestException("Organization '%s' does not exists", 400, null, [$orgRegNumber]);
837
838
839
840
841
842
843
            }

            $servicePosition = \laabs::newInstance('organization/servicePosition');
            $servicePosition->serviceAccountId = $serviceAccountId;
            $servicePosition->orgId = (string) $organization->orgId;
            $organizationSdoFactory->create($servicePosition, 'organization/servicePosition');
        }
844
    }
Jerome Boucher's avatar
Jerome Boucher committed
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860

    /**
     * Import array of organizations org reg numbers
     *
     * @param array  $privileges       Array of privileges
     * @param string $serviceAccountId Unique user identifier
     *
     * @return
     */
    private function importServicePrivileges($privileges, $serviceAccountId)
    {
        $this->sdoFactory->deleteChildren("auth/servicePrivilege", array("accountId" => $serviceAccountId), 'auth/account');
        $existingPrivileges = $existingPrivileges = array_column(\laabs::configuration('auth')['servicePrivileges'], 'serviceURI');

        // Check if privileges exists in conf file
        if (!empty(array_diff($privileges, $existingPrivileges))) {
Jerome Boucher's avatar
Jerome Boucher committed
861
862
            $differences = array_diff($privileges, $existingPrivileges);
            throw new \core\Exception("Privileges %s does not exits", 400, null, $differences);
Jerome Boucher's avatar
Jerome Boucher committed
863
864
865
866
867
868
869
870
871
872
        }

        foreach ($privileges as $key => $privilege) {
            $servicePrivilege = new \stdClass();
            $servicePrivilege->serviceURI = $privilege;
            $servicePrivilege->accountId = $serviceAccountId;

            $this->sdoFactory->create($servicePrivilege, "auth/servicePrivilege");
        }
    }
873
874
875
876

    /**
     * If security level is activated in configuration, check if user has clearance
     *
877
878
     * @param auth/account $ownAccount           account realising request
     * @param auth/account $targetServiceAccount account to exert action to
879
880
881
     *
     * @return
     */
882
    protected function checkPrivilegesAccess($ownAccount, $targetServiceAccount)
883
    {
884
885
        $securityLevel = $ownAccount->getSecurityLevel();
        if ($securityLevel == $ownAccount::SECLEVEL_GENADMIN) {
886
            if (!isset($targetServiceAccount->ownerOrgId) || !$targetServiceAccount->isAdmin) {
887
                throw new \core\Exception\ForbiddenException("Only a Functional administrator can do this action");
888
            }
889
        } elseif ($securityLevel == $ownAccount::SECLEVEL_FUNCADMIN) {
890
            if ($targetServiceAccount->isAdmin) {
891
                throw new \core\Exception\ForbiddenException("Only a General administrator can do this action");
892
            }
893
894
        } elseif ($securityLevel == $ownAccount::SECLEVEL_USER) {
            if ($ownAccount != $targetServiceAccount) {
895
                throw new \core\Exception\ForbiddenException("You are not allowed to do this action");
896
            }
897
898
        }
    }
Prosper De Laure's avatar
Prosper De Laure committed
899
}