Completed
Push — ezp28213_deprecate_insecure_ha... ( d938d3...066421 )
by
unknown
14:19
created

UserService::updateDeprecatedPasswordHash()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 2
nop 3
dl 0
loc 19
rs 9.4285
c 0
b 0
f 0
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)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

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.

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
190
            $subUserGroups[] = $this->buildDomainUserGroupObject(
191
                $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
192
                    $searchHit->valueObject->contentInfo->id,
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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();
0 ignored issues
show
Documentation Bug introduced by
It seems like $location->getSortClauses() of type array<integer,object,{"0":"object"}> is incompatible with the declared type array<integer,object<eZ\...tent\Query\SortClause>> of property $sortClauses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
374
            throw new InvalidArgumentValue('email', $userCreateStruct->email, 'UserCreateStruct');
375
        }
376
377
        if (!ezcMailTools::validateEmailAddress($userCreateStruct->email)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \ezcMailTools::validateE...serCreateStruct->email) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
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) {
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
432
                    $userCreateStruct->fields[$index]->value->login = $userCreateStruct->login;
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
433
                } else {
434
                    $userCreateStruct->fields[$index]->value = new UserValue(
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __set(), maybe consider adding a @property or @property-write annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
503
        // Get spiUser value from Field Value
504
        foreach ($content->getFields() as $field) {
505
            if (!$field->value instanceof UserValue) {
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
506
                continue;
507
            }
508
509
            /** @var \eZ\Publish\Core\FieldType\User\Value $value */
510
            $value = $field->value;
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 is deprecated, it will be updated to the hash type configured for the service (if they differ).
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->updateDeprecatedPasswordHash($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 the hash is of a deprecated type.
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 updateDeprecatedPasswordHash($login, $password, SPIUser $spiUser)
588
    {
589
        if ($spiUser->hashAlgorithm !== $this->settings['hashType'] &&
590
            in_array(
591
                $spiUser->hashAlgorithm,
592
                [
593
                    APIUser::PASSWORD_HASH_MD5_PASSWORD,
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...SWORD_HASH_MD5_PASSWORD has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
594
                    APIUser::PASSWORD_HASH_MD5_USER,
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...:PASSWORD_HASH_MD5_USER has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
595
                    APIUser::PASSWORD_HASH_MD5_SITE,
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...:PASSWORD_HASH_MD5_SITE has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
596
                    APIUser::PASSWORD_HASH_PLAINTEXT,
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...PASSWORD_HASH_PLAINTEXT has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
597
                ],
598
                true
599
            )
600
        ) {
601
            $spiUser->passwordHash = $this->createPasswordHash($login, $password, null, $this->settings['hashType']);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createPasswordHas...->settings['hashType']) can also be of type false. However, the property $passwordHash is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
602
            $spiUser->hashAlgorithm = $this->settings['hashType'];
603
            $this->userHandler->update($spiUser);
604
        }
605
    }
606
607
    /**
608
     * Loads a user for the given login.
609
     *
610
     * {@inheritdoc}
611
     *
612
     * @param string $login
613
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
614
     *
615
     * @return \eZ\Publish\API\Repository\Values\User\User
616
     *
617
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found
618
     */
619
    public function loadUserByLogin($login, array $prioritizedLanguages = [])
620
    {
621
        if (!is_string($login) || empty($login)) {
622
            throw new InvalidArgumentValue('login', $login);
623
        }
624
625
        $spiUser = $this->userHandler->loadByLogin($login);
626
627
        return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages);
628
    }
629
630
    /**
631
     * Loads a user for the given email.
632
     *
633
     * {@inheritdoc}
634
     *
635
     * @param string $email
636
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
637
     *
638
     * @return \eZ\Publish\API\Repository\Values\User\User[]
639
     */
640
    public function loadUsersByEmail($email, array $prioritizedLanguages = [])
641
    {
642
        if (!is_string($email) || empty($email)) {
643
            throw new InvalidArgumentValue('email', $email);
644
        }
645
646
        $users = array();
647
        foreach ($this->userHandler->loadByEmail($email) as $spiUser) {
648
            $users[] = $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages);
649
        }
650
651
        return $users;
652
    }
653
654
    /**
655
     * This method deletes a user.
656
     *
657
     * @param \eZ\Publish\API\Repository\Values\User\User $user
658
     *
659
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete the user
660
     */
661 View Code Duplication
    public function deleteUser(APIUser $user)
662
    {
663
        $loadedUser = $this->loadUser($user->id);
664
665
        $this->repository->beginTransaction();
666
        try {
667
            $affectedLocationIds = $this->repository->getContentService()->deleteContent($loadedUser->getVersionInfo()->getContentInfo());
668
            $this->userHandler->delete($loadedUser->id);
669
            $this->repository->commit();
670
        } catch (Exception $e) {
671
            $this->repository->rollback();
672
            throw $e;
673
        }
674
675
        return $affectedLocationIds;
676
    }
677
678
    /**
679
     * Updates a user.
680
     *
681
     * 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
682
     * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods.
683
     *
684
     * @param \eZ\Publish\API\Repository\Values\User\User $user
685
     * @param \eZ\Publish\API\Repository\Values\User\UserUpdateStruct $userUpdateStruct
686
     *
687
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user
688
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userUpdateStruct is not valid
689
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty
690
     *
691
     * @return \eZ\Publish\API\Repository\Values\User\User
692
     */
693
    public function updateUser(APIUser $user, UserUpdateStruct $userUpdateStruct)
694
    {
695
        $loadedUser = $this->loadUser($user->id);
696
697
        // We need to determine if we have anything to update.
698
        // UserUpdateStruct is specific as some of the new content is in
699
        // content update struct and some of it is in additional fields like
700
        // email, password and so on
701
        $doUpdate = false;
702
        foreach ($userUpdateStruct as $propertyValue) {
0 ignored issues
show
Bug introduced by
The expression $userUpdateStruct of type object<eZ\Publish\API\Re...\User\UserUpdateStruct> is not traversable.
Loading history...
703
            if ($propertyValue !== null) {
704
                $doUpdate = true;
705
                break;
706
            }
707
        }
708
709
        if (!$doUpdate) {
710
            // Nothing to update, so we just quit
711
            return $user;
712
        }
713
714
        if ($userUpdateStruct->email !== null) {
715 View Code Duplication
            if (!is_string($userUpdateStruct->email) || empty($userUpdateStruct->email)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
716
                throw new InvalidArgumentValue('email', $userUpdateStruct->email, 'UserUpdateStruct');
717
            }
718
719
            if (!ezcMailTools::validateEmailAddress($userUpdateStruct->email)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \ezcMailTools::validateE...serUpdateStruct->email) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
720
                throw new InvalidArgumentValue('email', $userUpdateStruct->email, 'UserUpdateStruct');
721
            }
722
        }
723
724 View Code Duplication
        if ($userUpdateStruct->password !== null && (!is_string($userUpdateStruct->password) || empty($userUpdateStruct->password))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
725
            throw new InvalidArgumentValue('password', $userUpdateStruct->password, 'UserUpdateStruct');
726
        }
727
728
        if ($userUpdateStruct->enabled !== null && !is_bool($userUpdateStruct->enabled)) {
729
            throw new InvalidArgumentValue('enabled', $userUpdateStruct->enabled, 'UserUpdateStruct');
730
        }
731
732
        if ($userUpdateStruct->maxLogin !== null && !is_int($userUpdateStruct->maxLogin)) {
733
            throw new InvalidArgumentValue('maxLogin', $userUpdateStruct->maxLogin, 'UserUpdateStruct');
734
        }
735
736
        $contentService = $this->repository->getContentService();
737
738
        if (!$this->repository->canUser('content', 'edit', $loadedUser)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

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.

Loading history...
739
            throw new UnauthorizedException('content', 'edit');
740
        }
741
742
        $this->repository->beginTransaction();
743
        try {
744
            $publishedContent = $loadedUser;
745 View Code Duplication
            if ($userUpdateStruct->contentUpdateStruct !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
746
                $contentDraft = $contentService->createContentDraft($loadedUser->getVersionInfo()->getContentInfo());
747
                $contentDraft = $contentService->updateContent(
748
                    $contentDraft->getVersionInfo(),
749
                    $userUpdateStruct->contentUpdateStruct
750
                );
751
                $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo());
752
            }
753
754
            if ($userUpdateStruct->contentMetadataUpdateStruct !== null) {
755
                $contentService->updateContentMetadata(
756
                    $publishedContent->getVersionInfo()->getContentInfo(),
757
                    $userUpdateStruct->contentMetadataUpdateStruct
758
                );
759
            }
760
761
            $this->userHandler->update(
762
                new SPIUser(
763
                    array(
764
                        'id' => $loadedUser->id,
765
                        'login' => $loadedUser->login,
0 ignored issues
show
Documentation introduced by
The property $login is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
766
                        'email' => $userUpdateStruct->email ?: $loadedUser->email,
0 ignored issues
show
Documentation introduced by
The property $email is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
767
                        'passwordHash' => $userUpdateStruct->password ?
768
                            $this->createPasswordHash(
769
                                $loadedUser->login,
0 ignored issues
show
Documentation introduced by
The property $login is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
770
                                $userUpdateStruct->password,
771
                                $this->settings['siteName'],
772
                                $this->settings['hashType']
773
                            ) :
774
                            $loadedUser->passwordHash,
0 ignored issues
show
Documentation introduced by
The property $passwordHash is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
775
                        'hashAlgorithm' => $userUpdateStruct->password ?
776
                            $this->settings['hashType'] :
777
                            $loadedUser->hashAlgorithm,
0 ignored issues
show
Documentation introduced by
The property $hashAlgorithm is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
778
                        'isEnabled' => $userUpdateStruct->enabled !== null ? $userUpdateStruct->enabled : $loadedUser->enabled,
0 ignored issues
show
Documentation introduced by
The property $enabled is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
779
                        'maxLogin' => $userUpdateStruct->maxLogin !== null ? (int)$userUpdateStruct->maxLogin : $loadedUser->maxLogin,
0 ignored issues
show
Documentation introduced by
The property $maxLogin is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
780
                    )
781
                )
782
            );
783
784
            $this->repository->commit();
785
        } catch (Exception $e) {
786
            $this->repository->rollback();
787
            throw $e;
788
        }
