94 $this->logger = LoggerFactory::getInstance(
'ratelimit' );
98 $this->options = $options;
99 $this->wrstatsFactory = $wrstatsFactory;
100 $this->centralIdLookup = $centralIdLookup;
101 $this->userFactory = $userFactory;
102 $this->userGroupManager = $userGroupManager;
103 $this->hookContainer = $hookContainer;
104 $this->hookRunner =
new HookRunner( $hookContainer );
186 if ( $this->nonLimitableActions[$action] ??
false ) {
191 $ip = $subject->
getIP();
195 $legacyUser = $this->userFactory->newFromUserIdentity( $user );
196 if ( !$this->hookRunner->onPingLimiter( $legacyUser, $action, $result, $incrBy ) ) {
197 $this->incrementStats(
"limit.$action.result." . ( $result ?
'tripped_by_hook' :
'passed_by_hook' ) );
201 if ( !isset( $this->rateLimits[$action] ) ) {
206 if ( $this->canBypass( $action ) && $this->
isExempt( $subject ) ) {
207 $this->incrementStats(
"limit.$action.result.exempt" );
211 $conds = $this->getConditions( $action );
212 $limiter = $this->wrstatsFactory->createRateLimiter( $conds, [
'limiter', $action ] );
213 $limitBatch = $limiter->createBatch( $incrBy );
214 $this->logger->debug( __METHOD__ .
": limiting $action rate for {$user->getName()}" );
216 $id = $user->getId();
217 $isNewbie = $subject->
is( RateLimitSubject::NEWBIE );
221 if ( isset( $conds[
'anon'] ) ) {
222 $limitBatch->localOp(
'anon', [] );
226 if ( isset( $conds[
'user-global'] ) ) {
228 $centralId = $this->centralIdLookup
229 ? $this->centralIdLookup->centralIdFromLocalUser( $user, CentralIdLookup::AUDIENCE_RAW )
234 $realm = $this->centralIdLookup->getProviderId();
235 $limitBatch->globalOp(
'user-global', [ $realm, $centralId ] );
238 $limitBatch->localOp(
'user-global', [
'local', $id ] );
243 if ( $isNewbie && $ip ) {
245 if ( isset( $conds[
'ip'] ) ) {
246 $limitBatch->globalOp(
'ip', $ip );
249 if ( isset( $conds[
'subnet'] ) ) {
250 $subnet = IPUtils::getSubnet( $ip );
251 if ( $subnet !==
false ) {
252 $limitBatch->globalOp(
'subnet', $subnet );
258 $userEntityType =
false;
259 if ( $id !== 0 && isset( $conds[
'user'] ) ) {
261 $userEntityType =
'user';
264 if ( $id !== 0 && $isNewbie && isset( $conds[
'newbie'] ) ) {
265 $userEntityType =
'newbie';
269 $userGroups = $this->userGroupManager->getUserGroups( $user );
270 foreach ( $userGroups as $group ) {
271 if ( isset( $conds[$group] ) ) {
272 if ( $userEntityType ===
false
273 || $conds[$group]->perSecond() > $conds[$userEntityType]->perSecond()
275 $userEntityType = $group;
282 if ( $userEntityType !==
false ) {
283 $limitBatch->localOp( $userEntityType, $id );
287 if ( isset( $conds[
'ip-all'] ) && $ip ) {
289 if ( $isNewbie || $userEntityType ===
false
290 || $conds[
'ip-all']->perSecond() > $conds[$userEntityType]->perSecond()
292 $limitBatch->globalOp(
'ip-all', $ip );
297 if ( isset( $conds[
'subnet-all'] ) && $ip ) {
298 $subnet = IPUtils::getSubnet( $ip );
299 if ( $subnet !==
false ) {
301 if ( $isNewbie || $userEntityType ===
false
302 || $conds[
'ip-all']->perSecond() > $conds[$userEntityType]->perSecond()
304 $limitBatch->globalOp(
'subnet-all', $subnet );
310 'name' => $user->getName(),
314 $batchResult = $limitBatch->tryIncr();
315 foreach ( $batchResult->getFailedResults() as $type => $result ) {
317 'User::pingLimiter: User tripped rate limit',
320 'limit' => $result->condition->limit,
321 'period' => $result->condition->window,
322 'count' => $result->prevTotal,
327 $this->incrementStats(
"limit.$action.tripped_by.$type" );
330 $allowed = $batchResult->isAllowed();
332 $this->incrementStats(
"limit.$action.result." . ( $allowed ?
'passed' :
'tripped' ) );