Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.81% covered (success)
98.81%
83 / 84
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
DefaultPresenter
98.81% covered (success)
98.81%
83 / 84
83.33% covered (warning)
83.33%
5 / 6
20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 present
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
11
 presentInfo
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
4
 presentIPoidInfo
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 presentBlockInfo
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 presentContributionInfo
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
2.01
1<?php
2
3namespace MediaWiki\IPInfo\Rest\Presenter;
4
5use MediaWiki\IPInfo\AccessLevelTrait;
6use MediaWiki\IPInfo\Info\BlockInfo;
7use MediaWiki\IPInfo\Info\ContributionInfo;
8use MediaWiki\IPInfo\Info\Info;
9use MediaWiki\IPInfo\Info\IPCountInfo;
10use MediaWiki\IPInfo\Info\IPoidInfo;
11use MediaWiki\IPInfo\Info\IPVersionInfo;
12use MediaWiki\IPInfo\Info\Location;
13use MediaWiki\Permissions\PermissionManager;
14use MediaWiki\User\UserIdentity;
15use Wikimedia\Assert\Assert;
16
17class DefaultPresenter {
18    use AccessLevelTrait;
19
20    private PermissionManager $permissionManager;
21
22    public const IPINFO_VIEW_BASIC_RIGHT = 'ipinfo-view-basic';
23    public const IPINFO_VIEW_FULL_RIGHT = 'ipinfo-view-full';
24
25    private const IPINFO_VIEW_BASIC = [
26        'connectionType',
27        'countryNames',
28        'numActiveBlocks',
29        'numLocalEdits',
30        'numRecentEdits',
31        'proxyType',
32        'userType',
33        'version',
34    ];
35
36    private const IPINFO_VIEW_FULL = [
37        'asn',
38        'behaviors',
39        'connectionType',
40        'connectionTypes',
41        'countryNames',
42        'isp',
43        'location',
44        'numActiveBlocks',
45        'numDeletedEdits',
46        'numLocalEdits',
47        'numRecentEdits',
48        'numUsersOnThisIP',
49        'numIPAddresses',
50        'organization',
51        'proxies',
52        'proxyType',
53        'risks',
54        'tunnelOperators',
55        'userType',
56        'version',
57    ];
58
59    /**
60     * The viewing privileges of each level.
61     *
62     * Each describes themselves independently of one another.
63     */
64    private const VIEWING_RIGHTS = [
65        self::IPINFO_VIEW_BASIC_RIGHT => self::IPINFO_VIEW_BASIC,
66        self::IPINFO_VIEW_FULL_RIGHT => self::IPINFO_VIEW_FULL,
67    ];
68
69    public function __construct(
70        PermissionManager $permissionManager
71    ) {
72        $this->permissionManager = $permissionManager;
73    }
74
75    /**
76     * @param array $info The output of {@link MediaWiki\IPInfo\InfoManager::retrieveFor()}
77     * @param UserIdentity $user User performing the request
78     * @return array
79     */
80    public function present( array $info, UserIdentity $user ): array {
81        Assert::parameterElementType(
82            [
83                Info::class,
84                IPCountInfo::class,
85                IPoidInfo::class,
86                BlockInfo::class,
87                ContributionInfo::class,
88                IPVersionInfo::class,
89            ],
90            $info['data'],
91            "info['data']"
92        );
93
94        $result = [
95            'subject' => $info['subject'],
96            'data' => [],
97        ];
98
99        // Get the highest access list of properties user has permissions for
100        $level = $this->highestAccessLevel( $this->permissionManager->getUserPermissions( $user ) );
101        $viewableProperties = $level ? self::VIEWING_RIGHTS[$level] : [];
102
103        foreach ( $info['data'] as $source => $itemInfo ) {
104            $data = [];
105
106            if ( $itemInfo instanceof Info ) {
107                $data += $this->presentInfo( $itemInfo );
108            } elseif ( $itemInfo instanceof IPCountInfo ) {
109                $data += [ 'numIPAddresses' => $itemInfo->getCount() ];
110            } elseif ( $itemInfo instanceof IPoidInfo ) {
111                $data += $this->presentIPoidInfo( $itemInfo );
112            } elseif ( $itemInfo instanceof BlockInfo ) {
113                $data += $this->presentBlockInfo( $itemInfo );
114            } elseif ( $itemInfo instanceof ContributionInfo ) {
115                $data += $this->presentContributionInfo( $itemInfo, $user );
116            } elseif ( $itemInfo instanceof IPVersionInfo ) {
117                $data += [ 'version' => $itemInfo->getVersion() ];
118            }
119
120            // Unset all properties the user doesn't have access to before writing to $result
121            foreach ( $data as $datum => $value ) {
122                if ( !in_array( $datum, $viewableProperties ) ) {
123                    unset( $data[$datum] );
124                }
125            }
126
127            $result['data'][$source] = $data;
128        }
129
130        return $result;
131    }
132
133    /**
134     * Converts an instance of `\MediaWiki\IPInfo\Info\Info` to an array.
135     *
136     * @param Info $info
137     * @return array<string,mixed>
138     */
139    private function presentInfo( Info $info ): array {
140        $coordinates = $info->getCoordinates();
141        $proxyType = $info->getProxyType();
142        $location = $info->getLocation();
143
144        return [
145            'coordinates' => $coordinates ? [
146                'latitude' => $coordinates->getLatitude(),
147                'longitude' => $coordinates->getLongitude(),
148            ] : null,
149            'asn' => $info->getAsn(),
150            'organization' => $info->getOrganization(),
151            'countryNames' => $info->getCountryNames(),
152            'location' => $location ? array_map( static function ( Location $location ) {
153                return [
154                    'id' => $location->getId(),
155                    'label' => $location->getLabel(),
156                ];
157            }, $location ) : null,
158            'isp' => $info->getIsp(),
159            'connectionType' => $info->getConnectionType(),
160            'userType' => $info->getUserType(),
161            'proxyType' => $proxyType ? [
162                'isAnonymousVpn' => $proxyType->isAnonymousVpn(),
163                'isPublicProxy' => $proxyType->isPublicProxy(),
164                'isResidentialProxy' => $proxyType->isResidentialProxy(),
165                'isLegitimateProxy' => $proxyType->isLegitimateProxy(),
166                'isTorExitNode' => $proxyType->isTorExitNode(),
167                'isHostingProvider' => $proxyType->isHostingProvider(),
168
169            ] : null,
170        ];
171    }
172
173    /**
174     * @param IPoidInfo $info
175     * @return array<string,mixed>
176     */
177    private function presentIPoidInfo( IPoidInfo $info ): array {
178        return [
179            'behaviors' => $info->getBehaviors(),
180            'risks' => $info->getRisks(),
181            'connectionTypes' => $info->getConnectionTypes(),
182            'tunnelOperators' => $info->getTunnelOperators(),
183            'proxies' => $info->getProxies(),
184            'numUsersOnThisIP' => $info->getNumUsersOnThisIP(),
185        ];
186    }
187
188    /**
189     * @param BlockInfo $info
190     * @return array<string,int>
191     */
192    private function presentBlockInfo( BlockInfo $info ): array {
193        return [
194            'numActiveBlocks' => $info->getNumActiveBlocks(),
195        ];
196    }
197
198    /**
199     * @param ContributionInfo $info
200     * @param UserIdentity $user
201     * @return array<string,int>
202     */
203    private function presentContributionInfo( ContributionInfo $info, UserIdentity $user ): array {
204        $contributionInfo = [
205            'numLocalEdits' => $info->getNumLocalEdits(),
206            'numRecentEdits' => $info->getNumRecentEdits(),
207        ];
208        if ( $this->permissionManager->userHasRight(
209             $user, 'deletedhistory' ) ) {
210            $contributionInfo['numDeletedEdits'] = $info->getNumDeletedEdits();
211        }
212        return $contributionInfo;
213    }
214}