789
790
        return $this->loadUser($loadedUser->id);
791
    }
792
793
    /**
794
     * Assigns a new user group to the user.
795
     *
796
     * @param \eZ\Publish\API\Repository\Values\User\User $user
797
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
798
     *
799
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign the user group to the user
800
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is already in the given user group
801
     */
802
    public function assignUserToUserGroup(APIUser $user, APIUserGroup $userGroup)
803
    {
804
        $loadedUser = $this->loadUser($user->id);
805
        $loadedGroup = $this->loadUserGroup($userGroup->id);
806
        $locationService = $this->repository->getLocationService();
807
808
        $existingGroupIds = array();
809
        $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo());
810
        foreach ($userLocations as $userLocation) {
811
            $existingGroupIds[] = $userLocation->parentLocationId;
812
        }
813
814
        if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
815
            throw new BadStateException('userGroup', 'user group has no main location or no locations');
816
        }
817
818
        if (in_array($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId, $existingGroupIds)) {
819
            // user is already assigned to the user group
820
            throw new InvalidArgumentException('user', 'user is already in the given user group');
821
        }
822
823
        $locationCreateStruct = $locationService->newLocationCreateStruct(
824
            $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId
825
        );
826
827
        $this->repository->beginTransaction();
828
        try {
829
            $locationService->createLocation(
830
                $loadedUser->getVersionInfo()->getContentInfo(),
831
                $locationCreateStruct
832
            );
833
            $this->repository->commit();
834
        } catch (Exception $e) {
835
            $this->repository->rollback();
836
            throw $e;
837
        }
