Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
BlockUtils
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 2
132
0.00% covered (danger)
0.00%
0 / 1
 getBlockErrorMsgs
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 logBlockedEditAttempt
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2
3namespace WikimediaEvents;
4
5use MediaWiki\Block\Block;
6use MediaWiki\Extension\EventLogging\EventLogging;
7use MediaWiki\MediaWikiServices;
8use MediaWiki\Title\Title;
9use MediaWiki\User\User;
10use Message;
11use RequestContext;
12
13class BlockUtils {
14    // Possible block error keys from Block\BlockErrorFormatter::getBlockErrorMessageKey()
15    public const LOCAL_ERROR_KEYS = [
16        'blockedtext',
17        'autoblockedtext',
18        'blockedtext-partial',
19        'systemblockedtext',
20        'blockedtext-composite'
21    ];
22    // Possible block error keys from GlobalBlocking extension GlobalBlocking::getUserBlockDetails()
23    public const GLOBAL_ERROR_KEYS = [
24        'globalblocking-ipblocked',
25        'globalblocking-ipblocked-range',
26        'globalblocking-ipblocked-xff',
27        // WikimediaMessages versions
28        'wikimedia-globalblocking-ipblocked',
29        'wikimedia-globalblocking-ipblocked-range',
30        'wikimedia-globalblocking-ipblocked-xff',
31    ];
32
33    /**
34     * Build error messages for error keys
35     *
36     * @param array[] $errors from PermissionManager getPermissionErrors
37     * @return array<string, Message[]>
38     */
39    public static function getBlockErrorMsgs( $errors ) {
40        $blockedErrorMsgs = $globalBlockedErrorMsgs = [];
41        foreach ( $errors as $error ) {
42            $errorMsg = Message::newFromSpecifier( $error );
43            $errorKey = $errorMsg->getKey();
44            if ( in_array( $errorKey, self::LOCAL_ERROR_KEYS, true ) ) {
45                $blockedErrorMsgs[] = $errorMsg;
46            } elseif ( in_array( $errorKey, self::GLOBAL_ERROR_KEYS, true ) ) {
47                $globalBlockedErrorMsgs[] = $errorMsg;
48            }
49        }
50        $allErrorMsgs = array_merge( $blockedErrorMsgs, $globalBlockedErrorMsgs );
51
52        return [
53            'local' => $blockedErrorMsgs,
54            'global' => $globalBlockedErrorMsgs,
55            'all' => $allErrorMsgs,
56        ];
57    }
58
59    /**
60     * Log a blocked edit attempt
61     *
62     * @param User $user
63     * @param Title $title
64     * @param string $interface
65     * @param string $platform
66     */
67    public static function logBlockedEditAttempt( $user, $title, $interface, $platform ) {
68        global $wgDBname;
69
70        // Prefer the local block over the global one if both are set. This is
71        // somewhat arbitrary, but is consistent with account creation block
72        // logging.
73        $local = MediaWikiServices::getInstance()->getPermissionManager()->isBlockedFrom( $user, $title, false );
74        if ( $local ) {
75            $block = $user->getBlock();
76        } else {
77            $block = $user->getGlobalBlock();
78        }
79
80        if ( !$block ) {
81            return;
82        }
83
84        $rawExpiry = $block->getExpiry();
85        if ( wfIsInfinity( $rawExpiry ) ) {
86            $expiry = 'infinity';
87        } else {
88            $expiry = wfTimestamp( TS_ISO_8601, $rawExpiry );
89        }
90
91        $request = RequestContext::getMain()->getRequest();
92        // Avoid accessing the service and its dependencies if we can by checking
93        // first if we can get the country code from the GeoIP cookie.
94        $countryCode = WikimediaEventsCountryCodeLookup::getFromCookie( $request );
95        if ( !$countryCode ) {
96            /** @var WikimediaEventsCountryCodeLookup $countryCodeLookup */
97            $countryCodeLookup = MediaWikiServices::getInstance()->get( 'WikimediaEventsCountryCodeLookup' );
98            $countryCode = $countryCodeLookup->getFromGeoIP( $request );
99        }
100
101        $event = [
102            '$schema' => '/analytics/mediawiki/editattemptsblocked/1.0.0',
103            'block_id' => json_encode( $block->getIdentifier() ),
104            // @phan-suppress-next-line PhanTypeMismatchDimFetchNullable
105            'block_type' => Block::BLOCK_TYPES[ $block->getType() ] ?? 'other',
106            'block_expiry' => $expiry,
107            'block_scope' => $local ? 'local' : 'global',
108            'platform' => $platform,
109            'interface' => $interface,
110            'country_code' => WikimediaEventsCountryCodeLookup::getCountryCodeFormattedForEvent( $countryCode ),
111            // http.client_ip is handled by eventgate-wikimedia
112            'database' => $wgDBname,
113            'page_id' => $title->getId(),
114            'page_namespace' => $title->getNamespace(),
115            'rev_id' => $title->getLatestRevID(),
116            'performer' => [
117                'user_id' => $user->getId(),
118                'user_edit_count' => $user->getEditCount() ?: 0,
119            ],
120        ];
121        EventLogging::submit( 'mediawiki.editattempt_block', $event );
122    }
123
124}