1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
18 *
19 * @file
20 */
21
22namespace MediaWiki\Block;
23
24use MediaWiki\Config\ServiceOptions;
25use MediaWiki\Permissions\Authority;
26use MediaWiki\User\UserFactory;
27use MediaWiki\User\UserIdentity;
28
29/**
30 * Block permissions
31 *
32 * This class is responsible for making sure a user has permission to block
33 *
34 * This class is usable for both blocking as well as
35 * the unblocking process.
36 *
37 * @since 1.35
38 */
39class BlockPermissionChecker {
40	/**
41	 * @var UserIdentity|string|null Block target or null when unknown
42	 */
43	private $target;
44
45	/**
46	 * @var int|null One of AbstractBlock::TYPE_* constants, or null when unknown
47	 */
48	private $targetType = null;
49
50	/**
51	 * @var Authority Block performer
52	 */
53	private $performer;
54
55	/**
56	 * @internal only for use by ServiceWiring and BlockPermissionCheckerFactory
57	 */
58	public const CONSTRUCTOR_OPTIONS = [
59		'EnableUserEmail',
60	];
61
62	/** @var ServiceOptions */
63	private $options;
64
65	/** @var UserFactory */
66	private $userFactory;
67
68	/**
69	 * @param ServiceOptions $options
70	 * @param BlockUtils $blockUtils
71	 * @param UserFactory $userFactory
72	 * @param UserIdentity|string|null $target
73	 * @param Authority $performer
74	 */
75	public function __construct(
76		ServiceOptions $options,
77		BlockUtils $blockUtils,
78		UserFactory $userFactory,
79		$target,
80		Authority $performer
81	) {
82		$options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
83		$this->options = $options;
84		$this->userFactory = $userFactory;
85		list( $this->target, $this->targetType ) = $blockUtils->parseBlockTarget( $target );
86		$this->performer = $performer;
87	}
88
89	/**
90	 * Check base permission that apply to either block or unblock
91	 *
92	 * @since 1.36
93	 * @param bool $checkHideuser
94	 * @return bool|string
95	 */
96	public function checkBasePermissions( $checkHideuser = false ) {
97		if ( !$this->performer->isAllowed( 'block' ) ) {
98			return 'badaccess-group0';
99		}
100
101		if (
102			$checkHideuser &&
103			!$this->performer->isAllowed( 'hideuser' )
104		) {
105			return 'unblock-hideuser';
106		}
107
108		return true;
109	}
110
111	/**
112	 * Checks block-related permissions (doesn't check any other permissions)
113	 *
114	 * T17810: Sitewide blocked admins should not be able to block/unblock
115	 * others with one exception; they can block the user who blocked them,
116	 * to reduce advantage of a malicious account blocking all admins (T150826).
117	 *
118	 * T208965: Partially blocked admins can block and unblock others as normal.
119	 *
120	 * @return bool|string True when checks passed, message code for failures
121	 */
122	public function checkBlockPermissions() {
123		$performerIdentity = $this->performer->getUser();
124		$legacyUser = $this->userFactory->newFromUserIdentity( $performerIdentity );
125
126		$block = $legacyUser->getBlock();
127		if ( !$block ) {
128			// User is not blocked, process as normal
129			return true;
130		}
131
132		if ( !$block->isSitewide() ) {
133			// T208965: Partially blocked admins should have full access
134			return true;
135		}
136
137		if (
138			$this->target instanceof UserIdentity &&
139			$this->target->getId() === $performerIdentity->getId()
140		) {
141			// Blocked admin is trying to alter their own block
142
143			// Self-blocked admins can always remove or alter their block
144			if ( $block->getByName() === $performerIdentity->getName() ) {
145				return true;
146			}
147
148			// Users with 'unblockself' right can unblock themselves or alter their own block
149			if ( $this->performer->isAllowed( 'unblockself' ) ) {
150				return true;
151			} else {
152				return 'ipbnounblockself';
153			}
154		}
155
156		if (
157			$this->target instanceof UserIdentity &&
158			$block->getByName() === $this->target->getName()
159		) {
160			// T150826: Blocked admins can always block the admin who blocked them
161			return true;
162		}
163
164		// User is blocked and no exception took effect
165		return 'ipbblocked';
166	}
167
168	/**
169	 * Check permission to block emailing
170	 *
171	 * @since 1.36
172	 * @return bool
173	 */
174	public function checkEmailPermissions() {
175		return $this->options->get( 'EnableUserEmail' ) &&
176			$this->performer->isAllowed( 'blockemail' );
177	}
178}
179