Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.69% covered (warning)
87.69%
57 / 65
46.67% covered (danger)
46.67%
7 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
RealTempUserConfig
87.69% covered (warning)
87.69%
57 / 65
46.67% covered (danger)
46.67%
7 / 15
41.84
0.00% covered (danger)
0.00%
0 / 1
 __construct
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
6.01
 isEnabled
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\AndExpressionGroup;
10use Wikimedia\Rdbms\IExpression;
11use Wikimedia\Rdbms\IReadableDatabase;
12use Wikimedia\Rdbms\OrExpressionGroup;
13
14/**
15 * The real TempUserConfig including internal methods used by TempUserCreator.
16 *
17 * @since 1.39
18 */
19class RealTempUserConfig implements TempUserConfig {
20    /** @var bool */
21    private $enabled = false;
22
23    /** @var array */
24    private $serialProviderConfig = [];
25
26    /** @var array */
27    private $serialMappingConfig = [];
28
29    /** @var string[] */
30    private $autoCreateActions;
31
32    /** @var Pattern|null */
33    private $genPattern;
34
35    /** @var Pattern[]|null */
36    private $matchPatterns;
37
38    /** @var Pattern|null */
39    private $reservedPattern;
40
41    /** @var int|null */
42    private $expireAfterDays;
43
44    /** @var int|null */
45    private $notifyBeforeExpirationDays;
46
47    /**
48     * @param array $config See the documentation of $wgAutoCreateTempUser.
49     *   - enabled: bool
50     *   - actions: array
51     *   - genPattern: string
52     *   - matchPattern: string|string[], optional
53     *   - reservedPattern: string, optional
54     *   - serialProvider: array
55     *   - serialMapping: array
56     *   - expireAfterDays: int, optional
57     *   - notifyBeforeExpirationDays: int, optional
58     */
59    public function __construct( $config ) {
60        if ( $config['enabled'] ?? false ) {
61            $this->enabled = true;
62            $this->autoCreateActions = $config['actions'];
63            $this->genPattern = new Pattern( 'genPattern', $config['genPattern'] );
64            if ( isset( $config['matchPattern'] ) ) {
65                $matchPatterns = $config['matchPattern'];
66                if ( !is_array( $config['matchPattern'] ) ) {
67                    $matchPatterns = [ $matchPatterns ];
68                }
69                foreach ( $matchPatterns as &$pattern ) {
70                    $pattern = new Pattern( 'matchPattern', $pattern );
71                }
72                $this->matchPatterns = $matchPatterns;
73            } else {
74                $this->matchPatterns = [ $this->genPattern ];
75            }
76            $this->serialProviderConfig = $config['serialProvider'];
77            $this->serialMappingConfig = $config['serialMapping'];
78            $this->expireAfterDays = $config['expireAfterDays'] ?? null;
79            $this->notifyBeforeExpirationDays = $config['notifyBeforeExpirationDays'] ?? null;
80        }
81        if ( isset( $config['reservedPattern'] ) ) {
82            $this->reservedPattern = new Pattern( 'reservedPattern', $config['reservedPattern'] );
83        }
84    }
85
86    public function isEnabled() {
87        return $this->enabled;
88    }
89
90    public function isAutoCreateAction( string $action ) {
91        if ( $action === 'create' ) {
92            $action = 'edit';
93        }
94        return $this->isEnabled()
95            && in_array( $action, $this->autoCreateActions, true );
96    }
97
98    public function shouldAutoCreate( Authority $authority, string $action ) {
99        return $this->isAutoCreateAction( $action )
100            && !$authority->isRegistered()
101            && $authority->isAllowed( 'createaccount' );
102    }
103
104    public function isTempName( string $name ) {
105        if ( !$this->isEnabled() ) {
106            return false;
107        }
108        foreach ( $this->matchPatterns as $pattern ) {
109            if ( $pattern->isMatch( $name ) ) {
110                return true;
111            }
112        }
113        return false;
114    }
115
116    public function isReservedName( string $name ) {
117        return $this->isTempName( $name ) || ( $this->reservedPattern && $this->reservedPattern->isMatch( $name ) );
118    }
119
120    public function getPlaceholderName(): string {
121        $year = null;
122        if ( $this->serialProviderConfig['useYear'] ?? false ) {
123            $year = MWTimestamp::getInstance()->format( 'Y' );
124        }
125        if ( $this->isEnabled() ) {
126            return $this->genPattern->generate( '*', $year );
127        } else {
128            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
129        }
130    }
131
132    public function getMatchPattern(): Pattern {
133        wfDeprecated( __METHOD__, '1.42' );
134        if ( $this->isEnabled() ) {
135            // This method is deprecated to allow time for callers to update.
136            // This method only returns one Pattern, so just return the first one.
137            return $this->getMatchPatterns()[0];
138        } else {
139            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
140        }
141    }
142
143    public function getMatchPatterns(): array {
144        if ( $this->isEnabled() ) {
145            return $this->matchPatterns;
146        } else {
147            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
148        }
149    }
150
151    public function getMatchCondition( IReadableDatabase $db, string $field, string $op ): IExpression {
152        if ( $this->isEnabled() ) {
153            $exprs = [];
154            foreach ( $this->getMatchPatterns() as $pattern ) {
155                $exprs[] = $db->expr( $field, $op, $pattern->toLikeValue( $db ) );
156            }
157            if ( count( $exprs ) === 1 ) {
158                return $exprs[0];
159            }
160            if ( $op === IExpression::LIKE ) {
161                return new OrExpressionGroup( ...$exprs );
162            } elseif ( $op === IExpression::NOT_LIKE ) {
163                return new AndExpressionGroup( ...$exprs );
164            } else {
165                throw new InvalidArgumentException( "Invalid operator $op" );
166            }
167        } else {
168            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
169        }
170    }
171
172    public function getExpireAfterDays(): ?int {
173        return $this->expireAfterDays;
174    }
175
176    public function getNotifyBeforeExpirationDays(): ?int {
177        return $this->notifyBeforeExpirationDays;
178    }
179
180    /**
181     * @internal For TempUserCreator only
182     * @return Pattern
183     */
184    public function getGeneratorPattern(): Pattern {
185        if ( $this->isEnabled() ) {
186            return $this->genPattern;
187        } else {
188            throw new BadMethodCallException( __METHOD__ . ' is disabled' );
189        }
190    }
191
192    /**
193     * @internal For TempUserCreator only
194     * @return array
195     */
196    public function getSerialProviderConfig(): array {
197        return $this->serialProviderConfig;
198    }
199
200    /**
201     * @internal For TempUserCreator only
202     * @return array
203     */
204    public function getSerialMappingConfig(): array {
205        return $this->serialMappingConfig;
206    }
207}