Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
27 / 27 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
VariablesBlobStore | |
100.00% |
27 / 27 |
|
100.00% |
3 / 3 |
10 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
storeVarDump | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
loadVarDump | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\AbuseFilter\Variables; |
4 | |
5 | use InvalidArgumentException; |
6 | use MediaWiki\Json\FormatJson; |
7 | use MediaWiki\Storage\BlobAccessException; |
8 | use MediaWiki\Storage\BlobStore; |
9 | use MediaWiki\Storage\BlobStoreFactory; |
10 | use stdClass; |
11 | |
12 | /** |
13 | * This service is used to store and load var dumps to a BlobStore |
14 | */ |
15 | class VariablesBlobStore { |
16 | public const SERVICE_NAME = 'AbuseFilterVariablesBlobStore'; |
17 | |
18 | /** @var VariablesManager */ |
19 | private $varManager; |
20 | |
21 | /** @var BlobStoreFactory */ |
22 | private $blobStoreFactory; |
23 | |
24 | /** @var BlobStore */ |
25 | private $blobStore; |
26 | |
27 | /** @var string|null */ |
28 | private $centralDB; |
29 | |
30 | /** |
31 | * @param VariablesManager $varManager |
32 | * @param BlobStoreFactory $blobStoreFactory |
33 | * @param BlobStore $blobStore |
34 | * @param string|null $centralDB |
35 | */ |
36 | public function __construct( |
37 | VariablesManager $varManager, |
38 | BlobStoreFactory $blobStoreFactory, |
39 | BlobStore $blobStore, |
40 | ?string $centralDB |
41 | ) { |
42 | $this->varManager = $varManager; |
43 | $this->blobStoreFactory = $blobStoreFactory; |
44 | $this->blobStore = $blobStore; |
45 | $this->centralDB = $centralDB; |
46 | } |
47 | |
48 | /** |
49 | * Store a var dump to a BlobStore. |
50 | * |
51 | * @param VariableHolder $varsHolder |
52 | * @param bool $global |
53 | * |
54 | * @return string Address of the record |
55 | */ |
56 | public function storeVarDump( VariableHolder $varsHolder, $global = false ) { |
57 | // Get all variables yet set and compute old and new wikitext if not yet done |
58 | // as those are needed for the diff view on top of the abuse log pages |
59 | $vars = $this->varManager->dumpAllVars( $varsHolder, [ 'old_wikitext', 'new_wikitext' ] ); |
60 | |
61 | // if user_unnamed_ip exists it can't be saved, as var dump blobs are stored in an append-only |
62 | // database and stored IPs eventually need to be cleared. |
63 | // Set the value to something safe here, as by now it's been used in the filter and if |
64 | // logs later need it, it can be reconstructed from afl_ip. |
65 | if ( isset( $vars[ 'user_unnamed_ip' ] ) && $vars[ 'user_unnamed_ip' ] ) { |
66 | $vars[ 'user_unnamed_ip' ] = true; |
67 | } |
68 | |
69 | // Vars is an array with native PHP data types (non-objects) now |
70 | $text = FormatJson::encode( $vars ); |
71 | |
72 | $dbDomain = $global ? $this->centralDB : false; |
73 | $blobStore = $this->blobStoreFactory->newBlobStore( $dbDomain ); |
74 | |
75 | $hints = [ |
76 | BlobStore::DESIGNATION_HINT => 'AbuseFilter', |
77 | BlobStore::MODEL_HINT => 'AbuseFilter', |
78 | ]; |
79 | return $blobStore->storeBlob( $text, $hints ); |
80 | } |
81 | |
82 | /** |
83 | * Retrieve a var dump from a BlobStore. |
84 | * |
85 | * The entire $row is passed through but only the following columns are actually required: |
86 | * - afl_var_dump: the main variable store to load |
87 | * - afl_ip: the IP value to use if necessary |
88 | * |
89 | * @param stdClass $row |
90 | * |
91 | * @return VariableHolder |
92 | */ |
93 | public function loadVarDump( stdClass $row ): VariableHolder { |
94 | if ( !isset( $row->afl_var_dump ) || !isset( $row->afl_ip ) ) { |
95 | throw new InvalidArgumentException( 'Both afl_var_dump and afl_ip must be set' ); |
96 | } |
97 | |
98 | try { |
99 | $varDump = $row->afl_var_dump; |
100 | $blob = $this->blobStore->getBlob( $varDump ); |
101 | } catch ( BlobAccessException $ex ) { |
102 | return new VariableHolder; |
103 | } |
104 | |
105 | $vars = FormatJson::decode( $blob, true ); |
106 | $obj = VariableHolder::newFromArray( $vars ); |
107 | $this->varManager->translateDeprecatedVars( $obj ); |
108 | |
109 | // If user_unnamed_ip was set when afl_var_dump was saved, it was saved as a visibility boolean |
110 | // and needs to be translated back into an IP |
111 | // user_unnamed_ip uses afl_ip instead of saving the value because afl_ip gets purged and the blob |
112 | // that contains user_unnamed_ip can't be modified |
113 | if ( |
114 | $this->varManager->getVar( $obj, 'user_unnamed_ip', $this->varManager::GET_LAX )->toNative() |
115 | ) { |
116 | $obj->setVar( 'user_unnamed_ip', $row->afl_ip ); |
117 | } |
118 | |
119 | return $obj; |
120 | } |
121 | } |