Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.33% covered (success)
93.33%
56 / 60
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
LogHandler
93.33% covered (success)
93.33%
56 / 60
66.67% covered (warning)
66.67%
2 / 3
12.04
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 factory
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 getInfo
90.00% covered (success)
90.00%
36 / 40
0.00% covered (danger)
0.00%
0 / 1
10.10
1<?php
2
3namespace MediaWiki\IPInfo\Rest\Handler;
4
5use DatabaseLogEntry;
6use JobQueueGroup;
7use LogEventsList;
8use LogPage;
9use MediaWiki\IPInfo\InfoManager;
10use MediaWiki\IPInfo\Rest\Presenter\DefaultPresenter;
11use MediaWiki\Languages\LanguageFallback;
12use MediaWiki\Permissions\PermissionManager;
13use MediaWiki\Rest\LocalizedHttpException;
14use MediaWiki\User\Options\UserOptionsLookup;
15use MediaWiki\User\UserFactory;
16use Wikimedia\IPUtils;
17use Wikimedia\Message\MessageValue;
18use Wikimedia\Rdbms\IConnectionProvider;
19
20class LogHandler extends IPInfoHandler {
21
22    private IConnectionProvider $dbProvider;
23
24    public function __construct(
25        InfoManager $infoManager,
26        IConnectionProvider $dbProvider,
27        PermissionManager $permissionManager,
28        UserOptionsLookup $userOptionsLookup,
29        UserFactory $userFactory,
30        DefaultPresenter $presenter,
31        JobQueueGroup $jobQueueGroup,
32        LanguageFallback $languageFallback
33    ) {
34        parent::__construct(
35            $infoManager,
36            $permissionManager,
37            $userOptionsLookup,
38            $userFactory,
39            $presenter,
40            $jobQueueGroup,
41            $languageFallback,
42        );
43        $this->dbProvider = $dbProvider;
44    }
45
46    /**
47     * @param InfoManager $infoManager
48     * @param IConnectionProvider $dbProvider
49     * @param PermissionManager $permissionManager
50     * @param UserOptionsLookup $userOptionsLookup
51     * @param UserFactory $userFactory
52     * @param JobQueueGroup $jobQueueGroup
53     * @param LanguageFallback $languageFallback
54     * @return self
55     */
56    public static function factory(
57        InfoManager $infoManager,
58        IConnectionProvider $dbProvider,
59        PermissionManager $permissionManager,
60        UserOptionsLookup $userOptionsLookup,
61        UserFactory $userFactory,
62        JobQueueGroup $jobQueueGroup,
63        LanguageFallback $languageFallback
64    ) {
65        return new self(
66            $infoManager,
67            $dbProvider,
68            $permissionManager,
69            $userOptionsLookup,
70            $userFactory,
71            new DefaultPresenter( $permissionManager ),
72            $jobQueueGroup,
73            $languageFallback
74        );
75    }
76
77    /** @inheritDoc */
78    protected function getInfo( int $id ): array {
79        $dbr = $this->dbProvider->getReplicaDatabase();
80        $entry = DatabaseLogEntry::newFromId( $id, $dbr );
81
82        if ( !$entry ) {
83            throw new LocalizedHttpException(
84                new MessageValue( 'ipinfo-rest-log-nonexistent' ),
85                404
86            );
87        }
88
89        if ( !LogEventsList::userCanViewLogType( $entry->getType(), $this->getAuthority() ) ) {
90            throw new LocalizedHttpException(
91                new MessageValue( 'ipinfo-rest-log-denied' ),
92                403
93            );
94        }
95
96        // A log entry logs an action performed by a performer, on a target. Either the
97        // performer, or the target may be an IP address. This returns info about whichever is an
98        // IP address, or both, if both are IP addresses.
99        $canAccessPerformer = LogEventsList::userCanBitfield( $entry->getDeleted(), LogPage::DELETED_USER,
100            $this->getAuthority() );
101        $canAccessTarget = LogEventsList::userCanBitfield( $entry->getDeleted(), LogPage::DELETED_ACTION,
102            $this->getAuthority() );
103
104        // If the user cannot access the performer, nor the target, throw an error since there won't
105        // be anything to respond with.
106        if ( !$canAccessPerformer && !$canAccessTarget ) {
107            throw new LocalizedHttpException(
108                new MessageValue( 'ipinfo-rest-log-denied' ),
109                403
110            );
111        }
112
113        $performer = $entry->getPerformerIdentity()->getName();
114
115        // The target of a log entry may be an IP address. Targets are stored as titles.
116        $target = $entry->getTarget()->getText();
117
118        $info = [];
119        $showPerformer = IPUtils::isValid( $performer ) && $canAccessPerformer;
120        $showTarget = IPUtils::isValid( $target ) && $canAccessTarget;
121        if ( $showPerformer ) {
122            $info[] = $this->presenter->present(
123                $this->infoManager->retrieveFromIP( $performer ),
124                $this->getAuthority()->getUser()
125            );
126        }
127        if ( $showTarget ) {
128            $info[] = $this->presenter->present( $this->infoManager->retrieveFromIP( $target ),
129            $this->getAuthority()->getUser() );
130        }
131
132        if ( count( $info ) === 0 ) {
133            // Since the IP address only exists in CheckUser, there is no way to access it.
134            // @TODO Allow extensions (like CheckUser) to either pass without a value
135            //      (which would result in a 404) or throw a fatal (which could result in a 403).
136            throw new LocalizedHttpException(
137                new MessageValue( 'ipinfo-rest-log-registered' ),
138                404
139            );
140        }
141
142        return $info;
143    }
144}