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