1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* File containing the eZ\Publish\Core\Repository\UserService class. |
5
|
|
|
* |
6
|
|
|
* @copyright Copyright (C) eZ Systems AS. All rights reserved. |
7
|
|
|
* @license For full copyright and license information view LICENSE file distributed with this source code. |
8
|
|
|
*/ |
9
|
|
|
namespace eZ\Publish\Core\Repository; |
10
|
|
|
|
11
|
|
|
use eZ\Publish\API\Repository\Values\Content\LocationQuery; |
12
|
|
|
use eZ\Publish\Core\Repository\Values\User\UserCreateStruct; |
13
|
|
|
use eZ\Publish\API\Repository\Values\User\UserCreateStruct as APIUserCreateStruct; |
14
|
|
|
use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; |
15
|
|
|
use eZ\Publish\Core\Repository\Values\User\User; |
16
|
|
|
use eZ\Publish\API\Repository\Values\User\User as APIUser; |
17
|
|
|
use eZ\Publish\Core\Repository\Values\User\UserGroup; |
18
|
|
|
use eZ\Publish\API\Repository\Values\User\UserGroup as APIUserGroup; |
19
|
|
|
use eZ\Publish\Core\Repository\Values\User\UserGroupCreateStruct; |
20
|
|
|
use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct as APIUserGroupCreateStruct; |
21
|
|
|
use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; |
22
|
|
|
use eZ\Publish\API\Repository\Values\Content\Location; |
23
|
|
|
use eZ\Publish\API\Repository\Values\Content\Content as APIContent; |
24
|
|
|
use eZ\Publish\SPI\Persistence\User\Handler; |
25
|
|
|
use eZ\Publish\API\Repository\Repository as RepositoryInterface; |
26
|
|
|
use eZ\Publish\API\Repository\UserService as UserServiceInterface; |
27
|
|
|
use eZ\Publish\SPI\Persistence\User as SPIUser; |
28
|
|
|
use eZ\Publish\Core\FieldType\User\Value as UserValue; |
29
|
|
|
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd; |
30
|
|
|
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId as CriterionContentTypeId; |
31
|
|
|
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId as CriterionLocationId; |
32
|
|
|
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId as CriterionParentLocationId; |
33
|
|
|
use eZ\Publish\Core\Base\Exceptions\ContentValidationException; |
34
|
|
|
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; |
35
|
|
|
use eZ\Publish\Core\Base\Exceptions\BadStateException; |
36
|
|
|
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; |
37
|
|
|
use eZ\Publish\Core\Base\Exceptions\NotFoundException; |
38
|
|
|
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; |
39
|
|
|
use ezcMailTools; |
40
|
|
|
use Exception; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* This service provides methods for managing users and user groups. |
44
|
|
|
* |
45
|
|
|
* @example Examples/user.php |
46
|
|
|
*/ |
47
|
|
|
class UserService implements UserServiceInterface |
48
|
|
|
{ |
49
|
|
|
/** |
50
|
|
|
* @var \eZ\Publish\API\Repository\Repository |
51
|
|
|
*/ |
52
|
|
|
protected $repository; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @var \eZ\Publish\SPI\Persistence\User\Handler |
56
|
|
|
*/ |
57
|
|
|
protected $userHandler; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var array |
61
|
|
|
*/ |
62
|
|
|
protected $settings; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Setups service with reference to repository object that created it & corresponding handler. |
66
|
|
|
* |
67
|
|
|
* @param \eZ\Publish\API\Repository\Repository $repository |
68
|
|
|
* @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler |
69
|
|
|
* @param array $settings |
70
|
|
|
*/ |
71
|
|
|
public function __construct(RepositoryInterface $repository, Handler $userHandler, array $settings = array()) |
72
|
|
|
{ |
73
|
|
|
$this->repository = $repository; |
74
|
|
|
$this->userHandler = $userHandler; |
75
|
|
|
// Union makes sure default settings are ignored if provided in argument |
76
|
|
|
$this->settings = $settings + array( |
77
|
|
|
'defaultUserPlacement' => 12, |
78
|
|
|
'userClassID' => 4, // @todo Rename this settings to swap out "Class" for "Type" |
79
|
|
|
'userGroupClassID' => 3, |
80
|
|
|
'hashType' => APIUser::DEFAULT_PASSWORD_HASH, |
81
|
|
|
'siteName' => 'ez.no', |
82
|
|
|
); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Creates a new user group using the data provided in the ContentCreateStruct parameter. |
87
|
|
|
* |
88
|
|
|
* In 4.x in the content type parameter in the profile is ignored |
89
|
|
|
* - the content type is determined via configuration and can be set to null. |
90
|
|
|
* The returned version is published. |
91
|
|
|
* |
92
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct $userGroupCreateStruct a structure for setting all necessary data to create this user group |
93
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $parentGroup |
94
|
|
|
* |
95
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroup |
96
|
|
|
* |
97
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group |
98
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the input structure has invalid data |
99
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupCreateStruct is not valid |
100
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value |
101
|
|
|
*/ |
102
|
|
|
public function createUserGroup(APIUserGroupCreateStruct $userGroupCreateStruct, APIUserGroup $parentGroup) |
103
|
|
|
{ |
104
|
|
|
$contentService = $this->repository->getContentService(); |
105
|
|
|
$locationService = $this->repository->getLocationService(); |
106
|
|
|
$contentTypeService = $this->repository->getContentTypeService(); |
107
|
|
|
|
108
|
|
|
if ($userGroupCreateStruct->contentType === null) { |
109
|
|
|
$userGroupContentType = $contentTypeService->loadContentType($this->settings['userGroupClassID']); |
110
|
|
|
$userGroupCreateStruct->contentType = $userGroupContentType; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
$loadedParentGroup = $this->loadUserGroup($parentGroup->id); |
114
|
|
|
|
115
|
|
|
if ($loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
116
|
|
|
throw new InvalidArgumentException('parentGroup', 'parent user group has no main location'); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
$locationCreateStruct = $locationService->newLocationCreateStruct( |
120
|
|
|
$loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId |
121
|
|
|
); |
122
|
|
|
|
123
|
|
|
$this->repository->beginTransaction(); |
124
|
|
|
try { |
125
|
|
|
$contentDraft = $contentService->createContent($userGroupCreateStruct, array($locationCreateStruct)); |
126
|
|
|
$publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); |
127
|
|
|
$this->repository->commit(); |
128
|
|
|
} catch (Exception $e) { |
129
|
|
|
$this->repository->rollback(); |
130
|
|
|
throw $e; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
return $this->buildDomainUserGroupObject($publishedContent); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Loads a user group for the given id. |
138
|
|
|
* |
139
|
|
|
* @param mixed $id |
140
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
141
|
|
|
* |
142
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroup |
143
|
|
|
* |
144
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group |
145
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the user group with the given id was not found |
146
|
|
|
*/ |
147
|
|
|
public function loadUserGroup($id, array $prioritizedLanguages = []) |
148
|
|
|
{ |
149
|
|
|
$content = $this->repository->getContentService()->loadContent($id, $prioritizedLanguages); |
150
|
|
|
|
151
|
|
|
return $this->buildDomainUserGroupObject($content); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Loads the sub groups of a user group. |
156
|
|
|
* |
157
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
158
|
|
|
* @param int $offset the start offset for paging |
159
|
|
|
* @param int $limit the number of user groups returned |
160
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
161
|
|
|
* |
162
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroup[] |
163
|
|
|
* |
164
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the user group |
165
|
|
|
*/ |
166
|
|
|
public function loadSubUserGroups(APIUserGroup $userGroup, $offset = 0, $limit = 25, array $prioritizedLanguages = []) |
167
|
|
|
{ |
168
|
|
|
$locationService = $this->repository->getLocationService(); |
169
|
|
|
|
170
|
|
|
$loadedUserGroup = $this->loadUserGroup($userGroup->id); |
171
|
|
|
if (!$this->repository->canUser('content', 'read', $loadedUserGroup)) { |
|
|
|
|
172
|
|
|
throw new UnauthorizedException('content', 'read'); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
176
|
|
|
return array(); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
$mainGroupLocation = $locationService->loadLocation( |
180
|
|
|
$loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId |
181
|
|
|
); |
182
|
|
|
|
183
|
|
|
$searchResult = $this->searchSubGroups($mainGroupLocation, $offset, $limit); |
184
|
|
|
if ($searchResult->totalCount == 0) { |
185
|
|
|
return array(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
$subUserGroups = array(); |
189
|
|
View Code Duplication |
foreach ($searchResult->searchHits as $searchHit) { |
|
|
|
|
190
|
|
|
$subUserGroups[] = $this->buildDomainUserGroupObject( |
191
|
|
|
$this->repository->getContentService()->internalLoadContent( |
|
|
|
|
192
|
|
|
$searchHit->valueObject->contentInfo->id, |
|
|
|
|
193
|
|
|
$prioritizedLanguages |
194
|
|
|
) |
195
|
|
|
); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
return $subUserGroups; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Returns (searches) subgroups of a user group described by its main location. |
203
|
|
|
* |
204
|
|
|
* @param \eZ\Publish\API\Repository\Values\Content\Location $location |
205
|
|
|
* @param int $offset |
206
|
|
|
* @param int $limit |
207
|
|
|
* |
208
|
|
|
* @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult |
209
|
|
|
*/ |
210
|
|
|
protected function searchSubGroups(Location $location, $offset = 0, $limit = 25) |
211
|
|
|
{ |
212
|
|
|
$searchQuery = new LocationQuery(); |
213
|
|
|
|
214
|
|
|
$searchQuery->offset = $offset; |
215
|
|
|
$searchQuery->limit = $limit; |
216
|
|
|
|
217
|
|
|
$searchQuery->filter = new CriterionLogicalAnd([ |
218
|
|
|
new CriterionContentTypeId($this->settings['userGroupClassID']), |
219
|
|
|
new CriterionParentLocationId($location->id), |
220
|
|
|
]); |
221
|
|
|
|
222
|
|
|
$searchQuery->sortClauses = $location->getSortClauses(); |
|
|
|
|
223
|
|
|
|
224
|
|
|
return $this->repository->getSearchService()->findLocations($searchQuery, array(), false); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Removes a user group. |
229
|
|
|
* |
230
|
|
|
* the users which are not assigned to other groups will be deleted. |
231
|
|
|
* |
232
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
233
|
|
|
* |
234
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group |
235
|
|
|
*/ |
236
|
|
View Code Duplication |
public function deleteUserGroup(APIUserGroup $userGroup) |
237
|
|
|
{ |
238
|
|
|
$loadedUserGroup = $this->loadUserGroup($userGroup->id); |
239
|
|
|
|
240
|
|
|
$this->repository->beginTransaction(); |
241
|
|
|
try { |
242
|
|
|
//@todo: what happens to sub user groups and users below sub user groups |
243
|
|
|
$affectedLocationIds = $this->repository->getContentService()->deleteContent($loadedUserGroup->getVersionInfo()->getContentInfo()); |
244
|
|
|
$this->repository->commit(); |
245
|
|
|
} catch (Exception $e) { |
246
|
|
|
$this->repository->rollback(); |
247
|
|
|
throw $e; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
return $affectedLocationIds; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Moves the user group to another parent. |
255
|
|
|
* |
256
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
257
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $newParent |
258
|
|
|
* |
259
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group |
260
|
|
|
*/ |
261
|
|
|
public function moveUserGroup(APIUserGroup $userGroup, APIUserGroup $newParent) |
262
|
|
|
{ |
263
|
|
|
$loadedUserGroup = $this->loadUserGroup($userGroup->id); |
264
|
|
|
$loadedNewParent = $this->loadUserGroup($newParent->id); |
265
|
|
|
|
266
|
|
|
$locationService = $this->repository->getLocationService(); |
267
|
|
|
|
268
|
|
|
if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
269
|
|
|
throw new BadStateException('userGroup', 'existing user group is not stored and/or does not have any location yet'); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
if ($loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
273
|
|
|
throw new BadStateException('newParent', 'new user group is not stored and/or does not have any location yet'); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
$userGroupMainLocation = $locationService->loadLocation( |
277
|
|
|
$loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId |
278
|
|
|
); |
279
|
|
|
$newParentMainLocation = $locationService->loadLocation( |
280
|
|
|
$loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId |
281
|
|
|
); |
282
|
|
|
|
283
|
|
|
$this->repository->beginTransaction(); |
284
|
|
|
try { |
285
|
|
|
$locationService->moveSubtree($userGroupMainLocation, $newParentMainLocation); |
286
|
|
|
$this->repository->commit(); |
287
|
|
|
} catch (Exception $e) { |
288
|
|
|
$this->repository->rollback(); |
289
|
|
|
throw $e; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Updates the group profile with fields and meta data. |
295
|
|
|
* |
296
|
|
|
* 4.x: If the versionUpdateStruct is set in $userGroupUpdateStruct, this method internally creates a content draft, updates ts with the provided data |
297
|
|
|
* and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. |
298
|
|
|
* |
299
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
300
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct $userGroupUpdateStruct |
301
|
|
|
* |
302
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroup |
303
|
|
|
* |
304
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user group |
305
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupUpdateStruct is not valid |
306
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty |
307
|
|
|
*/ |
308
|
|
|
public function updateUserGroup(APIUserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct) |
309
|
|
|
{ |
310
|
|
|
if ($userGroupUpdateStruct->contentUpdateStruct === null && |
311
|
|
|
$userGroupUpdateStruct->contentMetadataUpdateStruct === null) { |
312
|
|
|
// both update structs are empty, nothing to do |
313
|
|
|
return $userGroup; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
$contentService = $this->repository->getContentService(); |
317
|
|
|
|
318
|
|
|
$loadedUserGroup = $this->loadUserGroup($userGroup->id); |
319
|
|
|
|
320
|
|
|
$this->repository->beginTransaction(); |
321
|
|
|
try { |
322
|
|
|
$publishedContent = $loadedUserGroup; |
323
|
|
View Code Duplication |
if ($userGroupUpdateStruct->contentUpdateStruct !== null) { |
|
|
|
|
324
|
|
|
$contentDraft = $contentService->createContentDraft($loadedUserGroup->getVersionInfo()->getContentInfo()); |
325
|
|
|
|
326
|
|
|
$contentDraft = $contentService->updateContent( |
327
|
|
|
$contentDraft->getVersionInfo(), |
328
|
|
|
$userGroupUpdateStruct->contentUpdateStruct |
329
|
|
|
); |
330
|
|
|
|
331
|
|
|
$publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
if ($userGroupUpdateStruct->contentMetadataUpdateStruct !== null) { |
335
|
|
|
$publishedContent = $contentService->updateContentMetadata( |
336
|
|
|
$publishedContent->getVersionInfo()->getContentInfo(), |
337
|
|
|
$userGroupUpdateStruct->contentMetadataUpdateStruct |
338
|
|
|
); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
$this->repository->commit(); |
342
|
|
|
} catch (Exception $e) { |
343
|
|
|
$this->repository->rollback(); |
344
|
|
|
throw $e; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
return $this->buildDomainUserGroupObject($publishedContent); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Create a new user. The created user is published by this method. |
352
|
|
|
* |
353
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserCreateStruct $userCreateStruct the data used for creating the user |
354
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup[] $parentGroups the groups which are assigned to the user after creation |
355
|
|
|
* |
356
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
357
|
|
|
* |
358
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group |
359
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userCreateStruct is not valid |
360
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value |
361
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a user with provided login already exists |
362
|
|
|
*/ |
363
|
|
|
public function createUser(APIUserCreateStruct $userCreateStruct, array $parentGroups) |
364
|
|
|
{ |
365
|
|
|
if (empty($parentGroups)) { |
366
|
|
|
throw new InvalidArgumentValue('parentGroups', $parentGroups); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
if (!is_string($userCreateStruct->login) || empty($userCreateStruct->login)) { |
370
|
|
|
throw new InvalidArgumentValue('login', $userCreateStruct->login, 'UserCreateStruct'); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
View Code Duplication |
if (!is_string($userCreateStruct->email) || empty($userCreateStruct->email)) { |
|
|
|
|
374
|
|
|
throw new InvalidArgumentValue('email', $userCreateStruct->email, 'UserCreateStruct'); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
if (!ezcMailTools::validateEmailAddress($userCreateStruct->email)) { |
|
|
|
|
378
|
|
|
throw new InvalidArgumentValue('email', $userCreateStruct->email, 'UserCreateStruct'); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
if (!is_string($userCreateStruct->password) || empty($userCreateStruct->password)) { |
382
|
|
|
throw new InvalidArgumentValue('password', $userCreateStruct->password, 'UserCreateStruct'); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
if (!is_bool($userCreateStruct->enabled)) { |
386
|
|
|
throw new InvalidArgumentValue('enabled', $userCreateStruct->enabled, 'UserCreateStruct'); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
try { |
390
|
|
|
$this->userHandler->loadByLogin($userCreateStruct->login); |
391
|
|
|
throw new InvalidArgumentException('userCreateStruct', 'User with provided login already exists'); |
392
|
|
|
} catch (NotFoundException $e) { |
393
|
|
|
// Do nothing |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
$contentService = $this->repository->getContentService(); |
397
|
|
|
$locationService = $this->repository->getLocationService(); |
398
|
|
|
$contentTypeService = $this->repository->getContentTypeService(); |
399
|
|
|
|
400
|
|
|
if ($userCreateStruct->contentType === null) { |
401
|
|
|
$userContentType = $contentTypeService->loadContentType($this->settings['userClassID']); |
402
|
|
|
$userCreateStruct->contentType = $userContentType; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
$locationCreateStructs = array(); |
406
|
|
|
foreach ($parentGroups as $parentGroup) { |
407
|
|
|
$parentGroup = $this->loadUserGroup($parentGroup->id); |
408
|
|
|
if ($parentGroup->getVersionInfo()->getContentInfo()->mainLocationId !== null) { |
409
|
|
|
$locationCreateStructs[] = $locationService->newLocationCreateStruct( |
410
|
|
|
$parentGroup->getVersionInfo()->getContentInfo()->mainLocationId |
411
|
|
|
); |
412
|
|
|
} |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
// Search for the first ezuser field type in content type |
416
|
|
|
$userFieldDefinition = null; |
417
|
|
|
foreach ($userCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) { |
418
|
|
|
if ($fieldDefinition->fieldTypeIdentifier == 'ezuser') { |
419
|
|
|
$userFieldDefinition = $fieldDefinition; |
420
|
|
|
break; |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
if ($userFieldDefinition === null) { |
425
|
|
|
throw new ContentValidationException('Provided content type does not contain ezuser field type'); |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
$fixUserFieldType = true; |
429
|
|
|
foreach ($userCreateStruct->fields as $index => $field) { |
430
|
|
|
if ($field->fieldDefIdentifier == $userFieldDefinition->identifier) { |
431
|
|
|
if ($field->value instanceof UserValue) { |
|
|
|
|
432
|
|
|
$userCreateStruct->fields[$index]->value->login = $userCreateStruct->login; |
|
|
|
|
433
|
|
|
} else { |
434
|
|
|
$userCreateStruct->fields[$index]->value = new UserValue( |
|
|
|
|
435
|
|
|
array( |
436
|
|
|
'login' => $userCreateStruct->login, |
437
|
|
|
) |
438
|
|
|
); |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
$fixUserFieldType = false; |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
if ($fixUserFieldType) { |
446
|
|
|
$userCreateStruct->setField( |
447
|
|
|
$userFieldDefinition->identifier, |
448
|
|
|
new UserValue( |
449
|
|
|
array( |
450
|
|
|
'login' => $userCreateStruct->login, |
451
|
|
|
) |
452
|
|
|
) |
453
|
|
|
); |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
$this->repository->beginTransaction(); |
457
|
|
|
try { |
458
|
|
|
$contentDraft = $contentService->createContent($userCreateStruct, $locationCreateStructs); |
459
|
|
|
// Create user before publishing, so that external data can be returned |
460
|
|
|
$spiUser = $this->userHandler->create( |
461
|
|
|
new SPIUser( |
462
|
|
|
array( |
463
|
|
|
'id' => $contentDraft->id, |
464
|
|
|
'login' => $userCreateStruct->login, |
465
|
|
|
'email' => $userCreateStruct->email, |
466
|
|
|
'passwordHash' => $this->createPasswordHash( |
467
|
|
|
$userCreateStruct->login, |
468
|
|
|
$userCreateStruct->password, |
469
|
|
|
$this->settings['siteName'], |
470
|
|
|
$this->settings['hashType'] |
471
|
|
|
), |
472
|
|
|
'hashAlgorithm' => $this->settings['hashType'], |
473
|
|
|
'isEnabled' => $userCreateStruct->enabled, |
474
|
|
|
'maxLogin' => 0, |
475
|
|
|
) |
476
|
|
|
) |
477
|
|
|
); |
478
|
|
|
$publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); |
479
|
|
|
|
480
|
|
|
$this->repository->commit(); |
481
|
|
|
} catch (Exception $e) { |
482
|
|
|
$this->repository->rollback(); |
483
|
|
|
throw $e; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
return $this->buildDomainUserObject($spiUser, $publishedContent); |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/** |
490
|
|
|
* Loads a user. |
491
|
|
|
* |
492
|
|
|
* @param mixed $userId |
493
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
494
|
|
|
* |
495
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
496
|
|
|
* |
497
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given id was not found |
498
|
|
|
*/ |
499
|
|
|
public function loadUser($userId, array $prioritizedLanguages = []) |
500
|
|
|
{ |
501
|
|
|
/** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ |
502
|
|
|
$content = $this->repository->getContentService()->internalLoadContent($userId, $prioritizedLanguages); |
|
|
|
|
503
|
|
|
// Get spiUser value from Field Value |
504
|
|
|
foreach ($content->getFields() as $field) { |
505
|
|
|
if (!$field->value instanceof UserValue) { |
|
|
|
|
506
|
|
|
continue; |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** @var \eZ\Publish\Core\FieldType\User\Value $value */ |
510
|
|
|
$value = $field->value; |
|
|
|
|
511
|
|
|
$spiUser = new SPIUser(); |
512
|
|
|
$spiUser->id = $value->contentId; |
513
|
|
|
$spiUser->login = $value->login; |
514
|
|
|
$spiUser->email = $value->email; |
515
|
|
|
$spiUser->hashAlgorithm = $value->passwordHashType; |
516
|
|
|
$spiUser->passwordHash = $value->passwordHash; |
517
|
|
|
$spiUser->isEnabled = $value->enabled; |
518
|
|
|
$spiUser->maxLogin = $value->maxLogin; |
519
|
|
|
break; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
// If for some reason not found, load it |
523
|
|
|
if (!isset($spiUser)) { |
524
|
|
|
$spiUser = $this->userHandler->load($userId); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
return $this->buildDomainUserObject($spiUser, $content); |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
/** |
531
|
|
|
* Loads anonymous user. |
532
|
|
|
* |
533
|
|
|
* @deprecated since 5.3, use loadUser( $anonymousUserId ) instead |
534
|
|
|
* |
535
|
|
|
* @uses ::loadUser() |
536
|
|
|
* |
537
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
538
|
|
|
*/ |
539
|
|
|
public function loadAnonymousUser() |
540
|
|
|
{ |
541
|
|
|
return $this->loadUser($this->settings['anonymousUserID']); |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
/** |
545
|
|
|
* Loads a user for the given login and password. |
546
|
|
|
* |
547
|
|
|
* If the password hash type differs from that configured for the service, it will be updated to the configured one. |
548
|
|
|
* |
549
|
|
|
* {@inheritdoc} |
550
|
|
|
* |
551
|
|
|
* @param string $login |
552
|
|
|
* @param string $password the plain password |
553
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
554
|
|
|
* |
555
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
556
|
|
|
* |
557
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if credentials are invalid |
558
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found |
559
|
|
|
*/ |
560
|
|
|
public function loadUserByCredentials($login, $password, array $prioritizedLanguages = []) |
561
|
|
|
{ |
562
|
|
|
if (!is_string($login) || empty($login)) { |
563
|
|
|
throw new InvalidArgumentValue('login', $login); |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
if (!is_string($password)) { |
567
|
|
|
throw new InvalidArgumentValue('password', $password); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
$spiUser = $this->userHandler->loadByLogin($login); |
571
|
|
|
if (!$this->verifyPassword($login, $password, $spiUser)) { |
572
|
|
|
throw new NotFoundException('user', $login); |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
$this->updatePasswordHash($login, $password, $spiUser); |
576
|
|
|
|
577
|
|
|
return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Update password hash to the type configured for the service, if they differ. |
582
|
|
|
* |
583
|
|
|
* @param string $login User login |
584
|
|
|
* @param string $password User password |
585
|
|
|
* @param \eZ\Publish\SPI\Persistence\User $spiUser |
586
|
|
|
*/ |
587
|
|
|
private function updatePasswordHash($login, $password, SPIUser $spiUser) |
588
|
|
|
{ |
589
|
|
|
if ($spiUser->hashAlgorithm !== $this->settings['hashType']) { |
590
|
|
|
$spiUser->passwordHash = $this->createPasswordHash($login, $password, null, $this->settings['hashType']); |
|
|
|
|
591
|
|
|
$spiUser->hashAlgorithm = $this->settings['hashType']; |
592
|
|
|
$this->userHandler->update($spiUser); |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
/** |
597
|
|
|
* Loads a user for the given login. |
598
|
|
|
* |
599
|
|
|
* {@inheritdoc} |
600
|
|
|
* |
601
|
|
|
* @param string $login |
602
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
603
|
|
|
* |
604
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
605
|
|
|
* |
606
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found |
607
|
|
|
*/ |
608
|
|
|
public function loadUserByLogin($login, array $prioritizedLanguages = []) |
609
|
|
|
{ |
610
|
|
|
if (!is_string($login) || empty($login)) { |
611
|
|
|
throw new InvalidArgumentValue('login', $login); |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
$spiUser = $this->userHandler->loadByLogin($login); |
615
|
|
|
|
616
|
|
|
return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
/** |
620
|
|
|
* Loads a user for the given email. |
621
|
|
|
* |
622
|
|
|
* {@inheritdoc} |
623
|
|
|
* |
624
|
|
|
* @param string $email |
625
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
626
|
|
|
* |
627
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User[] |
628
|
|
|
*/ |
629
|
|
|
public function loadUsersByEmail($email, array $prioritizedLanguages = []) |
630
|
|
|
{ |
631
|
|
|
if (!is_string($email) || empty($email)) { |
632
|
|
|
throw new InvalidArgumentValue('email', $email); |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
$users = array(); |
636
|
|
|
foreach ($this->userHandler->loadByEmail($email) as $spiUser) { |
637
|
|
|
$users[] = $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
return $users; |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
/** |
644
|
|
|
* This method deletes a user. |
645
|
|
|
* |
646
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\User $user |
647
|
|
|
* |
648
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete the user |
649
|
|
|
*/ |
650
|
|
View Code Duplication |
public function deleteUser(APIUser $user) |
651
|
|
|
{ |
652
|
|
|
$loadedUser = $this->loadUser($user->id); |
653
|
|
|
|
654
|
|
|
$this->repository->beginTransaction(); |
655
|
|
|
try { |
656
|
|
|
$affectedLocationIds = $this->repository->getContentService()->deleteContent($loadedUser->getVersionInfo()->getContentInfo()); |
657
|
|
|
$this->userHandler->delete($loadedUser->id); |
658
|
|
|
$this->repository->commit(); |
659
|
|
|
} catch (Exception $e) { |
660
|
|
|
$this->repository->rollback(); |
661
|
|
|
throw $e; |
662
|
|
|
} |
663
|
|
|
|
664
|
|
|
return $affectedLocationIds; |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
/** |
668
|
|
|
* Updates a user. |
669
|
|
|
* |
670
|
|
|
* 4.x: If the versionUpdateStruct is set in the user update structure, this method internally creates a content draft, updates ts with the provided data |
671
|
|
|
* and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. |
672
|
|
|
* |
673
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\User $user |
674
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserUpdateStruct $userUpdateStruct |
675
|
|
|
* |
676
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user |
677
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userUpdateStruct is not valid |
678
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty |
679
|
|
|
* |
680
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
681
|
|
|
*/ |
682
|
|
|
public function updateUser(APIUser $user, UserUpdateStruct $userUpdateStruct) |
683
|
|
|
{ |
684
|
|
|
$loadedUser = $this->loadUser($user->id); |
685
|
|
|
|
686
|
|
|
// We need to determine if we have anything to update. |
687
|
|
|
// UserUpdateStruct is specific as some of the new content is in |
688
|
|
|
// content update struct and some of it is in additional fields like |
689
|
|
|
// email, password and so on |
690
|
|
|
$doUpdate = false; |
691
|
|
|
foreach ($userUpdateStruct as $propertyValue) { |
|
|
|
|
692
|
|
|
if ($propertyValue !== null) { |
693
|
|
|
$doUpdate = true; |
694
|
|
|
break; |
695
|
|
|
} |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
if (!$doUpdate) { |
699
|
|
|
// Nothing to update, so we just quit |
700
|
|
|
return $user; |
701
|
|
|
} |
702
|
|
|
|
703
|
|
|
if ($userUpdateStruct->email !== null) { |
704
|
|
View Code Duplication |
if (!is_string($userUpdateStruct->email) || empty($userUpdateStruct->email)) { |
|
|
|
|
705
|
|
|
throw new InvalidArgumentValue('email', $userUpdateStruct->email, 'UserUpdateStruct'); |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
if (!ezcMailTools::validateEmailAddress($userUpdateStruct->email)) { |
|
|
|
|
709
|
|
|
throw new InvalidArgumentValue('email', $userUpdateStruct->email, 'UserUpdateStruct'); |
710
|
|
|
} |
711
|
|
|
} |
712
|
|
|
|
713
|
|
View Code Duplication |
if ($userUpdateStruct->password !== null && (!is_string($userUpdateStruct->password) || empty($userUpdateStruct->password))) { |
|
|
|
|
714
|
|
|
throw new InvalidArgumentValue('password', $userUpdateStruct->password, 'UserUpdateStruct'); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
if ($userUpdateStruct->enabled !== null && !is_bool($userUpdateStruct->enabled)) { |
718
|
|
|
throw new InvalidArgumentValue('enabled', $userUpdateStruct->enabled, 'UserUpdateStruct'); |
719
|
|
|
} |
720
|
|
|
|
721
|
|
|
if ($userUpdateStruct->maxLogin !== null && !is_int($userUpdateStruct->maxLogin)) { |
722
|
|
|
throw new InvalidArgumentValue('maxLogin', $userUpdateStruct->maxLogin, 'UserUpdateStruct'); |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
$contentService = $this->repository->getContentService(); |
726
|
|
|
|
727
|
|
|
if (!$this->repository->canUser('content', 'edit', $loadedUser)) { |
|
|
|
|
728
|
|
|
throw new UnauthorizedException('content', 'edit'); |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
$this->repository->beginTransaction(); |
732
|
|
|
try { |
733
|
|
|
$publishedContent = $loadedUser; |
734
|
|
View Code Duplication |
if ($userUpdateStruct->contentUpdateStruct !== null) { |
|
|
|
|
735
|
|
|
$contentDraft = $contentService->createContentDraft($loadedUser->getVersionInfo()->getContentInfo()); |
736
|
|
|
$contentDraft = $contentService->updateContent( |
737
|
|
|
$contentDraft->getVersionInfo(), |
738
|
|
|
$userUpdateStruct->contentUpdateStruct |
739
|
|
|
); |
740
|
|
|
$publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); |
741
|
|
|
} |
742
|
|
|
|
743
|
|
|
if ($userUpdateStruct->contentMetadataUpdateStruct !== null) { |
744
|
|
|
$contentService->updateContentMetadata( |
745
|
|
|
$publishedContent->getVersionInfo()->getContentInfo(), |
746
|
|
|
$userUpdateStruct->contentMetadataUpdateStruct |
747
|
|
|
); |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
$this->userHandler->update( |
751
|
|
|
new SPIUser( |
752
|
|
|
array( |
753
|
|
|
'id' => $loadedUser->id, |
754
|
|
|
'login' => $loadedUser->login, |
|
|
|
|
755
|
|
|
'email' => $userUpdateStruct->email ?: $loadedUser->email, |
|
|
|
|
756
|
|
|
'passwordHash' => $userUpdateStruct->password ? |
757
|
|
|
$this->createPasswordHash( |
758
|
|
|
$loadedUser->login, |
|
|
|
|
759
|
|
|
$userUpdateStruct->password, |
760
|
|
|
$this->settings['siteName'], |
761
|
|
|
$this->settings['hashType'] |
762
|
|
|
) : |
763
|
|
|
$loadedUser->passwordHash, |
|
|
|
|
764
|
|
|
'hashAlgorithm' => $userUpdateStruct->password ? |
765
|
|
|
$this->settings['hashType'] : |
766
|
|
|
$loadedUser->hashAlgorithm, |
|
|
|
|
767
|
|
|
'isEnabled' => $userUpdateStruct->enabled !== null ? $userUpdateStruct->enabled : $loadedUser->enabled, |
|
|
|
|
768
|
|
|
'maxLogin' => $userUpdateStruct->maxLogin !== null ? (int)$userUpdateStruct->maxLogin : $loadedUser->maxLogin, |
|
|
|
|
769
|
|
|
) |
770
|
|
|
) |
771
|
|
|
); |
772
|
|
|
|
773
|
|
|
$this->repository->commit(); |
774
|
|
|
} catch (Exception $e) { |
775
|
|
|
$this->repository->rollback(); |
776
|
|
|
throw $e; |
777
|
|
|
} |
778
|
|
|
|
779
|
|
|
return $this->loadUser($loadedUser->id); |
780
|
|
|
} |
781
|
|
|
|
782
|
|
|
/** |
783
|
|
|
* Assigns a new user group to the user. |
784
|
|
|
* |
785
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\User $user |
786
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
787
|
|
|
* |
788
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign the user group to the user |
789
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is already in the given user group |
790
|
|
|
*/ |
791
|
|
|
public function assignUserToUserGroup(APIUser $user, APIUserGroup $userGroup) |
792
|
|
|
{ |
793
|
|
|
$loadedUser = $this->loadUser($user->id); |
794
|
|
|
$loadedGroup = $this->loadUserGroup($userGroup->id); |
795
|
|
|
$locationService = $this->repository->getLocationService(); |
796
|
|
|
|
797
|
|
|
$existingGroupIds = array(); |
798
|
|
|
$userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo()); |
799
|
|
|
foreach ($userLocations as $userLocation) { |
800
|
|
|
$existingGroupIds[] = $userLocation->parentLocationId; |
801
|
|
|
} |
802
|
|
|
|
803
|
|
|
if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
804
|
|
|
throw new BadStateException('userGroup', 'user group has no main location or no locations'); |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
if (in_array($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId, $existingGroupIds)) { |
808
|
|
|
// user is already assigned to the user group |
809
|
|
|
throw new InvalidArgumentException('user', 'user is already in the given user group'); |
810
|
|
|
} |
811
|
|
|
|
812
|
|
|
$locationCreateStruct = $locationService->newLocationCreateStruct( |
813
|
|
|
$loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId |
814
|
|
|
); |
815
|
|
|
|
816
|
|
|
$this->repository->beginTransaction(); |
817
|
|
|
try { |
818
|
|
|
$locationService->createLocation( |
819
|
|
|
$loadedUser->getVersionInfo()->getContentInfo(), |
820
|
|
|
$locationCreateStruct |
821
|
|
|
); |
822
|
|
|
$this->repository->commit(); |
823
|
|
|
} catch (Exception $e) { |
824
|
|
|
$this->repository->rollback(); |
825
|
|
|
throw $e; |
826
|
|
|
} |
827
|
|
|
} |
828
|
|
|
|
829
|
|
|
/** |
830
|
|
|
* Removes a user group from the user. |
831
|
|
|
* |
832
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\User $user |
833
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
834
|
|
|
* |
835
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove the user group from the user |
836
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is not in the given user group |
837
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $userGroup is the last assigned user group |
838
|
|
|
*/ |
839
|
|
|
public function unAssignUserFromUserGroup(APIUser $user, APIUserGroup $userGroup) |
840
|
|
|
{ |
841
|
|
|
$loadedUser = $this->loadUser($user->id); |
842
|
|
|
$loadedGroup = $this->loadUserGroup($userGroup->id); |
843
|
|
|
$locationService = $this->repository->getLocationService(); |
844
|
|
|
|
845
|
|
|
$userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo()); |
846
|
|
|
if (empty($userLocations)) { |
847
|
|
|
throw new BadStateException('user', 'user has no locations, cannot unassign from group'); |
848
|
|
|
} |
849
|
|
|
|
850
|
|
|
if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
851
|
|
|
throw new BadStateException('userGroup', 'user group has no main location or no locations, cannot unassign'); |
852
|
|
|
} |
853
|
|
|
|
854
|
|
|
foreach ($userLocations as $userLocation) { |
855
|
|
|
if ($userLocation->parentLocationId == $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId) { |
856
|
|
|
// Throw this specific BadState when we know argument is valid |
857
|
|
|
if (count($userLocations) === 1) { |
858
|
|
|
throw new BadStateException('user', 'user only has one user group, cannot unassign from last group'); |
859
|
|
|
} |
860
|
|
|
|
861
|
|
|
$this->repository->beginTransaction(); |
862
|
|
|
try { |
863
|
|
|
$locationService->deleteLocation($userLocation); |
864
|
|
|
$this->repository->commit(); |
865
|
|
|
|
866
|
|
|
return; |
867
|
|
|
} catch (Exception $e) { |
868
|
|
|
$this->repository->rollback(); |
869
|
|
|
throw $e; |
870
|
|
|
} |
871
|
|
|
} |
872
|
|
|
} |
873
|
|
|
|
874
|
|
|
throw new InvalidArgumentException('userGroup', 'user is not in the given user group'); |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
/** |
878
|
|
|
* Loads the user groups the user belongs to. |
879
|
|
|
* |
880
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed read the user or user group |
881
|
|
|
* |
882
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\User $user |
883
|
|
|
* @param int $offset the start offset for paging |
884
|
|
|
* @param int $limit the number of user groups returned |
885
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
886
|
|
|
* |
887
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroup[] |
888
|
|
|
*/ |
889
|
|
|
public function loadUserGroupsOfUser(APIUser $user, $offset = 0, $limit = 25, array $prioritizedLanguages = []) |
890
|
|
|
{ |
891
|
|
|
$locationService = $this->repository->getLocationService(); |
892
|
|
|
|
893
|
|
|
if (!$this->repository->getPermissionResolver()->canUser('content', 'read', $user)) { |
894
|
|
|
throw new UnauthorizedException('content', 'read'); |
895
|
|
|
} |
896
|
|
|
|
897
|
|
|
$userLocations = $locationService->loadLocations( |
898
|
|
|
$user->getVersionInfo()->getContentInfo() |
899
|
|
|
); |
900
|
|
|
|
901
|
|
|
$parentLocationIds = array(); |
902
|
|
|
foreach ($userLocations as $userLocation) { |
903
|
|
|
if ($userLocation->parentLocationId !== null) { |
904
|
|
|
$parentLocationIds[] = $userLocation->parentLocationId; |
905
|
|
|
} |
906
|
|
|
} |
907
|
|
|
|
908
|
|
|
$searchQuery = new LocationQuery(); |
909
|
|
|
|
910
|
|
|
$searchQuery->offset = $offset; |
911
|
|
|
$searchQuery->limit = $limit; |
912
|
|
|
$searchQuery->performCount = false; |
913
|
|
|
|
914
|
|
|
$searchQuery->filter = new CriterionLogicalAnd( |
915
|
|
|
[ |
916
|
|
|
new CriterionContentTypeId($this->settings['userGroupClassID']), |
917
|
|
|
new CriterionLocationId($parentLocationIds), |
918
|
|
|
] |
919
|
|
|
); |
920
|
|
|
|
921
|
|
|
$searchResult = $this->repository->getSearchService()->findLocations($searchQuery); |
922
|
|
|
|
923
|
|
|
$userGroups = []; |
924
|
|
View Code Duplication |
foreach ($searchResult->searchHits as $resultItem) { |
|
|
|
|
925
|
|
|
$userGroups[] = $this->buildDomainUserGroupObject( |
926
|
|
|
$this->repository->getContentService()->internalLoadContent( |
|
|
|
|
927
|
|
|
$resultItem->valueObject->contentInfo->id, |
|
|
|
|
928
|
|
|
$prioritizedLanguages |
929
|
|
|
) |
930
|
|
|
); |
931
|
|
|
} |
932
|
|
|
|
933
|
|
|
return $userGroups; |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
/** |
937
|
|
|
* Loads the users of a user group. |
938
|
|
|
* |
939
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the users or user group |
940
|
|
|
* |
941
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup |
942
|
|
|
* @param int $offset the start offset for paging |
943
|
|
|
* @param int $limit the number of users returned |
944
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
945
|
|
|
* |
946
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User[] |
947
|
|
|
*/ |
948
|
|
|
public function loadUsersOfUserGroup( |
949
|
|
|
APIUserGroup $userGroup, |
950
|
|
|
$offset = 0, |
951
|
|
|
$limit = 25, |
952
|
|
|
array $prioritizedLanguages = [] |
953
|
|
|
) { |
954
|
|
|
$loadedUserGroup = $this->loadUserGroup($userGroup->id); |
955
|
|
|
|
956
|
|
|
if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { |
957
|
|
|
return []; |
958
|
|
|
} |
959
|
|
|
|
960
|
|
|
$mainGroupLocation = $this->repository->getLocationService()->loadLocation( |
961
|
|
|
$loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId |
962
|
|
|
); |
963
|
|
|
|
964
|
|
|
$searchQuery = new LocationQuery(); |
965
|
|
|
|
966
|
|
|
$searchQuery->filter = new CriterionLogicalAnd( |
967
|
|
|
[ |
968
|
|
|
new CriterionContentTypeId($this->settings['userClassID']), |
969
|
|
|
new CriterionParentLocationId($mainGroupLocation->id), |
970
|
|
|
] |
971
|
|
|
); |
972
|
|
|
|
973
|
|
|
$searchQuery->offset = $offset; |
974
|
|
|
$searchQuery->limit = $limit; |
975
|
|
|
$searchQuery->performCount = false; |
976
|
|
|
$searchQuery->sortClauses = $mainGroupLocation->getSortClauses(); |
|
|
|
|
977
|
|
|
|
978
|
|
|
$searchResult = $this->repository->getSearchService()->findLocations($searchQuery); |
979
|
|
|
|
980
|
|
|
$users = []; |
981
|
|
|
foreach ($searchResult->searchHits as $resultItem) { |
982
|
|
|
$users[] = $this->buildDomainUserObject( |
983
|
|
|
$this->userHandler->load($resultItem->valueObject->contentInfo->id), |
|
|
|
|
984
|
|
|
$this->repository->getContentService()->internalLoadContent( |
|
|
|
|
985
|
|
|
$resultItem->valueObject->contentInfo->id, |
|
|
|
|
986
|
|
|
$prioritizedLanguages |
987
|
|
|
) |
988
|
|
|
); |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
return $users; |
992
|
|
|
} |
993
|
|
|
|
994
|
|
|
/** |
995
|
|
|
* Instantiate a user create class. |
996
|
|
|
* |
997
|
|
|
* @param string $login the login of the new user |
998
|
|
|
* @param string $email the email of the new user |
999
|
|
|
* @param string $password the plain password of the new user |
1000
|
|
|
* @param string $mainLanguageCode the main language for the underlying content object |
1001
|
|
|
* @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration |
1002
|
|
|
* |
1003
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserCreateStruct |
1004
|
|
|
*/ |
1005
|
|
|
public function newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType = null) |
1006
|
|
|
{ |
1007
|
|
|
if ($contentType === null) { |
1008
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType( |
1009
|
|
|
$this->settings['userClassID'] |
1010
|
|
|
); |
1011
|
|
|
} |
1012
|
|
|
|
1013
|
|
|
return new UserCreateStruct( |
1014
|
|
|
array( |
1015
|
|
|
'contentType' => $contentType, |
1016
|
|
|
'mainLanguageCode' => $mainLanguageCode, |
1017
|
|
|
'login' => $login, |
1018
|
|
|
'email' => $email, |
1019
|
|
|
'password' => $password, |
1020
|
|
|
'enabled' => true, |
1021
|
|
|
'fields' => array(), |
1022
|
|
|
) |
1023
|
|
|
); |
1024
|
|
|
} |
1025
|
|
|
|
1026
|
|
|
/** |
1027
|
|
|
* Instantiate a user group create class. |
1028
|
|
|
* |
1029
|
|
|
* @param string $mainLanguageCode The main language for the underlying content object |
1030
|
|
|
* @param null|\eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration |
1031
|
|
|
* |
1032
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct |
1033
|
|
|
*/ |
1034
|
|
|
public function newUserGroupCreateStruct($mainLanguageCode, $contentType = null) |
1035
|
|
|
{ |
1036
|
|
|
if ($contentType === null) { |
1037
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType( |
1038
|
|
|
$this->settings['userGroupClassID'] |
1039
|
|
|
); |
1040
|
|
|
} |
1041
|
|
|
|
1042
|
|
|
return new UserGroupCreateStruct( |
1043
|
|
|
array( |
1044
|
|
|
'contentType' => $contentType, |
1045
|
|
|
'mainLanguageCode' => $mainLanguageCode, |
1046
|
|
|
'fields' => array(), |
1047
|
|
|
) |
1048
|
|
|
); |
1049
|
|
|
} |
1050
|
|
|
|
1051
|
|
|
/** |
1052
|
|
|
* Instantiate a new user update struct. |
1053
|
|
|
* |
1054
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserUpdateStruct |
1055
|
|
|
*/ |
1056
|
|
|
public function newUserUpdateStruct() |
1057
|
|
|
{ |
1058
|
|
|
return new UserUpdateStruct(); |
1059
|
|
|
} |
1060
|
|
|
|
1061
|
|
|
/** |
1062
|
|
|
* Instantiate a new user group update struct. |
1063
|
|
|
* |
1064
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct |
1065
|
|
|
*/ |
1066
|
|
|
public function newUserGroupUpdateStruct() |
1067
|
|
|
{ |
1068
|
|
|
return new UserGroupUpdateStruct(); |
1069
|
|
|
} |
1070
|
|
|
|
1071
|
|
|
/** |
1072
|
|
|
* Builds the domain UserGroup object from provided Content object. |
1073
|
|
|
* |
1074
|
|
|
* @param \eZ\Publish\API\Repository\Values\Content\Content $content |
1075
|
|
|
* |
1076
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\UserGroup |
1077
|
|
|
*/ |
1078
|
|
|
protected function buildDomainUserGroupObject(APIContent $content) |
1079
|
|
|
{ |
1080
|
|
|
$locationService = $this->repository->getLocationService(); |
1081
|
|
|
|
1082
|
|
|
$subGroupCount = 0; |
1083
|
|
|
if ($content->getVersionInfo()->getContentInfo()->mainLocationId !== null) { |
1084
|
|
|
$mainLocation = $locationService->loadLocation( |
1085
|
|
|
$content->getVersionInfo()->getContentInfo()->mainLocationId |
1086
|
|
|
); |
1087
|
|
|
$parentLocation = $locationService->loadLocation($mainLocation->parentLocationId); |
1088
|
|
|
$subGroups = $this->searchSubGroups($mainLocation, 0, 0); |
1089
|
|
|
$subGroupCount = $subGroups->totalCount; |
1090
|
|
|
} |
1091
|
|
|
|
1092
|
|
|
return new UserGroup( |
1093
|
|
|
array( |
1094
|
|
|
'content' => $content, |
1095
|
|
|
'parentId' => isset($parentLocation) ? $parentLocation->contentId : null, |
1096
|
|
|
'subGroupCount' => $subGroupCount, |
1097
|
|
|
) |
1098
|
|
|
); |
1099
|
|
|
} |
1100
|
|
|
|
1101
|
|
|
/** |
1102
|
|
|
* Builds the domain user object from provided persistence user object. |
1103
|
|
|
* |
1104
|
|
|
* @param \eZ\Publish\SPI\Persistence\User $spiUser |
1105
|
|
|
* @param \eZ\Publish\API\Repository\Values\Content\Content|null $content |
1106
|
|
|
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. |
1107
|
|
|
* |
1108
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
1109
|
|
|
*/ |
1110
|
|
|
protected function buildDomainUserObject( |
1111
|
|
|
SPIUser $spiUser, |
1112
|
|
|
APIContent $content = null, |
1113
|
|
|
array $prioritizedLanguages = [] |
1114
|
|
|
) { |
1115
|
|
|
if ($content === null) { |
1116
|
|
|
$content = $this->repository->getContentService()->internalLoadContent( |
|
|
|
|
1117
|
|
|
$spiUser->id, |
1118
|
|
|
$prioritizedLanguages |
1119
|
|
|
); |
1120
|
|
|
} |
1121
|
|
|
|
1122
|
|
|
return new User( |
1123
|
|
|
array( |
1124
|
|
|
'content' => $content, |
1125
|
|
|
'login' => $spiUser->login, |
1126
|
|
|
'email' => $spiUser->email, |
1127
|
|
|
'passwordHash' => $spiUser->passwordHash, |
1128
|
|
|
'hashAlgorithm' => (int)$spiUser->hashAlgorithm, |
1129
|
|
|
'enabled' => $spiUser->isEnabled, |
1130
|
|
|
'maxLogin' => (int)$spiUser->maxLogin, |
1131
|
|
|
) |
1132
|
|
|
); |
1133
|
|
|
} |
1134
|
|
|
|
1135
|
|
|
/** |
1136
|
|
|
* Verifies if the provided login and password are valid. |
1137
|
|
|
* |
1138
|
|
|
* @param string $login User login |
1139
|
|
|
* @param string $password User password |
1140
|
|
|
* @param \eZ\Publish\SPI\Persistence\User $spiUser Loaded user handler |
1141
|
|
|
* |
1142
|
|
|
* @return bool return true if the login and password are sucessfully |
1143
|
|
|
* validate and false, if not. |
1144
|
|
|
*/ |
1145
|
|
|
protected function verifyPassword($login, $password, $spiUser) |
1146
|
|
|
{ |
1147
|
|
|
// In case of bcrypt let php's password functionality do it's magic |
1148
|
|
|
if ($spiUser->hashAlgorithm === APIUser::PASSWORD_HASH_BCRYPT || |
1149
|
|
|
$spiUser->hashAlgorithm === APIUser::PASSWORD_HASH_PHP_DEFAULT) { |
1150
|
|
|
return password_verify($password, $spiUser->passwordHash); |
1151
|
|
|
} |
1152
|
|
|
|
1153
|
|
|
// Randomize login time to protect against timing attacks |
1154
|
|
|
usleep(mt_rand(0, 30000)); |
1155
|
|
|
|
1156
|
|
|
$passwordHash = $this->createPasswordHash( |
1157
|
|
|
$login, |
1158
|
|
|
$password, |
1159
|
|
|
$this->settings['siteName'], |
1160
|
|
|
$spiUser->hashAlgorithm |
1161
|
|
|
); |
1162
|
|
|
|
1163
|
|
|
return $passwordHash === $spiUser->passwordHash; |
1164
|
|
|
} |
1165
|
|
|
|
1166
|
|
|
/** |
1167
|
|
|
* Returns password hash based on user data and site settings. |
1168
|
|
|
* |
1169
|
|
|
* @param string $login User login |
1170
|
|
|
* @param string $password User password |
1171
|
|
|
* @param string $site The name of the site |
1172
|
|
|
* @param int $type Type of password to generate |
1173
|
|
|
* |
1174
|
|
|
* @return string Generated password hash |
1175
|
|
|
* |
1176
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the type is not recognized |
1177
|
|
|
*/ |
1178
|
|
|
protected function createPasswordHash($login, $password, $site, $type) |
1179
|
|
|
{ |
1180
|
|
|
$deprecationWarningFormat = 'Password hash type %s is deprecated since 6.13.'; |
1181
|
|
|
|
1182
|
|
|
switch ($type) { |
1183
|
|
|
case APIUser::PASSWORD_HASH_MD5_PASSWORD: |
|
|
|
|
1184
|
|
|
@trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_MD5_PASSWORD'), E_USER_DEPRECATED); |
|
|
|
|
1185
|
|
|
|
1186
|
|
|
return md5($password); |
1187
|
|
|
|
1188
|
|
View Code Duplication |
case APIUser::PASSWORD_HASH_MD5_USER: |
|
|
|
|
1189
|
|
|
@trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_MD5_USER'), E_USER_DEPRECATED); |
|
|
|
|
1190
|
|
|
|
1191
|
|
|
return md5("$login\n$password"); |
1192
|
|
|
|
1193
|
|
View Code Duplication |
case APIUser::PASSWORD_HASH_MD5_SITE: |
|
|
|
|
1194
|
|
|
@trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_MD5_SITE'), E_USER_DEPRECATED); |
|
|
|
|
1195
|
|
|
|
1196
|
|
|
return md5("$login\n$password\n$site"); |
1197
|
|
|
|
1198
|
|
|
case APIUser::PASSWORD_HASH_PLAINTEXT: |
|
|
|
|
1199
|
|
|
@trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_PLAINTEXT'), E_USER_DEPRECATED); |
|
|
|
|
1200
|
|
|
|
1201
|
|
|
return $password; |
1202
|
|
|
|
1203
|
|
|
case APIUser::PASSWORD_HASH_BCRYPT: |
1204
|
|
|
return password_hash($password, PASSWORD_BCRYPT); |
1205
|
|
|
|
1206
|
|
|
case APIUser::PASSWORD_HASH_PHP_DEFAULT: |
1207
|
|
|
return password_hash($password, PASSWORD_DEFAULT); |
1208
|
|
|
|
1209
|
|
|
default: |
1210
|
|
|
throw new InvalidArgumentException('type', "Password hash type '$type' is not recognized"); |
1211
|
|
|
} |
1212
|
|
|
} |
1213
|
|
|
} |
1214
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.