838
    }
839
840
    /**
841
     * Removes a user group from the user.
842
     *
843
     * @param \eZ\Publish\API\Repository\Values\User\User $user
844
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
845
     *
846
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove the user group from the user
847
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is not in the given user group
848
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $userGroup is the last assigned user group
849
     */
850
    public function unAssignUserFromUserGroup(APIUser $user, APIUserGroup $userGroup)
851
    {
852
        $loadedUser = $this->loadUser($user->id);
853
        $loadedGroup = $this->loadUserGroup($userGroup->id);
854
        $locationService = $this->repository->getLocationService();
855
856
        $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo());
857
        if (empty($userLocations)) {
858
            throw new BadStateException('user', 'user has no locations, cannot unassign from group');
859
        }
860
861
        if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
862
            throw new BadStateException('userGroup', 'user group has no main location or no locations, cannot unassign');
863
        }
864
865
        foreach ($userLocations as $userLocation) {
866
            if ($userLocation->parentLocationId == $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId) {
867
                // Throw this specific BadState when we know argument is valid
868
                if (count($userLocations) === 1) {
869
                    throw new BadStateException('user', 'user only has one user group, cannot unassign from last group');
870
                }
871
872
                $this->repository->beginTransaction();
873
                try {
874
                    $locationService->deleteLocation($userLocation);
875
                    $this->repository->commit();
876
877
                    return;
878
                } catch (Exception $e) {
879
                    $this->repository->rollback();
880
                    throw $e;
881
                }
882
            }
883
        }
