This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare( strict_types = 1 ); |
||
4 | |||
5 | namespace Wikibase\Repo\Api; |
||
6 | |||
7 | use ApiBase; |
||
8 | use ApiMain; |
||
9 | use ApiUsageException; |
||
10 | use DataValues\IllegalValueException; |
||
11 | use Deserializers\Deserializer; |
||
12 | use Diff\Comparer\ComparableComparer; |
||
13 | use Diff\Differ\OrderedListDiffer; |
||
14 | use IBufferingStatsdDataFactory; |
||
15 | use InvalidArgumentException; |
||
16 | use LogicException; |
||
17 | use OutOfBoundsException; |
||
18 | use Wikibase\DataModel\Entity\StatementListProvidingEntity; |
||
19 | use Wikibase\DataModel\Services\Statement\StatementGuidParser; |
||
20 | use Wikibase\DataModel\Services\Statement\StatementGuidParsingException; |
||
21 | use Wikibase\DataModel\Statement\Statement; |
||
22 | use Wikibase\DataModel\Statement\StatementList; |
||
23 | use Wikibase\Lib\Summary; |
||
24 | use Wikibase\Repo\ChangeOp\StatementChangeOpFactory; |
||
25 | use Wikibase\Repo\ClaimSummaryBuilder; |
||
26 | use Wikibase\Repo\Diff\ClaimDiffer; |
||
27 | use Wikibase\Repo\FederatedProperties\FederatedPropertiesException; |
||
28 | use Wikibase\Repo\WikibaseRepo; |
||
29 | |||
30 | /** |
||
31 | * API module for creating or updating an entire Claim. |
||
32 | * |
||
33 | * @license GPL-2.0-or-later |
||
34 | * @author Jeroen De Dauw < [email protected] > |
||
35 | * @author Tobias Gritschacher < [email protected] > |
||
36 | * @author Addshore |
||
37 | */ |
||
38 | class SetClaim extends ApiBase { |
||
39 | |||
40 | use FederatedPropertyApiValidatorTrait; |
||
41 | |||
42 | /** |
||
43 | * @var StatementChangeOpFactory |
||
44 | */ |
||
45 | private $statementChangeOpFactory; |
||
46 | |||
47 | /** |
||
48 | * @var ApiErrorReporter |
||
49 | */ |
||
50 | protected $errorReporter; |
||
51 | |||
52 | /** |
||
53 | * @var Deserializer |
||
54 | */ |
||
55 | private $statementDeserializer; |
||
56 | |||
57 | /** |
||
58 | * @var StatementModificationHelper |
||
59 | */ |
||
60 | private $modificationHelper; |
||
61 | |||
62 | /** |
||
63 | * @var StatementGuidParser |
||
64 | */ |
||
65 | private $guidParser; |
||
66 | |||
67 | /** |
||
68 | * @var ResultBuilder |
||
69 | */ |
||
70 | private $resultBuilder; |
||
71 | |||
72 | /** |
||
73 | * @var EntitySavingHelper |
||
74 | */ |
||
75 | private $entitySavingHelper; |
||
76 | |||
77 | /** @var IBufferingStatsdDataFactory */ |
||
78 | private $stats; |
||
79 | |||
80 | public function __construct( |
||
81 | ApiMain $mainModule, |
||
82 | string $moduleName, |
||
83 | ApiErrorReporter $errorReporter, |
||
84 | Deserializer $statementDeserializer, |
||
85 | StatementChangeOpFactory $statementChangeOpFactory, |
||
86 | StatementModificationHelper $modificationHelper, |
||
87 | StatementGuidParser $guidParser, |
||
88 | callable $resultBuilderInstantiator, |
||
89 | callable $entitySavingHelperInstantiator, |
||
90 | IBufferingStatsdDataFactory $stats, |
||
91 | bool $federatedPropertiesEnabled |
||
92 | ) { |
||
93 | parent::__construct( $mainModule, $moduleName ); |
||
94 | |||
95 | $this->errorReporter = $errorReporter; |
||
96 | $this->statementDeserializer = $statementDeserializer; |
||
97 | $this->statementChangeOpFactory = $statementChangeOpFactory; |
||
98 | $this->modificationHelper = $modificationHelper; |
||
99 | $this->guidParser = $guidParser; |
||
100 | $this->resultBuilder = $resultBuilderInstantiator( $this ); |
||
101 | $this->entitySavingHelper = $entitySavingHelperInstantiator( $this ); |
||
102 | $this->stats = $stats; |
||
103 | $this->federatedPropertiesEnabled = $federatedPropertiesEnabled; |
||
104 | } |
||
105 | |||
106 | public static function factory( ApiMain $mainModule, string $moduleName, IBufferingStatsdDataFactory $stats ): self { |
||
107 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
108 | $apiHelperFactory = $wikibaseRepo->getApiHelperFactory( $mainModule->getContext() ); |
||
109 | $changeOpFactoryProvider = $wikibaseRepo->getChangeOpFactoryProvider(); |
||
110 | |||
111 | $modificationHelper = new StatementModificationHelper( |
||
112 | $wikibaseRepo->getSnakFactory(), |
||
113 | $wikibaseRepo->getEntityIdParser(), |
||
114 | $wikibaseRepo->getStatementGuidValidator(), |
||
115 | $apiHelperFactory->getErrorReporter( $mainModule ) |
||
116 | ); |
||
117 | |||
118 | return new self( |
||
119 | $mainModule, |
||
120 | $moduleName, |
||
121 | $apiHelperFactory->getErrorReporter( $mainModule ), |
||
122 | $wikibaseRepo->getExternalFormatStatementDeserializer(), |
||
123 | $changeOpFactoryProvider->getStatementChangeOpFactory(), |
||
124 | $modificationHelper, |
||
125 | $wikibaseRepo->getStatementGuidParser(), |
||
126 | function ( $module ) use ( $apiHelperFactory ) { |
||
127 | return $apiHelperFactory->getResultBuilder( $module ); |
||
128 | }, |
||
129 | function ( $module ) use ( $apiHelperFactory ) { |
||
130 | return $apiHelperFactory->getEntitySavingHelper( $module ); |
||
131 | }, |
||
132 | $stats, |
||
133 | $wikibaseRepo->inFederatedPropertyMode() |
||
134 | ); |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * @inheritDoc |
||
139 | */ |
||
140 | public function execute(): void { |
||
141 | try { |
||
142 | $this->executeInternal(); |
||
143 | } catch ( FederatedPropertiesException $ex ) { |
||
144 | $this->errorReporter->dieWithError( |
||
145 | 'wikibase-federated-properties-save-api-error-message', |
||
146 | 'failed-save' |
||
147 | ); |
||
148 | } |
||
149 | } |
||
150 | |||
151 | private function executeInternal(): void { |
||
152 | $params = $this->extractRequestParams(); |
||
153 | $statement = $this->getStatementFromParams( $params ); |
||
154 | $guid = $statement->getGuid(); |
||
155 | |||
156 | if ( $guid === null ) { |
||
157 | $this->errorReporter->dieError( 'GUID must be set when setting a claim', 'invalid-claim' ); |
||
0 ignored issues
–
show
|
|||
158 | } |
||
159 | |||
160 | try { |
||
161 | $statementGuid = $this->guidParser->parse( $guid ); |
||
162 | } catch ( StatementGuidParsingException $ex ) { |
||
163 | $this->errorReporter->dieException( $ex, 'invalid-claim' ); |
||
164 | throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' ); |
||
165 | } |
||
166 | |||
167 | $entityId = $statementGuid->getEntityId(); |
||
168 | $this->validateAlteringEntityById( $entityId ); |
||
169 | $entity = $this->entitySavingHelper->loadEntity( $entityId ); |
||
170 | |||
171 | if ( !( $entity instanceof StatementListProvidingEntity ) ) { |
||
172 | $this->errorReporter->dieError( 'The given entity cannot contain statements', 'not-supported' ); |
||
0 ignored issues
–
show
The method
Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.
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. ![]() |
|||
173 | throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' ); |
||
174 | } |
||
175 | |||
176 | if ( $params['ignoreduplicatemainsnak'] ) { |
||
177 | if ( $this->statementMainSnakAlreadyExists( $statement, $entity->getStatements() ) ) { |
||
178 | $this->addWarning( 'wikibase-setclaim-warning-duplicatemainsnak' ); |
||
179 | return; |
||
180 | } |
||
181 | } |
||
182 | $summary = $this->getSummary( $params, $statement, $entity->getStatements() ); |
||
183 | |||
184 | $index = $params['index'] ?? null; |
||
185 | $changeop = $this->statementChangeOpFactory->newSetStatementOp( $statement, $index ); |
||
186 | $this->modificationHelper->applyChangeOp( $changeop, $entity, $summary ); |
||
187 | |||
188 | $status = $this->entitySavingHelper->attemptSaveEntity( $entity, $summary ); |
||
189 | $this->resultBuilder->addRevisionIdFromStatusToResult( $status, 'pageinfo' ); |
||
190 | $this->resultBuilder->markSuccess(); |
||
191 | $this->resultBuilder->addStatement( $statement ); |
||
192 | |||
193 | $this->stats->increment( 'wikibase.repo.api.wbsetclaim.total' ); |
||
194 | if ( $index !== null ) { |
||
195 | $this->stats->increment( 'wikibase.repo.api.wbsetclaim.index' ); |
||
196 | } |
||
197 | } |
||
198 | |||
199 | private function statementMainSnakAlreadyExists( |
||
200 | Statement $statement, |
||
201 | StatementList $existingStatements |
||
202 | ) : bool { |
||
203 | $propertyId = $statement->getPropertyId(); |
||
204 | $mainSnak = $statement->getMainSnak(); |
||
205 | foreach ( $existingStatements as $existingStatement ) { |
||
206 | if ( $existingStatement->getPropertyId()->equals( $propertyId ) ) { |
||
207 | if ( $existingStatement->getMainSnak()->equals( $mainSnak ) ) { |
||
208 | return true; |
||
209 | } |
||
210 | } |
||
211 | } |
||
212 | return false; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * @param array $params |
||
217 | * @param Statement $statement |
||
218 | * @param StatementList $statementList |
||
219 | * |
||
220 | * @throws InvalidArgumentException |
||
221 | * @return Summary |
||
222 | * |
||
223 | * @todo this summary builder is ugly and summary stuff needs to be refactored |
||
224 | */ |
||
225 | private function getSummary( array $params, Statement $statement, StatementList $statementList ): Summary { |
||
226 | $claimSummaryBuilder = new ClaimSummaryBuilder( |
||
227 | $this->getModuleName(), |
||
228 | new ClaimDiffer( new OrderedListDiffer( new ComparableComparer() ) ) |
||
229 | ); |
||
230 | |||
231 | $summary = $claimSummaryBuilder->buildClaimSummary( |
||
232 | $statementList->getFirstStatementWithGuid( $statement->getGuid() ), |
||
233 | $statement |
||
234 | ); |
||
235 | |||
236 | if ( isset( $params['summary'] ) ) { |
||
237 | $summary->setUserSummary( $params['summary'] ); |
||
238 | } |
||
239 | |||
240 | return $summary; |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * @param array $params |
||
245 | * |
||
246 | * @throws IllegalValueException |
||
247 | * @throws ApiUsageException |
||
248 | * @throws LogicException |
||
249 | * @return Statement |
||
250 | */ |
||
251 | private function getStatementFromParams( array $params ): Statement { |
||
252 | try { |
||
253 | $serializedStatement = json_decode( $params['claim'], true ); |
||
254 | if ( !is_array( $serializedStatement ) ) { |
||
255 | throw new IllegalValueException( 'Failed to get statement from Serialization' ); |
||
256 | } |
||
257 | $statement = $this->statementDeserializer->deserialize( $serializedStatement ); |
||
258 | if ( !( $statement instanceof Statement ) ) { |
||
259 | throw new IllegalValueException( 'Failed to get statement from Serialization' ); |
||
260 | } |
||
261 | return $statement; |
||
262 | } catch ( InvalidArgumentException $invalidArgumentException ) { |
||
263 | $this->errorReporter->dieError( |
||
0 ignored issues
–
show
The method
Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.
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. ![]() |
|||
264 | 'Failed to get claim from claim Serialization ' . $invalidArgumentException->getMessage(), |
||
265 | 'invalid-claim' |
||
266 | ); |
||
267 | } catch ( OutOfBoundsException $outOfBoundsException ) { |
||
268 | $this->errorReporter->dieError( |
||
0 ignored issues
–
show
The method
Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.
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. ![]() |
|||
269 | 'Failed to get claim from claim Serialization ' . $outOfBoundsException->getMessage(), |
||
270 | 'invalid-claim' |
||
271 | ); |
||
272 | } |
||
273 | |||
274 | // Note: since dieUsage() never returns, this should be unreachable! |
||
275 | throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' ); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * @inheritDoc |
||
280 | */ |
||
281 | public function isWriteMode(): bool { |
||
282 | return true; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * @see ApiBase::needsToken |
||
287 | * |
||
288 | * @return string |
||
289 | */ |
||
290 | public function needsToken(): string { |
||
291 | return 'csrf'; |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * @inheritDoc |
||
296 | */ |
||
297 | protected function getAllowedParams(): array { |
||
298 | return array_merge( |
||
299 | [ |
||
300 | 'claim' => [ |
||
301 | self::PARAM_TYPE => 'text', |
||
302 | self::PARAM_REQUIRED => true, |
||
303 | ], |
||
304 | 'index' => [ |
||
305 | self::PARAM_TYPE => 'integer', |
||
306 | ], |
||
307 | 'summary' => [ |
||
308 | self::PARAM_TYPE => 'string', |
||
309 | ], |
||
310 | 'tags' => [ |
||
311 | self::PARAM_TYPE => 'tags', |
||
312 | self::PARAM_ISMULTI => true, |
||
313 | ], |
||
314 | 'token' => null, |
||
315 | 'baserevid' => [ |
||
316 | self::PARAM_TYPE => 'integer', |
||
317 | ], |
||
318 | 'bot' => false, |
||
319 | 'ignoreduplicatemainsnak' => false, |
||
320 | ], |
||
321 | parent::getAllowedParams() |
||
322 | ); |
||
323 | } |
||
324 | |||
325 | /** |
||
326 | * @inheritDoc |
||
327 | */ |
||
328 | protected function getExamplesMessages(): array { |
||
329 | return [ |
||
330 | 'action=wbsetclaim&claim={"id":"Q2$5627445f-43cb-ed6d-3adb-760e85bd17ee",' |
||
331 | . '"type":"claim","mainsnak":{"snaktype":"value","property":"P1",' |
||
332 | . '"datavalue":{"value":"City","type":"string"}}}' |
||
333 | => 'apihelp-wbsetclaim-example-1', |
||
334 | 'action=wbsetclaim&claim={"id":"Q2$5627445f-43cb-ed6d-3adb-760e85bd17ee",' |
||
335 | . '"type":"claim","mainsnak":{"snaktype":"value","property":"P1",' |
||
336 | . '"datavalue":{"value":"City","type":"string"}}}&index=0' |
||
337 | => 'apihelp-wbsetclaim-example-2', |
||
338 | 'action=wbsetclaim&claim={"id":"Q2$5627445f-43cb-ed6d-3adb-760e85bd17ee",' |
||
339 | . '"type":"statement","mainsnak":{"snaktype":"value","property":"P1",' |
||
340 | . '"datavalue":{"value":"City","type":"string"}},' |
||
341 | . '"references":[{"snaks":{"P2":[{"snaktype":"value","property":"P2",' |
||
342 | . '"datavalue":{"value":"The Economy of Cities","type":"string"}}]},' |
||
343 | . '"snaks-order":["P2"]}],"rank":"normal"}' |
||
344 | => 'apihelp-wbsetclaim-example-3', |
||
345 | ]; |
||
346 | } |
||
347 | |||
348 | } |
||
349 |
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.