Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.24% covered (warning)
88.24%
60 / 68
50.00% covered (danger)
50.00%
8 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
RealTempUserConfig
88.24% covered (warning)
88.24%
60 / 68
50.00% covered (danger)
50.00%
8 / 16
46.01
0.00% covered (danger)
0.00%
0 / 1
 __construct
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
9
 isEnabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isKnown
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAutoCreateAction
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 shouldAutoCreate
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 isTempName
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 isReservedName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 getPlaceholderName
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getMatchPattern
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getMatchPatterns
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getMatchCondition
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
6.02
 getExpireAfterDays
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNotifyBeforeExpirationDays
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGeneratorPattern
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getSerialProviderConfig
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSerialMappingConfig
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\User\TempUser;
4
5use BadMethodCallException;
6use InvalidArgumentException;
7use MediaWiki\Permissions\Authority;
8use MediaWiki\Utils\MWTimestamp;
9use Wikimedia\Rdbms\IExpression;
10use Wikimedia\Rdbms\IReadableDatabase;
11
12/**
13 * The real TempUserConfig including internal methods used by TempUserCreator.
14 *
15 * @since 1.39
16 */
17class RealTempUserConfig implements TempUserConfig {
18    /** @var bool */
19    private $known = false;
20
21    /** @var bool */
22    private $enabled = false;
23
24    /** @var array */
25    private $serialProviderConfig = [];
26
27    /** @var array */
28    private $serialMappingConfig = [];
29
30    /** @var string[] */
31    private $autoCreateActions;
32
33    /** @var Pattern|null */
34    private $genPattern;
35
36    /** @var Pattern[]|null */
37    private $matchPatterns;
38
39    /** @var Pattern|null */
40    private $reservedPattern;
41
42    /** @var int|null */
43    private $expireAfterDays;
44
45    /** @var int|null */
46    private $notifyBeforeExpirationDays;
47
48    /**
49     * @param array $config See the documentation of $wgAutoCreateTempUser.
50     *   - known: bool
51     *   - enabled: bool
52     *   - actions: array
53     *   - genPattern: string
54     *   - matchPattern: string|string[], optional
55     *   - reservedPattern: string, optional
56     *   - serialProvider: array
57     *   - serialMapping: array
58     *   - expireAfterDays: int, optional
59     *   - notifyBeforeExpirationDays: int, optional
60     */
61    public function __construct( $config ) {
62        $this->enabled = $config['enabled'] ?? false;
63        $this->known = $this->enabled || ( $config['known'] ?? false );
64
65        // Configuration related to creating new temporary accounts for some actions
66        if ( $this->enabled ) {
67            $this->autoCreateActions = $config['actions'];
68            $this->serialProviderConfig = $config['serialProvider'];
69            $this->serialMappingConfig = $config['serialMapping'];
70        }
71
72        // Configuration related to managing and identifying existing temporary accounts,
73        // regardless of whether new temp accounts are being actively created via the
74        // 'enabled' config flag.
75        if ( $this->known || $this->enabled ) {
76            $this->genPattern = new Pattern( 'genPattern', $config['genPattern'] );
77            $this->expireAfterDays = $config['expireAfterDays'] ?? null;
78            $this->notifyBeforeExpirationDays = $config['notifyBeforeExpirationDays'] ?? null;
79            if ( isset( $config['matchPattern'] ) ) {
80                $matchPatterns = $config['matchPattern'];
81                if ( !is_array( $config['matchPattern'] ) ) {
82                    $matchPatterns = [ $matchPatterns ];
83                }
84                foreach ( $matchPatterns as &$pattern ) {
85                    $pattern = new Pattern( 'matchPattern', $pattern );
86                }
87                $this->matchPatterns = $matchPatterns;
88            } else {
89                $this->matchPatterns = [ $this->genPattern ];
90            }
91        }
92
93        // Configuration that is set regardless of whether the feature is enabled or known.
94        if ( isset( $config['reservedPattern'] ) ) {
95            $this->reservedPattern = new Pattern( 'reservedPattern', $config['reservedPattern'] );
96        }
97    }
98
99    /** @inheritDoc */
100    public function isEnabled() {
101        return $this->enabled;
102    }
103
104    /** @inheritDoc */
105    public function isKnown() {
106        return $this->known;
107    }
108
109    /** @inheritDoc */
110    public function isAutoCreateAction( string $action ) {
111        if ( $action === 'create' ) {
112            $action = 'edit';
113        }
114        return $this->isEnabled()
115            && in_array( $action, $this->autoCreateActions, true );
116    }
117
118    /** @inheritDoc */
119    public function shouldAutoCreate( Authority $authority, string $action ) {
120        return $this->isAutoCreateAction( $action )
121            && !$authority->isRegistered()
122            && $authority->isAllowedAny( 'createaccount', 'autocreateaccount' );
123    }
124
125    /** @inheritDoc */
126    public function isTempName( string $name ) {
127        if ( !$this->isKnown() ) {
128            return false;
129        }
130        foreach ( $this->matchPatterns as $pattern ) {
131            if ( $pattern->isMatch( $name ) ) {
132                return true;
133            }
134        }
135        return false;
136    }
137
138    /** @inheritDoc */
139    public function isReservedName( string $name ) {
140        return $this->isTempName( $name ) || ( $this->reservedPattern && $this->reservedPattern->isMatch( $name ) );
141    }
142
143    /** @inheritDoc */
144    public function getPlaceholderName(): string {
145        $year = null;
146        if ( $this->serialProviderConfig['useYear'] ?? false ) {
147            $year = MWTimestamp::getInstance()->format( 'Y' );
148        }
149        if ( $this->isEnabled() ) {
150            return $this->genPattern->generate( '*', $year );
151        } else {
152            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
153        }
154    }
155
156    /**
157     * @deprecated since 1.42.
158     */
159    public function getMatchPattern(): Pattern {
160        wfDeprecated( __METHOD__, '1.42' );
161        if ( $this->isKnown() ) {
162            // This method is deprecated to allow time for callers to update.
163            // This method only returns one Pattern, so just return the first one.
164            return $this->getMatchPatterns()[0];
165        } else {
166            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
167        }
168    }
169
170    /** @inheritDoc */
171    public function getMatchPatterns(): array {
172        if ( $this->isKnown() ) {
173            return $this->matchPatterns;
174        } else {
175            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
176        }
177    }
178
179    /** @inheritDoc */
180    public function getMatchCondition( IReadableDatabase $db, string $field, string $op ): IExpression {
181        if ( $this->isKnown() ) {
182            $exprs = [];
183            foreach ( $this->getMatchPatterns() as $pattern ) {
184                $exprs[] = $db->expr( $field, $op, $pattern->toLikeValue( $db ) );
185            }
186            if ( count( $exprs ) === 1 ) {
187                return $exprs[0];
188            }
189            if ( $op === IExpression::LIKE ) {
190                return $db->orExpr( $exprs );
191            } elseif ( $op === IExpression::NOT_LIKE ) {
192                return $db->andExpr( $exprs );
193            } else {
194                throw new InvalidArgumentException( "Invalid operator $op" );
195            }
196        } else {
197            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
198        }
199    }
200
201    /** @inheritDoc */
202    public function getExpireAfterDays(): ?int {
203        return $this->expireAfterDays;
204    }
205
206    /** @inheritDoc */
207    public function getNotifyBeforeExpirationDays(): ?int {
208        return $this->notifyBeforeExpirationDays;
209    }
210
211    /**
212     * @internal For TempUserCreator only
213     * @return Pattern
214     */
215    public function getGeneratorPattern(): Pattern {
216        if ( $this->isEnabled() ) {
217            return $this->genPattern;
218        } else {
219            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
220        }
221    }
222
223    /**
224     * @internal For TempUserCreator only
225     * @return array
226     */
227    public function getSerialProviderConfig(): array {
228        return $this->serialProviderConfig;
229    }
230
231    /**
232     * @internal For TempUserCreator only
233     * @return array
234     */
235    public function getSerialMappingConfig(): array {
236        return $this->serialMappingConfig;
237    }
238}