884
885
        throw new InvalidArgumentException('userGroup', 'user is not in the given user group');
886
    }
887
888
    /**
889
     * Loads the user groups the user belongs to.
890
     *
891
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed read the user or user group
892
     *
893
     * @param \eZ\Publish\API\Repository\Values\User\User $user
894
     * @param int $offset the start offset for paging
895
     * @param int $limit the number of user groups returned
896
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
897
     *
898
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup[]
899
     */
900
    public function loadUserGroupsOfUser(APIUser $user, $offset = 0, $limit = 25, array $prioritizedLanguages = [])
901
    {
902
        $locationService = $this->repository->getLocationService();
903
904
        if (!$this->repository->getPermissionResolver()->canUser('content', 'read', $user)) {
905
            throw new UnauthorizedException('content', 'read');
906
        }
907
908
        $userLocations = $locationService->loadLocations(
909
            $user->getVersionInfo()->getContentInfo()
910
        );
911
912
        $parentLocationIds = array();
913
        foreach ($userLocations as $userLocation) {
914
            if ($userLocation->parentLocationId !== null) {
915
                $parentLocationIds[] = $userLocation->parentLocationId;
916
            }
917
        }
918
919
        $searchQuery = new LocationQuery();
920
921
        $searchQuery->offset = $offset;
922
        $searchQuery->limit = $limit;
923
        $searchQuery->performCount = false;
924
925
        $searchQuery->filter = new CriterionLogicalAnd(
926
            [
927
                new CriterionContentTypeId($this->settings['userGroupClassID']),
928
                new CriterionLocationId($parentLocationIds),
929
            ]
930
        );
931
932
        $searchResult = $this->repository->getSearchService()->findLocations($searchQuery);
933
934
        $userGroups = [];
935 View Code Duplication
        foreach ($searchResult->searchHits as $resultItem) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
936
            $userGroups[] = $this->buildDomainUserGroupObject(
937
                $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
938
                    $resultItem->valueObject->contentInfo->id,
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
939
                    $prioritizedLanguages
940
                )
941
            );
942
        }
943
944
        return $userGroups;
945
    }
946
947
    /**
948
     * Loads the users of a user group.
949
     *
950
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the users or user group
951
     *
952
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
953
     * @param int $offset the start offset for paging
954
     * @param int $limit the number of users returned
955
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
956
     *
957
     * @return \eZ\Publish\API\Repository\Values\User\User[]
958
     */
959
    public function loadUsersOfUserGroup(
960
        APIUserGroup $userGroup,
961
        $offset = 0,
962
        $limit = 25,
963
        array $prioritizedLanguages = []
964
    ) {
965
        $loadedUserGroup = $this->loadUserGroup($userGroup->id);
966
967
        if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
968
            return [];
969
        }
970
971
        $mainGroupLocation = $this->repository->getLocationService()->loadLocation(
972
            $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId
973
        );
974
975
        $searchQuery = new LocationQuery();
976
977
        $searchQuery->filter = new CriterionLogicalAnd(
978
            [
979
                new CriterionContentTypeId($this->settings['userClassID']),
980
                new CriterionParentLocationId($mainGroupLocation->id),
981
            ]
982
        );
983
984
        $searchQuery->offset = $offset;
985
        $searchQuery->limit = $limit;
986
        $searchQuery->performCount = false;
987
        $searchQuery->sortClauses = $mainGroupLocation->getSortClauses();
0 ignored issues
show
Documentation Bug introduced by
It seems like $mainGroupLocation->getSortClauses() of type array<integer,object,{"0":"object"}> is incompatible with the declared type array<integer,object<eZ\...tent\Query\SortClause>> of property $sortClauses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
988
989
        $searchResult = $this->repository->getSearchService()->findLocations($searchQuery);
