Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.67% covered (success)
91.67%
44 / 48
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
IPoidInfoRetriever
91.67% covered (success)
91.67%
44 / 48
50.00% covered (danger)
50.00%
2 / 4
9.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 retrieveFromIP
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
2
 getData
81.25% covered (warning)
81.25%
13 / 16
0.00% covered (danger)
0.00%
0 / 1
5.16
1<?php
2
3namespace MediaWiki\IPInfo\InfoRetriever;
4
5use MediaWiki\Config\ServiceOptions;
6use MediaWiki\Http\HttpRequestFactory;
7use MediaWiki\IPInfo\Info\IPoidInfo;
8use Psr\Log\LoggerInterface;
9use Wikimedia\IPUtils;
10
11/**
12 * Manager for getting information from the iPoid service.
13 */
14class IPoidInfoRetriever implements InfoRetriever {
15    /**
16     * @internal For use by ServiceWiring
17     */
18    public const CONSTRUCTOR_OPTIONS = [
19        'IPInfoIpoidUrl',
20    ];
21
22    private ServiceOptions $options;
23
24    private HttpRequestFactory $httpRequestFactory;
25
26    private LoggerInterface $logger;
27
28    /**
29     * @param ServiceOptions $options
30     * @param HttpRequestFactory $httpRequestFactory
31     * @param LoggerInterface $logger
32     */
33    public function __construct(
34        ServiceOptions $options,
35        HttpRequestFactory $httpRequestFactory,
36        LoggerInterface $logger
37    ) {
38        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
39        $this->options = $options;
40        $this->httpRequestFactory = $httpRequestFactory;
41        $this->logger = $logger;
42    }
43
44    /** @inheritDoc */
45    public function getName(): string {
46        return 'ipinfo-source-ipoid';
47    }
48
49    /**
50     * @inheritDoc
51     * @return IPoidInfo
52     */
53    public function retrieveFromIP( string $ip ): IPoidInfo {
54        $info = array_fill_keys(
55            [
56                'behaviors',
57                'risks',
58                'connectionTypes',
59                'tunnelOperators',
60                'proxies',
61                'numUsersOnThisIP',
62            ],
63            null
64        );
65
66        if ( $this->options->get( 'IPInfoIpoidUrl' ) ) {
67            $data = $this->getData( $ip );
68
69            $info['behaviors'] = $data['behaviors'] ?? null;
70            $info['risks'] = $data['risks'] ?? null;
71            $info['connectionTypes'] = $data['types'] ?? null;
72            $info['tunnelOperators'] = $data['tunnels'] ?? null;
73            $info['proxies'] = $data['proxies'] ?? null;
74            $info['numUsersOnThisIP'] = $data['client_count'] ?? null;
75        }
76
77        return new IPoidInfo(
78            $info['behaviors'],
79            $info['risks'],
80            $info['connectionTypes'],
81            $info['tunnelOperators'],
82            $info['proxies'],
83            $info['numUsersOnThisIP'],
84        );
85    }
86
87    /**
88     * Call the iPoid API to get data for an IP address.
89     *
90     * @param string $ip
91     * @return mixed[] Data returned by iPoid
92     */
93    private function getData( string $ip ) {
94        $data = [];
95
96        $baseUrl = $this->options->get( 'IPInfoIpoidUrl' );
97        $url = $baseUrl . '/feed/v1/ip/' . $ip;
98        $request = $this->httpRequestFactory->create( $url, [ 'method' => 'GET' ] );
99        $response = $request->execute();
100
101        if ( $response->isOK() ) {
102            $content = json_decode( $request->getContent(), true );
103            if ( is_array( $content ) ) {
104                $sanitizedIp = IPUtils::sanitizeIP( $ip );
105                foreach ( $content as $key => $value ) {
106                    if ( $sanitizedIp === IPUtils::sanitizeIP( $key ) ) {
107                        $data = $value;
108                    }
109                }
110            } else {
111                $this->logger->debug(
112                    "ipoid results were not in the expected format: " . $request->getContent()
113                );
114            }
115        }
116
117        return $data;
118    }
119
120}