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