990
991
        $users = [];
992
        foreach ($searchResult->searchHits as $resultItem) {
993
            $users[] = $this->buildDomainUserObject(
994
                $this->userHandler->load($resultItem->valueObject->contentInfo->id),
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
995
                $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
996
                    $resultItem->valueObject->contentInfo->id,
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
997
                    $prioritizedLanguages
998
                )
999
            );
1000
        }
1001
1002
        return $users;
1003
    }
1004
1005
    /**
1006
     * Instantiate a user create class.
1007
     *
1008
     * @param string $login the login of the new user
1009
     * @param string $email the email of the new user
1010
     * @param string $password the plain password of the new user
1011
     * @param string $mainLanguageCode the main language for the underlying content object
1012
     * @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
1013
     *
1014
     * @return \eZ\Publish\API\Repository\Values\User\UserCreateStruct
1015
     */
1016
    public function newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType = null)
1017
    {
1018
        if ($contentType === null) {
1019
            $contentType = $this->repository->getContentTypeService()->loadContentType(
1020
                $this->settings['userClassID']
1021
            );
1022
        }
1023
1024
        return new UserCreateStruct(
1025
            array(
1026
                'contentType' => $contentType,
1027
                'mainLanguageCode' => $mainLanguageCode,
1028
                'login' => $login,
1029
                'email' => $email,
1030
                'password' => $password,
1031
                'enabled' => true,
1032
                'fields' => array(),
1033
            )
1034
        );
1035
    }
1036
1037
    /**
1038
     * Instantiate a user group create class.
1039
     *
1040
     * @param string $mainLanguageCode The main language for the underlying content object
1041
     * @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
1042
     *
1043
     * @return \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct
1044
     */
1045
    public function newUserGroupCreateStruct($mainLanguageCode, $contentType = null)
1046
    {
1047
        if ($contentType === null) {
1048
            $contentType = $this->repository->getContentTypeService()->loadContentType(
1049
                $this->settings['userGroupClassID']
1050
            );
1051
        }
1052
1053
        return new UserGroupCreateStruct(
1054
            array(
1055
                'contentType' => $contentType,
1056
                'mainLanguageCode' => $mainLanguageCode,
1057
                'fields' => array(),
1058
            )
1059
        );
1060
    }
1061
1062
    /**
1063
     * Instantiate a new user update struct.
1064
     *
1065
     * @return \eZ\Publish\API\Repository\Values\User\UserUpdateStruct
1066
     */
1067
    public function newUserUpdateStruct()
1068
    {
1069
        return new UserUpdateStruct();
1070
    }
1071
1072
    /**
1073
     * Instantiate a new user group update struct.
1074
     *
1075
     * @return \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct
1076
     */
1077
    public function newUserGroupUpdateStruct()
1078
    {
1079
        return new UserGroupUpdateStruct();
1080
    }
1081
1082
    /**
1083
     * Builds the domain UserGroup object from provided Content object.
1084
     *
1085
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1086
     *
1087
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup
1088
     */
1089
    protected function buildDomainUserGroupObject(APIContent $content)
1090
    {
1091
        $locationService = $this->repository->getLocationService();
1092
1093
        $subGroupCount = 0;
1094
        if ($content->getVersionInfo()->getContentInfo()->mainLocationId !== null) {
1095
            $mainLocation = $locationService->loadLocation(
1096
                $content->getVersionInfo()->getContentInfo()->mainLocationId
1097
            );
1098
            $parentLocation = $locationService->loadLocation($mainLocation->parentLocationId);
1099
            $subGroups = $this->searchSubGroups($mainLocation, 0, 0);
1100
            $subGroupCount = $subGroups->totalCount;
1101
        }
1102
1103
        return new UserGroup(
1104
            array(
1105
                'content' => $content,
1106
                'parentId' => isset($parentLocation) ? $parentLocation->contentId : null,
1107
                'subGroupCount' => $subGroupCount,
1108
            )
1109
        );
1110
    }
1111
1112
    /**
1113
     * Builds the domain user object from provided persistence user object.
1114
     *
1115
     * @param \eZ\Publish\SPI\Persistence\User $spiUser
1116
     * @param \eZ\Publish\API\Repository\Values\Content\Content|null $content
1117
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
1118
     *
1119
     * @return \eZ\Publish\API\Repository\Values\User\User
1120
     */
