Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
BlockedDomainConfigProvider
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 8
342
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 loadConfig
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 processStoreStatus
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 loadValidConfiguration
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 loadValidConfigurationUncached
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 loadComputed
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 addDomain
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 removeDomain
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\BlockedDomains;
4
5use MediaWiki\Extension\CommunityConfiguration\Provider\AbstractProvider;
6use MediaWiki\Extension\CommunityConfiguration\Provider\ProviderServicesContainer;
7use MediaWiki\Extension\CommunityConfiguration\Store\IConfigurationStore;
8use MediaWiki\Extension\CommunityConfiguration\Validation\IValidator;
9use MediaWiki\Message\Message;
10use MediaWiki\Permissions\Authority;
11use StatusValue;
12use Wikimedia\ObjectCache\BagOStuff;
13
14class BlockedDomainConfigProvider extends AbstractProvider implements IBlockedDomainStorage {
15
16    public const PROVIDER_ID = 'BlockedDomain';
17
18    public function __construct(
19        ProviderServicesContainer $providerServicesContainer,
20        string $providerId,
21        array $options,
22        IConfigurationStore $store,
23        IValidator $validator,
24        private readonly BagOStuff $cache,
25        private readonly BlockedDomainValidator $domainValidator
26    ) {
27        parent::__construct( $providerServicesContainer, $providerId, $options, $store, $validator );
28    }
29
30    /** @inheritDoc */
31    public function loadConfig( int $flags = 0 ): StatusValue {
32        if ( $flags === 0 ) {
33            return $this->loadValidConfiguration();
34        } else {
35            return $this->loadValidConfigurationUncached();
36        }
37    }
38
39    private function processStoreStatus( StatusValue $value ): StatusValue {
40        if ( !$value->isOK() ) {
41            return $value;
42        }
43        return $value->setResult( true, wfObjectToArray( $value->getValue() ) );
44    }
45
46    public function loadValidConfiguration(): StatusValue {
47        return $this->processStoreStatus( $this->getStore()->loadConfiguration() );
48    }
49
50    public function loadValidConfigurationUncached(): StatusValue {
51        return $this->processStoreStatus( $this->getStore()->loadConfigurationUncached() );
52    }
53
54    /**
55     * Load the computed domain blocklist
56     *
57     * @return array<string,true> Flipped for performance reasons
58     */
59    public function loadComputed(): array {
60        return $this->cache->getWithSetCallback(
61            $this->cache->makeKey( 'abusefilter-blockeddomains-computed' ),
62            BagOStuff::TTL_MINUTE * 5,
63            function () {
64                $status = $this->loadValidConfiguration();
65                if ( !$status->isGood() ) {
66                    return [];
67                }
68                $computedDomains = [];
69                foreach ( $status->getValue() as $domain ) {
70                    if ( !( $domain['domain'] ?? null ) ) {
71                        continue;
72                    }
73                    $validatedDomain = $this->domainValidator->validateDomain( $domain['domain'] );
74                    if ( $validatedDomain ) {
75                        // It should be a map, benchmark at https://phabricator.wikimedia.org/P48956
76                        $computedDomains[$validatedDomain] = true;
77                    }
78                }
79                return $computedDomains;
80            }
81        );
82    }
83
84    /**
85     * This doesn't do validation.
86     *
87     * @param string $domain domain to be blocked
88     * @param string $notes User provided notes
89     * @param Authority $authority Performer
90     *
91     * @return StatusValue
92     */
93    public function addDomain( string $domain, string $notes, Authority $authority ): StatusValue {
94        $config = $this->loadValidConfigurationUncached();
95        if ( !$config->isGood() ) {
96            return $config;
97        }
98        $config = $config->getValue();
99        $config[] = [ 'domain' => $domain, 'notes' => $notes, 'addedBy' => $authority->getUser()->getName() ];
100        $comment = Message::newFromSpecifier( 'abusefilter-blocked-domains-domain-added-comment' )
101            ->params( $domain, $notes )
102            ->plain();
103        return $this->storeValidConfiguration( $config, $authority, $comment );
104    }
105
106    /**
107     * This doesn't do validation.
108     *
109     * @param string $domain domain to be unblocked
110     * @param string $notes User provided notes
111     * @param Authority $authority Performer
112     *
113     * @return StatusValue
114     */
115    public function removeDomain( string $domain, string $notes, Authority $authority ): StatusValue {
116        $config = $this->loadValidConfigurationUncached();
117        if ( !$config->isGood() ) {
118            return $config;
119        }
120
121        $config = $config->getValue();
122        foreach ( $config as $key => $value ) {
123            if ( ( $value['domain'] ?? '' ) == $domain ) {
124                unset( $config[$key] );
125            }
126        }
127
128        $comment = Message::newFromSpecifier( 'abusefilter-blocked-domains-domain-removed-comment' )
129            ->params( $domain, $notes )
130            ->plain();
131        return $this->storeValidConfiguration( array_values( $config ), $authority, $comment );
132    }
133}