1121
    protected function buildDomainUserObject(
1122
        SPIUser $spiUser,
1123
        APIContent $content = null,
1124
        array $prioritizedLanguages = []
1125
    ) {
1126
        if ($content === null) {
1127
            $content = $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1128
                $spiUser->id,
1129
                $prioritizedLanguages
1130
            );
1131
        }
1132
1133
        return new User(
1134
            array(
1135
                'content' => $content,
1136
                'login' => $spiUser->login,
1137
                'email' => $spiUser->email,
1138
                'passwordHash' => $spiUser->passwordHash,
1139
                'hashAlgorithm' => (int)$spiUser->hashAlgorithm,
1140
                'enabled' => $spiUser->isEnabled,
1141
                'maxLogin' => (int)$spiUser->maxLogin,
1142
            )
1143
        );
1144
    }
1145
1146
    /**
1147
     * Verifies if the provided login and password are valid.
1148
     *
1149
     * @param string $login User login
1150
     * @param string $password User password
1151
     * @param \eZ\Publish\SPI\Persistence\User $spiUser Loaded user handler
1152
     *
1153
     * @return bool return true if the login and password are sucessfully
1154
     * validate and false, if not.
1155
     */
1156
    protected function verifyPassword($login, $password, $spiUser)
1157
    {
1158
        // In case of bcrypt let php's password functionality do it's magic
1159
        if ($spiUser->hashAlgorithm === APIUser::PASSWORD_HASH_BCRYPT ||
1160
            $spiUser->hashAlgorithm === APIUser::PASSWORD_HASH_PHP_DEFAULT) {
1161
            return password_verify($password, $spiUser->passwordHash);
1162
        }
1163
1164
        // Randomize login time to protect against timing attacks
1165
        usleep(mt_rand(0, 30000));
1166
1167
        $passwordHash = $this->createPasswordHash(
1168
            $login,
1169
            $password,
1170
            $this->settings['siteName'],
1171
            $spiUser->hashAlgorithm
1172
        );
1173
1174
        return $passwordHash === $spiUser->passwordHash;
1175
    }
1176
1177
    /**
1178
     * Returns password hash based on user data and site settings.
1179
     *
1180
     * @param string $login User login
1181
     * @param string $password User password
1182
     * @param string $site The name of the site
1183
     * @param int $type Type of password to generate
1184
     *
1185
     * @return string Generated password hash
1186
     *
1187
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the type is not recognized
1188
     */
1189
    protected function createPasswordHash($login, $password, $site, $type)
1190
    {
1191
        $deprecationWarningFormat = 'Password hash type %s is deprecated since 7.0.';
1192
1193
        switch ($type) {
1194
            case APIUser::PASSWORD_HASH_MD5_PASSWORD:
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...SWORD_HASH_MD5_PASSWORD has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
1195
                @trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_MD5_PASSWORD'), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1196
1197
                return md5($password);
1198
1199 View Code Duplication
            case APIUser::PASSWORD_HASH_MD5_USER:
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...:PASSWORD_HASH_MD5_USER has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1200
                @trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_MD5_USER'), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1201
1202
                return md5("$login\n$password");
1203
1204 View Code Duplication
            case APIUser::PASSWORD_HASH_MD5_SITE:
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...:PASSWORD_HASH_MD5_SITE has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1205
                @trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_MD5_SITE'), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1206
1207
                return md5("$login\n$password\n$site");
1208
1209
            case APIUser::PASSWORD_HASH_PLAINTEXT:
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor...PASSWORD_HASH_PLAINTEXT has been deprecated with message: since 6.13

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
1210
                @trigger_error(sprintf($deprecationWarningFormat, 'PASSWORD_HASH_PLAINTEXT'), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1211
1212
                return $password;
1213
1214
            case APIUser::PASSWORD_HASH_BCRYPT:
1215
                return password_hash($password, PASSWORD_BCRYPT);
1216
1217
            case APIUser::PASSWORD_HASH_PHP_DEFAULT:
1218
                return password_hash($password, PASSWORD_DEFAULT);
1219
1220
            default:
1221
                throw new InvalidArgumentException('type', "Password hash type '$type' is not recognized");
1222
        }
1223
    }
1224
}
1225