Completed
Pull Request — newinternal (#542)
by Simon
11:55 queued 02:00
created
includes/ExceptionHandler.php 1 patch
Indentation   +92 added lines, -92 removed lines patch added patch discarded remove patch
@@ -13,22 +13,22 @@  discard block
 block discarded – undo
13 13
 
14 14
 class ExceptionHandler
15 15
 {
16
-    /**
17
-     * Global exception handler
18
-     *
19
-     * Smarty would be nice to use, but it COULD BE smarty that throws the errors.
20
-     * Let's build something ourselves, and hope it works.
21
-     *
22
-     * @param $exception
23
-     *
24
-     * @category Security-Critical - has the potential to leak data when exception is thrown.
25
-     */
26
-    public static function exceptionHandler(Throwable $exception)
27
-    {
28
-        /** @global $siteConfiguration SiteConfiguration */
29
-        global $siteConfiguration;
30
-
31
-        $errorDocument = <<<HTML
16
+	/**
17
+	 * Global exception handler
18
+	 *
19
+	 * Smarty would be nice to use, but it COULD BE smarty that throws the errors.
20
+	 * Let's build something ourselves, and hope it works.
21
+	 *
22
+	 * @param $exception
23
+	 *
24
+	 * @category Security-Critical - has the potential to leak data when exception is thrown.
25
+	 */
26
+	public static function exceptionHandler(Throwable $exception)
27
+	{
28
+		/** @global $siteConfiguration SiteConfiguration */
29
+		global $siteConfiguration;
30
+
31
+		$errorDocument = <<<HTML
32 32
 <!DOCTYPE html>
33 33
 <html lang="en"><head>
34 34
 <meta charset="utf-8">
@@ -48,80 +48,80 @@  discard block
 block discarded – undo
48 48
 </div></body></html>
49 49
 HTML;
50 50
 
51
-        $errorData = self::getExceptionData($exception);
52
-        $errorData['server'] = $_SERVER;
53
-        $errorData['get'] = $_GET;
54
-        $errorData['post'] = $_POST;
55
-
56
-        $state = serialize($errorData);
57
-        $errorId = sha1($state);
58
-
59
-        // Save the error for later analysis
60
-        file_put_contents($siteConfiguration->getErrorLog() . '/' . $errorId . '.log', $state);
61
-
62
-        // clear and discard any content that's been saved to the output buffer
63
-        if (ob_get_level() > 0) {
64
-            ob_end_clean();
65
-        }
66
-
67
-        // push error ID into the document.
68
-        $message = str_replace('$1$', $errorId, $errorDocument);
69
-
70
-        if ($siteConfiguration->getDebuggingTraceEnabled()) {
71
-            ob_start();
72
-            var_dump($errorData);
73
-            $textErrorData = ob_get_contents();
74
-            ob_end_clean();
75
-
76
-            $message = str_replace('$2$', $textErrorData, $message);
77
-        }
78
-        else {
79
-            $message = str_replace('$2$', "", $message);
80
-        }
81
-
82
-        // While we *shouldn't* have sent headers by now due to the output buffering, PHPUnit does weird things.
83
-        // This is "only" needed for the tests, but it's a good idea to wrap this anyway.
84
-        if (!headers_sent()) {
85
-            header('HTTP/1.1 500 Internal Server Error');
86
-        }
87
-
88
-        // output the document
89
-        print $message;
90
-    }
91
-
92
-    /**
93
-     * @param int    $errorSeverity The severity level of the exception.
94
-     * @param string $errorMessage  The Exception message to throw.
95
-     * @param string $errorFile     The filename where the exception is thrown.
96
-     * @param int    $errorLine     The line number where the exception is thrown.
97
-     *
98
-     * @throws ErrorException
99
-     */
100
-    public static function errorHandler($errorSeverity, $errorMessage, $errorFile, $errorLine)
101
-    {
102
-        // call into the main exception handler above
103
-        throw new ErrorException($errorMessage, 0, $errorSeverity, $errorFile, $errorLine);
104
-    }
105
-
106
-    /**
107
-     * @param Throwable $exception
108
-     *
109
-     * @return null|array
110
-     */
111
-    public static function getExceptionData($exception)
112
-    {
113
-        if ($exception == null) {
114
-            return null;
115
-        }
116
-
117
-        $array = array(
118
-            'exception' => get_class($exception),
119
-            'message'   => $exception->getMessage(),
120
-            'stack'     => $exception->getTraceAsString(),
121
-        );
122
-
123
-        $array['previous'] = self::getExceptionData($exception->getPrevious());
124
-
125
-        return $array;
126
-    }
51
+		$errorData = self::getExceptionData($exception);
52
+		$errorData['server'] = $_SERVER;
53
+		$errorData['get'] = $_GET;
54
+		$errorData['post'] = $_POST;
55
+
56
+		$state = serialize($errorData);
57
+		$errorId = sha1($state);
58
+
59
+		// Save the error for later analysis
60
+		file_put_contents($siteConfiguration->getErrorLog() . '/' . $errorId . '.log', $state);
61
+
62
+		// clear and discard any content that's been saved to the output buffer
63
+		if (ob_get_level() > 0) {
64
+			ob_end_clean();
65
+		}
66
+
67
+		// push error ID into the document.
68
+		$message = str_replace('$1$', $errorId, $errorDocument);
69
+
70
+		if ($siteConfiguration->getDebuggingTraceEnabled()) {
71
+			ob_start();
72
+			var_dump($errorData);
73
+			$textErrorData = ob_get_contents();
74
+			ob_end_clean();
75
+
76
+			$message = str_replace('$2$', $textErrorData, $message);
77
+		}
78
+		else {
79
+			$message = str_replace('$2$', "", $message);
80
+		}
81
+
82
+		// While we *shouldn't* have sent headers by now due to the output buffering, PHPUnit does weird things.
83
+		// This is "only" needed for the tests, but it's a good idea to wrap this anyway.
84
+		if (!headers_sent()) {
85
+			header('HTTP/1.1 500 Internal Server Error');
86
+		}
87
+
88
+		// output the document
89
+		print $message;
90
+	}
91
+
92
+	/**
93
+	 * @param int    $errorSeverity The severity level of the exception.
94
+	 * @param string $errorMessage  The Exception message to throw.
95
+	 * @param string $errorFile     The filename where the exception is thrown.
96
+	 * @param int    $errorLine     The line number where the exception is thrown.
97
+	 *
98
+	 * @throws ErrorException
99
+	 */
100
+	public static function errorHandler($errorSeverity, $errorMessage, $errorFile, $errorLine)
101
+	{
102
+		// call into the main exception handler above
103
+		throw new ErrorException($errorMessage, 0, $errorSeverity, $errorFile, $errorLine);
104
+	}
105
+
106
+	/**
107
+	 * @param Throwable $exception
108
+	 *
109
+	 * @return null|array
110
+	 */
111
+	public static function getExceptionData($exception)
112
+	{
113
+		if ($exception == null) {
114
+			return null;
115
+		}
116
+
117
+		$array = array(
118
+			'exception' => get_class($exception),
119
+			'message'   => $exception->getMessage(),
120
+			'stack'     => $exception->getTraceAsString(),
121
+		);
122
+
123
+		$array['previous'] = self::getExceptionData($exception->getPrevious());
124
+
125
+		return $array;
126
+	}
127 127
 }
Please login to merge, or discard this patch.
includes/Security/CredentialProviders/ScratchTokenCredentialProvider.php 3 patches
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -20,136 +20,136 @@
 block discarded – undo
20 20
 
21 21
 class ScratchTokenCredentialProvider extends CredentialProviderBase
22 22
 {
23
-    /** @var EncryptionHelper */
24
-    private $encryptionHelper;
25
-    /** @var array the tokens generated in the last generation round. */
26
-    private $generatedTokens;
27
-
28
-    /**
29
-     * ScratchTokenCredentialProvider constructor.
30
-     *
31
-     * @param PdoDatabase       $database
32
-     * @param SiteConfiguration $configuration
33
-     */
34
-    public function __construct(PdoDatabase $database, SiteConfiguration $configuration)
35
-    {
36
-        parent::__construct($database, $configuration, 'scratch');
37
-        $this->encryptionHelper = new EncryptionHelper($configuration);
38
-    }
39
-
40
-    /**
41
-     * Validates a user-provided credential
42
-     *
43
-     * @param User   $user The user to test the authentication against
44
-     * @param string $data The raw credential data to be validated
45
-     *
46
-     * @return bool
47
-     * @throws ApplicationLogicException|OptimisticLockFailedException
48
-     */
49
-    public function authenticate(User $user, $data)
50
-    {
51
-        if (is_array($data)) {
52
-            return false;
53
-        }
54
-
55
-        $storedData = $this->getCredentialData($user->getId());
56
-
57
-        if ($storedData === null) {
58
-            throw new ApplicationLogicException('Credential data not found');
59
-        }
60
-
61
-        $scratchTokens = unserialize($this->encryptionHelper->decryptData($storedData->getData()));
62
-
63
-        $usedToken = null;
64
-        foreach ($scratchTokens as $scratchToken) {
65
-            if (password_verify($data, $scratchToken)){
66
-                $usedToken = $scratchToken;
67
-                SessionAlert::quick("Hey, it looks like you used a scratch token to log in. Would you like to change your multi-factor authentication configuration?", 'alert-warning');
68
-                WebRequest::setPostLoginRedirect($this->getConfiguration()->getBaseUrl() . "/internal.php/multiFactor");
69
-                break;
70
-            }
71
-        }
72
-
73
-        if($usedToken === null) {
74
-            return false;
75
-        }
76
-
77
-        $scratchTokens = array_diff($scratchTokens, [$usedToken]);
78
-
79
-        $storedData->setData($this->encryptionHelper->encryptData(serialize($scratchTokens)));
80
-        $storedData->save();
81
-
82
-        return true;
83
-    }
84
-
85
-    /**
86
-     * @param User   $user   The user the credential belongs to
87
-     * @param int    $factor The factor this credential provides
88
-     * @param string $data   Unused.
89
-     *
90
-     * @throws OptimisticLockFailedException
91
-     */
92
-    public function setCredential(User $user, $factor, $data)
93
-    {
94
-        $plaintextScratch = array();
95
-        $storedScratch = array();
96
-        for ($i = 0; $i < 5; $i++) {
97
-            $token = Base32::encode(openssl_random_pseudo_bytes(10));
98
-            $plaintextScratch[] = $token;
99
-
100
-            $storedScratch[] = password_hash(
101
-                $token,
102
-                PasswordCredentialProvider::PASSWORD_ALGO,
103
-                array('cost' => PasswordCredentialProvider::PASSWORD_COST)
104
-            );
105
-        }
106
-
107
-        $storedData = $this->getCredentialData($user->getId(), null);
108
-
109
-        if ($storedData !== null) {
110
-            $storedData->delete();
111
-        }
112
-
113
-        $storedData = $this->createNewCredential($user);
114
-
115
-        $storedData->setData($this->encryptionHelper->encryptData(serialize($storedScratch)));
116
-        $storedData->setFactor($factor);
117
-        $storedData->setVersion(1);
118
-        $storedData->setPriority(9);
119
-
120
-        $storedData->save();
121
-        $this->generatedTokens = $plaintextScratch;
122
-    }
123
-
124
-    /**
125
-     * Gets the count of remaining valid tokens
126
-     *
127
-     * @param int $userId
128
-     *
129
-     * @return int
130
-     */
131
-    public function getRemaining($userId)
132
-    {
133
-        $storedData = $this->getCredentialData($userId);
134
-
135
-        if ($storedData === null) {
136
-            return 0;
137
-        }
138
-
139
-        $scratchTokens = unserialize($this->encryptionHelper->decryptData($storedData->getData()));
140
-
141
-        return count($scratchTokens);
142
-    }
143
-
144
-    /**
145
-     * @return array
146
-     */
147
-    public function getTokens()
148
-    {
149
-        if ($this->generatedTokens != null) {
150
-            return $this->generatedTokens;
151
-        }
152
-
153
-        return array();
154
-    }
23
+	/** @var EncryptionHelper */
24
+	private $encryptionHelper;
25
+	/** @var array the tokens generated in the last generation round. */
26
+	private $generatedTokens;
27
+
28
+	/**
29
+	 * ScratchTokenCredentialProvider constructor.
30
+	 *
31
+	 * @param PdoDatabase       $database
32
+	 * @param SiteConfiguration $configuration
33
+	 */
34
+	public function __construct(PdoDatabase $database, SiteConfiguration $configuration)
35
+	{
36
+		parent::__construct($database, $configuration, 'scratch');
37
+		$this->encryptionHelper = new EncryptionHelper($configuration);
38
+	}
39
+
40
+	/**
41
+	 * Validates a user-provided credential
42
+	 *
43
+	 * @param User   $user The user to test the authentication against
44
+	 * @param string $data The raw credential data to be validated
45
+	 *
46
+	 * @return bool
47
+	 * @throws ApplicationLogicException|OptimisticLockFailedException
48
+	 */
49
+	public function authenticate(User $user, $data)
50
+	{
51
+		if (is_array($data)) {
52
+			return false;
53
+		}
54
+
55
+		$storedData = $this->getCredentialData($user->getId());
56
+
57
+		if ($storedData === null) {
58
+			throw new ApplicationLogicException('Credential data not found');
59
+		}
60
+
61
+		$scratchTokens = unserialize($this->encryptionHelper->decryptData($storedData->getData()));
62
+
63
+		$usedToken = null;
64
+		foreach ($scratchTokens as $scratchToken) {
65
+			if (password_verify($data, $scratchToken)){
66
+				$usedToken = $scratchToken;
67
+				SessionAlert::quick("Hey, it looks like you used a scratch token to log in. Would you like to change your multi-factor authentication configuration?", 'alert-warning');
68
+				WebRequest::setPostLoginRedirect($this->getConfiguration()->getBaseUrl() . "/internal.php/multiFactor");
69
+				break;
70
+			}
71
+		}
72
+
73
+		if($usedToken === null) {
74
+			return false;
75
+		}
76
+
77
+		$scratchTokens = array_diff($scratchTokens, [$usedToken]);
78
+
79
+		$storedData->setData($this->encryptionHelper->encryptData(serialize($scratchTokens)));
80
+		$storedData->save();
81
+
82
+		return true;
83
+	}
84
+
85
+	/**
86
+	 * @param User   $user   The user the credential belongs to
87
+	 * @param int    $factor The factor this credential provides
88
+	 * @param string $data   Unused.
89
+	 *
90
+	 * @throws OptimisticLockFailedException
91
+	 */
92
+	public function setCredential(User $user, $factor, $data)
93
+	{
94
+		$plaintextScratch = array();
95
+		$storedScratch = array();
96
+		for ($i = 0; $i < 5; $i++) {
97
+			$token = Base32::encode(openssl_random_pseudo_bytes(10));
98
+			$plaintextScratch[] = $token;
99
+
100
+			$storedScratch[] = password_hash(
101
+				$token,
102
+				PasswordCredentialProvider::PASSWORD_ALGO,
103
+				array('cost' => PasswordCredentialProvider::PASSWORD_COST)
104
+			);
105
+		}
106
+
107
+		$storedData = $this->getCredentialData($user->getId(), null);
108
+
109
+		if ($storedData !== null) {
110
+			$storedData->delete();
111
+		}
112
+
113
+		$storedData = $this->createNewCredential($user);
114
+
115
+		$storedData->setData($this->encryptionHelper->encryptData(serialize($storedScratch)));
116
+		$storedData->setFactor($factor);
117
+		$storedData->setVersion(1);
118
+		$storedData->setPriority(9);
119
+
120
+		$storedData->save();
121
+		$this->generatedTokens = $plaintextScratch;
122
+	}
123
+
124
+	/**
125
+	 * Gets the count of remaining valid tokens
126
+	 *
127
+	 * @param int $userId
128
+	 *
129
+	 * @return int
130
+	 */
131
+	public function getRemaining($userId)
132
+	{
133
+		$storedData = $this->getCredentialData($userId);
134
+
135
+		if ($storedData === null) {
136
+			return 0;
137
+		}
138
+
139
+		$scratchTokens = unserialize($this->encryptionHelper->decryptData($storedData->getData()));
140
+
141
+		return count($scratchTokens);
142
+	}
143
+
144
+	/**
145
+	 * @return array
146
+	 */
147
+	public function getTokens()
148
+	{
149
+		if ($this->generatedTokens != null) {
150
+			return $this->generatedTokens;
151
+		}
152
+
153
+		return array();
154
+	}
155 155
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -62,15 +62,15 @@
 block discarded – undo
62 62
 
63 63
         $usedToken = null;
64 64
         foreach ($scratchTokens as $scratchToken) {
65
-            if (password_verify($data, $scratchToken)){
65
+            if (password_verify($data, $scratchToken)) {
66 66
                 $usedToken = $scratchToken;
67 67
                 SessionAlert::quick("Hey, it looks like you used a scratch token to log in. Would you like to change your multi-factor authentication configuration?", 'alert-warning');
68
-                WebRequest::setPostLoginRedirect($this->getConfiguration()->getBaseUrl() . "/internal.php/multiFactor");
68
+                WebRequest::setPostLoginRedirect($this->getConfiguration()->getBaseUrl()."/internal.php/multiFactor");
69 69
                 break;
70 70
             }
71 71
         }
72 72
 
73
-        if($usedToken === null) {
73
+        if ($usedToken === null) {
74 74
             return false;
75 75
         }
76 76
 
Please login to merge, or discard this patch.
Braces   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -62,7 +62,7 @@
 block discarded – undo
62 62
 
63 63
         $usedToken = null;
64 64
         foreach ($scratchTokens as $scratchToken) {
65
-            if (password_verify($data, $scratchToken)){
65
+            if (password_verify($data, $scratchToken)) {
66 66
                 $usedToken = $scratchToken;
67 67
                 SessionAlert::quick("Hey, it looks like you used a scratch token to log in. Would you like to change your multi-factor authentication configuration?", 'alert-warning');
68 68
                 WebRequest::setPostLoginRedirect($this->getConfiguration()->getBaseUrl() . "/internal.php/multiFactor");
Please login to merge, or discard this patch.
includes/Security/RoleConfiguration.php 1 patch
Indentation   +347 added lines, -347 removed lines patch added patch discarded remove patch
@@ -47,377 +47,377 @@
 block discarded – undo
47 47
 
48 48
 class RoleConfiguration
49 49
 {
50
-    const ACCESS_ALLOW = 1;
51
-    const ACCESS_DENY = -1;
52
-    const ACCESS_DEFAULT = 0;
53
-    const MAIN = 'main';
54
-    const ALL = '*';
55
-    /**
56
-     * A map of roles to rights
57
-     *
58
-     * For example:
59
-     *
60
-     * array(
61
-     *   'myrole' => array(
62
-     *       PageMyPage::class => array(
63
-     *           'edit' => self::ACCESS_ALLOW,
64
-     *           'create' => self::ACCESS_DENY,
65
-     *       )
66
-     *   )
67
-     * )
68
-     *
69
-     * Note that DENY takes precedence over everything else when roles are combined, followed by ALLOW, followed by
70
-     * DEFAULT. Thus, if you have the following ([A]llow, [D]eny, [-] (default)) grants in different roles, this should
71
-     * be the expected result:
72
-     *
73
-     * - (-,-,-) = - (default because nothing to explicitly say allowed or denied equates to a denial)
74
-     * - (A,-,-) = A
75
-     * - (D,-,-) = D
76
-     * - (A,D,-) = D (deny takes precedence over allow)
77
-     * - (A,A,A) = A (repetition has no effect)
78
-     *
79
-     * The public role is special, and is applied to all users automatically. Avoid using deny on this role.
80
-     *
81
-     * @var array
82
-     */
83
-    private $roleConfig = array(
84
-        'public'            => array(
85
-            /*
50
+	const ACCESS_ALLOW = 1;
51
+	const ACCESS_DENY = -1;
52
+	const ACCESS_DEFAULT = 0;
53
+	const MAIN = 'main';
54
+	const ALL = '*';
55
+	/**
56
+	 * A map of roles to rights
57
+	 *
58
+	 * For example:
59
+	 *
60
+	 * array(
61
+	 *   'myrole' => array(
62
+	 *       PageMyPage::class => array(
63
+	 *           'edit' => self::ACCESS_ALLOW,
64
+	 *           'create' => self::ACCESS_DENY,
65
+	 *       )
66
+	 *   )
67
+	 * )
68
+	 *
69
+	 * Note that DENY takes precedence over everything else when roles are combined, followed by ALLOW, followed by
70
+	 * DEFAULT. Thus, if you have the following ([A]llow, [D]eny, [-] (default)) grants in different roles, this should
71
+	 * be the expected result:
72
+	 *
73
+	 * - (-,-,-) = - (default because nothing to explicitly say allowed or denied equates to a denial)
74
+	 * - (A,-,-) = A
75
+	 * - (D,-,-) = D
76
+	 * - (A,D,-) = D (deny takes precedence over allow)
77
+	 * - (A,A,A) = A (repetition has no effect)
78
+	 *
79
+	 * The public role is special, and is applied to all users automatically. Avoid using deny on this role.
80
+	 *
81
+	 * @var array
82
+	 */
83
+	private $roleConfig = array(
84
+		'public'            => array(
85
+			/*
86 86
              * THIS ROLE IS GRANTED TO ALL LOGGED *OUT* USERS IMPLICITLY.
87 87
              *
88 88
              * USERS IN THIS ROLE DO NOT HAVE TO BE IDENTIFIED TO GET THE RIGHTS CONFERRED HERE.
89 89
              * DO NOT ADD ANY SECURITY-SENSITIVE RIGHTS HERE.
90 90
              */
91
-            '_childRoles'   => array(
92
-                'publicStats',
93
-            ),
94
-            PageTeam::class => array(
95
-                self::MAIN => self::ACCESS_ALLOW,
96
-            ),
97
-            PageXffDemo::class        => array(
98
-                self::MAIN  => self::ACCESS_ALLOW,
99
-            )
100
-        ),
101
-        'loggedIn'          => array(
102
-            /*
91
+			'_childRoles'   => array(
92
+				'publicStats',
93
+			),
94
+			PageTeam::class => array(
95
+				self::MAIN => self::ACCESS_ALLOW,
96
+			),
97
+			PageXffDemo::class        => array(
98
+				self::MAIN  => self::ACCESS_ALLOW,
99
+			)
100
+		),
101
+		'loggedIn'          => array(
102
+			/*
103 103
              * THIS ROLE IS GRANTED TO ALL LOGGED IN USERS IMPLICITLY.
104 104
              *
105 105
              * USERS IN THIS ROLE DO NOT HAVE TO BE IDENTIFIED TO GET THE RIGHTS CONFERRED HERE.
106 106
              * DO NOT ADD ANY SECURITY-SENSITIVE RIGHTS HERE.
107 107
              */
108
-            '_childRoles'             => array(
109
-                'public',
110
-            ),
111
-            PagePreferences::class    => array(
112
-                self::MAIN => self::ACCESS_ALLOW,
113
-            ),
114
-            PageChangePassword::class => array(
115
-                self::MAIN => self::ACCESS_ALLOW,
116
-            ),
117
-            PageMultiFactor::class    => array(
118
-                self::MAIN          => self::ACCESS_ALLOW,
119
-                'scratch'           => self::ACCESS_ALLOW,
120
-                'enableYubikeyOtp'  => self::ACCESS_ALLOW,
121
-                'disableYubikeyOtp' => self::ACCESS_ALLOW,
122
-                'enableTotp'        => self::ACCESS_ALLOW,
123
-                'disableTotp'       => self::ACCESS_ALLOW,
124
-                'enableU2F'       => self::ACCESS_ALLOW,
125
-                'disableU2F'       => self::ACCESS_ALLOW,
126
-            ),
127
-            PageOAuth::class          => array(
128
-                'attach' => self::ACCESS_ALLOW,
129
-                'detach' => self::ACCESS_ALLOW,
130
-            ),
131
-        ),
132
-        'user'              => array(
133
-            '_description'                       => 'A standard tool user.',
134
-            '_editableBy'                        => array('admin', 'toolRoot'),
135
-            '_childRoles'                        => array(
136
-                'internalStats',
137
-            ),
138
-            PageMain::class                      => array(
139
-                self::MAIN => self::ACCESS_ALLOW,
140
-            ),
141
-            PageBan::class                       => array(
142
-                self::MAIN => self::ACCESS_ALLOW,
143
-            ),
144
-            PageEditComment::class               => array(
145
-                self::MAIN => self::ACCESS_ALLOW,
146
-            ),
147
-            PageEmailManagement::class           => array(
148
-                self::MAIN => self::ACCESS_ALLOW,
149
-                'view'     => self::ACCESS_ALLOW,
150
-            ),
151
-            PageExpandedRequestList::class       => array(
152
-                self::MAIN => self::ACCESS_ALLOW,
153
-            ),
154
-            PageLog::class                       => array(
155
-                self::MAIN => self::ACCESS_ALLOW,
156
-            ),
157
-            PageSearch::class                    => array(
158
-                self::MAIN => self::ACCESS_ALLOW,
159
-            ),
160
-            PageWelcomeTemplateManagement::class => array(
161
-                self::MAIN => self::ACCESS_ALLOW,
162
-                'select'   => self::ACCESS_ALLOW,
163
-                'view'     => self::ACCESS_ALLOW,
164
-            ),
165
-            PageViewRequest::class               => array(
166
-                self::MAIN       => self::ACCESS_ALLOW,
167
-                'seeAllRequests' => self::ACCESS_ALLOW,
168
-            ),
169
-            'RequestData'                        => array(
170
-                'seePrivateDataWhenReserved' => self::ACCESS_ALLOW,
171
-                'seePrivateDataWithHash'     => self::ACCESS_ALLOW,
172
-            ),
173
-            PageCustomClose::class               => array(
174
-                self::MAIN => self::ACCESS_ALLOW,
175
-            ),
176
-            PageComment::class                   => array(
177
-                self::MAIN => self::ACCESS_ALLOW,
178
-            ),
179
-            PageCloseRequest::class              => array(
180
-                self::MAIN => self::ACCESS_ALLOW,
181
-            ),
182
-            PageCreateRequest::class             => array(
183
-                self::MAIN => self::ACCESS_ALLOW,
184
-            ),
185
-            PageDeferRequest::class              => array(
186
-                self::MAIN => self::ACCESS_ALLOW,
187
-            ),
188
-            PageDropRequest::class               => array(
189
-                self::MAIN => self::ACCESS_ALLOW,
190
-            ),
191
-            PageReservation::class               => array(
192
-                self::MAIN => self::ACCESS_ALLOW,
193
-            ),
194
-            PageSendToUser::class                => array(
195
-                self::MAIN => self::ACCESS_ALLOW,
196
-            ),
197
-            PageBreakReservation::class          => array(
198
-                self::MAIN => self::ACCESS_ALLOW,
199
-            ),
200
-            PageJobQueue::class                  => array(
201
-                self::MAIN => self::ACCESS_ALLOW,
202
-                'view'     => self::ACCESS_ALLOW,
203
-                'all'      => self::ACCESS_ALLOW,
204
-            ),
205
-            'RequestCreation'                    => array(
206
-                User::CREATION_MANUAL => self::ACCESS_ALLOW,
207
-                User::CREATION_OAUTH  => self::ACCESS_ALLOW,
208
-            ),
209
-        ),
210
-        'admin'             => array(
211
-            '_description'                       => 'A tool administrator.',
212
-            '_editableBy'                        => array('admin', 'toolRoot'),
213
-            '_childRoles'                        => array(
214
-                'user',
215
-                'requestAdminTools',
216
-            ),
217
-            PageEmailManagement::class           => array(
218
-                'edit'   => self::ACCESS_ALLOW,
219
-                'create' => self::ACCESS_ALLOW,
220
-            ),
221
-            PageSiteNotice::class                => array(
222
-                self::MAIN => self::ACCESS_ALLOW,
223
-            ),
224
-            PageUserManagement::class            => array(
225
-                self::MAIN  => self::ACCESS_ALLOW,
226
-                'approve'   => self::ACCESS_ALLOW,
227
-                'decline'   => self::ACCESS_ALLOW,
228
-                'rename'    => self::ACCESS_ALLOW,
229
-                'editUser'  => self::ACCESS_ALLOW,
230
-                'suspend'   => self::ACCESS_ALLOW,
231
-                'editRoles' => self::ACCESS_ALLOW,
232
-            ),
233
-            PageWelcomeTemplateManagement::class => array(
234
-                'edit'   => self::ACCESS_ALLOW,
235
-                'delete' => self::ACCESS_ALLOW,
236
-                'add'    => self::ACCESS_ALLOW,
237
-            ),
238
-            PageJobQueue::class                  => array(
239
-                'acknowledge' => self::ACCESS_ALLOW,
240
-                'requeue'     => self::ACCESS_ALLOW,
241
-            ),
242
-        ),
243
-        'checkuser'         => array(
244
-            '_description'            => 'A user with CheckUser access',
245
-            '_editableBy'             => array('checkuser', 'toolRoot'),
246
-            '_childRoles'             => array(
247
-                'user',
248
-                'requestAdminTools',
249
-            ),
250
-            PageUserManagement::class => array(
251
-                self::MAIN  => self::ACCESS_ALLOW,
252
-                'suspend'   => self::ACCESS_ALLOW,
253
-                'editRoles' => self::ACCESS_ALLOW,
254
-            ),
255
-            'RequestData'             => array(
256
-                'seeUserAgentData' => self::ACCESS_ALLOW,
257
-            ),
258
-        ),
259
-        'toolRoot'          => array(
260
-            '_description' => 'A user with shell access to the servers running the tool',
261
-            '_editableBy'  => array('toolRoot'),
262
-            '_childRoles'  => array(
263
-                'admin',
264
-                'checkuser',
265
-            ),
266
-        ),
267
-        'botCreation'       => array(
268
-            '_description'    => 'A user allowed to use the bot to perform account creations',
269
-            '_editableBy'     => array('admin', 'toolRoot'),
270
-            '_childRoles'     => array(),
271
-            'RequestCreation' => array(
272
-                User::CREATION_BOT => self::ACCESS_ALLOW,
273
-            ),
274
-        ),
108
+			'_childRoles'             => array(
109
+				'public',
110
+			),
111
+			PagePreferences::class    => array(
112
+				self::MAIN => self::ACCESS_ALLOW,
113
+			),
114
+			PageChangePassword::class => array(
115
+				self::MAIN => self::ACCESS_ALLOW,
116
+			),
117
+			PageMultiFactor::class    => array(
118
+				self::MAIN          => self::ACCESS_ALLOW,
119
+				'scratch'           => self::ACCESS_ALLOW,
120
+				'enableYubikeyOtp'  => self::ACCESS_ALLOW,
121
+				'disableYubikeyOtp' => self::ACCESS_ALLOW,
122
+				'enableTotp'        => self::ACCESS_ALLOW,
123
+				'disableTotp'       => self::ACCESS_ALLOW,
124
+				'enableU2F'       => self::ACCESS_ALLOW,
125
+				'disableU2F'       => self::ACCESS_ALLOW,
126
+			),
127
+			PageOAuth::class          => array(
128
+				'attach' => self::ACCESS_ALLOW,
129
+				'detach' => self::ACCESS_ALLOW,
130
+			),
131
+		),
132
+		'user'              => array(
133
+			'_description'                       => 'A standard tool user.',
134
+			'_editableBy'                        => array('admin', 'toolRoot'),
135
+			'_childRoles'                        => array(
136
+				'internalStats',
137
+			),
138
+			PageMain::class                      => array(
139
+				self::MAIN => self::ACCESS_ALLOW,
140
+			),
141
+			PageBan::class                       => array(
142
+				self::MAIN => self::ACCESS_ALLOW,
143
+			),
144
+			PageEditComment::class               => array(
145
+				self::MAIN => self::ACCESS_ALLOW,
146
+			),
147
+			PageEmailManagement::class           => array(
148
+				self::MAIN => self::ACCESS_ALLOW,
149
+				'view'     => self::ACCESS_ALLOW,
150
+			),
151
+			PageExpandedRequestList::class       => array(
152
+				self::MAIN => self::ACCESS_ALLOW,
153
+			),
154
+			PageLog::class                       => array(
155
+				self::MAIN => self::ACCESS_ALLOW,
156
+			),
157
+			PageSearch::class                    => array(
158
+				self::MAIN => self::ACCESS_ALLOW,
159
+			),
160
+			PageWelcomeTemplateManagement::class => array(
161
+				self::MAIN => self::ACCESS_ALLOW,
162
+				'select'   => self::ACCESS_ALLOW,
163
+				'view'     => self::ACCESS_ALLOW,
164
+			),
165
+			PageViewRequest::class               => array(
166
+				self::MAIN       => self::ACCESS_ALLOW,
167
+				'seeAllRequests' => self::ACCESS_ALLOW,
168
+			),
169
+			'RequestData'                        => array(
170
+				'seePrivateDataWhenReserved' => self::ACCESS_ALLOW,
171
+				'seePrivateDataWithHash'     => self::ACCESS_ALLOW,
172
+			),
173
+			PageCustomClose::class               => array(
174
+				self::MAIN => self::ACCESS_ALLOW,
175
+			),
176
+			PageComment::class                   => array(
177
+				self::MAIN => self::ACCESS_ALLOW,
178
+			),
179
+			PageCloseRequest::class              => array(
180
+				self::MAIN => self::ACCESS_ALLOW,
181
+			),
182
+			PageCreateRequest::class             => array(
183
+				self::MAIN => self::ACCESS_ALLOW,
184
+			),
185
+			PageDeferRequest::class              => array(
186
+				self::MAIN => self::ACCESS_ALLOW,
187
+			),
188
+			PageDropRequest::class               => array(
189
+				self::MAIN => self::ACCESS_ALLOW,
190
+			),
191
+			PageReservation::class               => array(
192
+				self::MAIN => self::ACCESS_ALLOW,
193
+			),
194
+			PageSendToUser::class                => array(
195
+				self::MAIN => self::ACCESS_ALLOW,
196
+			),
197
+			PageBreakReservation::class          => array(
198
+				self::MAIN => self::ACCESS_ALLOW,
199
+			),
200
+			PageJobQueue::class                  => array(
201
+				self::MAIN => self::ACCESS_ALLOW,
202
+				'view'     => self::ACCESS_ALLOW,
203
+				'all'      => self::ACCESS_ALLOW,
204
+			),
205
+			'RequestCreation'                    => array(
206
+				User::CREATION_MANUAL => self::ACCESS_ALLOW,
207
+				User::CREATION_OAUTH  => self::ACCESS_ALLOW,
208
+			),
209
+		),
210
+		'admin'             => array(
211
+			'_description'                       => 'A tool administrator.',
212
+			'_editableBy'                        => array('admin', 'toolRoot'),
213
+			'_childRoles'                        => array(
214
+				'user',
215
+				'requestAdminTools',
216
+			),
217
+			PageEmailManagement::class           => array(
218
+				'edit'   => self::ACCESS_ALLOW,
219
+				'create' => self::ACCESS_ALLOW,
220
+			),
221
+			PageSiteNotice::class                => array(
222
+				self::MAIN => self::ACCESS_ALLOW,
223
+			),
224
+			PageUserManagement::class            => array(
225
+				self::MAIN  => self::ACCESS_ALLOW,
226
+				'approve'   => self::ACCESS_ALLOW,
227
+				'decline'   => self::ACCESS_ALLOW,
228
+				'rename'    => self::ACCESS_ALLOW,
229
+				'editUser'  => self::ACCESS_ALLOW,
230
+				'suspend'   => self::ACCESS_ALLOW,
231
+				'editRoles' => self::ACCESS_ALLOW,
232
+			),
233
+			PageWelcomeTemplateManagement::class => array(
234
+				'edit'   => self::ACCESS_ALLOW,
235
+				'delete' => self::ACCESS_ALLOW,
236
+				'add'    => self::ACCESS_ALLOW,
237
+			),
238
+			PageJobQueue::class                  => array(
239
+				'acknowledge' => self::ACCESS_ALLOW,
240
+				'requeue'     => self::ACCESS_ALLOW,
241
+			),
242
+		),
243
+		'checkuser'         => array(
244
+			'_description'            => 'A user with CheckUser access',
245
+			'_editableBy'             => array('checkuser', 'toolRoot'),
246
+			'_childRoles'             => array(
247
+				'user',
248
+				'requestAdminTools',
249
+			),
250
+			PageUserManagement::class => array(
251
+				self::MAIN  => self::ACCESS_ALLOW,
252
+				'suspend'   => self::ACCESS_ALLOW,
253
+				'editRoles' => self::ACCESS_ALLOW,
254
+			),
255
+			'RequestData'             => array(
256
+				'seeUserAgentData' => self::ACCESS_ALLOW,
257
+			),
258
+		),
259
+		'toolRoot'          => array(
260
+			'_description' => 'A user with shell access to the servers running the tool',
261
+			'_editableBy'  => array('toolRoot'),
262
+			'_childRoles'  => array(
263
+				'admin',
264
+				'checkuser',
265
+			),
266
+		),
267
+		'botCreation'       => array(
268
+			'_description'    => 'A user allowed to use the bot to perform account creations',
269
+			'_editableBy'     => array('admin', 'toolRoot'),
270
+			'_childRoles'     => array(),
271
+			'RequestCreation' => array(
272
+				User::CREATION_BOT => self::ACCESS_ALLOW,
273
+			),
274
+		),
275 275
 
276
-        // Child roles go below this point
277
-        'publicStats'       => array(
278
-            '_hidden'               => true,
279
-            StatsUsers::class       => array(
280
-                self::MAIN => self::ACCESS_ALLOW,
281
-                'detail'   => self::ACCESS_ALLOW,
282
-            ),
283
-            StatsTopCreators::class => array(
284
-                self::MAIN => self::ACCESS_ALLOW,
285
-            ),
286
-        ),
287
-        'internalStats'     => array(
288
-            '_hidden'                    => true,
289
-            StatsMain::class             => array(
290
-                self::MAIN => self::ACCESS_ALLOW,
291
-            ),
292
-            StatsFastCloses::class       => array(
293
-                self::MAIN => self::ACCESS_ALLOW,
294
-            ),
295
-            StatsInactiveUsers::class    => array(
296
-                self::MAIN => self::ACCESS_ALLOW,
297
-            ),
298
-            StatsMonthlyStats::class     => array(
299
-                self::MAIN => self::ACCESS_ALLOW,
300
-            ),
301
-            StatsReservedRequests::class => array(
302
-                self::MAIN => self::ACCESS_ALLOW,
303
-            ),
304
-            StatsTemplateStats::class    => array(
305
-                self::MAIN => self::ACCESS_ALLOW,
306
-            ),
307
-        ),
308
-        'requestAdminTools' => array(
309
-            '_hidden'                   => true,
310
-            PageBan::class              => array(
311
-                self::MAIN => self::ACCESS_ALLOW,
312
-                'set'      => self::ACCESS_ALLOW,
313
-                'remove'   => self::ACCESS_ALLOW,
314
-            ),
315
-            PageEditComment::class      => array(
316
-                'editOthers' => self::ACCESS_ALLOW,
317
-            ),
318
-            PageBreakReservation::class => array(
319
-                'force' => self::ACCESS_ALLOW,
320
-            ),
321
-            PageCustomClose::class      => array(
322
-                'skipCcMailingList' => self::ACCESS_ALLOW,
323
-            ),
324
-            'RequestData'               => array(
325
-                'reopenOldRequest'      => self::ACCESS_ALLOW,
326
-                'alwaysSeePrivateData'  => self::ACCESS_ALLOW,
327
-                'alwaysSeeHash'         => self::ACCESS_ALLOW,
328
-                'seeRestrictedComments' => self::ACCESS_ALLOW,
329
-            ),
330
-        ),
331
-    );
332
-    /** @var array
333
-     * List of roles which are *exempt* from the identification requirements
334
-     *
335
-     * Think twice about adding roles to this list.
336
-     *
337
-     * @category Security-Critical
338
-     */
339
-    private $identificationExempt = array('public', 'loggedIn');
276
+		// Child roles go below this point
277
+		'publicStats'       => array(
278
+			'_hidden'               => true,
279
+			StatsUsers::class       => array(
280
+				self::MAIN => self::ACCESS_ALLOW,
281
+				'detail'   => self::ACCESS_ALLOW,
282
+			),
283
+			StatsTopCreators::class => array(
284
+				self::MAIN => self::ACCESS_ALLOW,
285
+			),
286
+		),
287
+		'internalStats'     => array(
288
+			'_hidden'                    => true,
289
+			StatsMain::class             => array(
290
+				self::MAIN => self::ACCESS_ALLOW,
291
+			),
292
+			StatsFastCloses::class       => array(
293
+				self::MAIN => self::ACCESS_ALLOW,
294
+			),
295
+			StatsInactiveUsers::class    => array(
296
+				self::MAIN => self::ACCESS_ALLOW,
297
+			),
298
+			StatsMonthlyStats::class     => array(
299
+				self::MAIN => self::ACCESS_ALLOW,
300
+			),
301
+			StatsReservedRequests::class => array(
302
+				self::MAIN => self::ACCESS_ALLOW,
303
+			),
304
+			StatsTemplateStats::class    => array(
305
+				self::MAIN => self::ACCESS_ALLOW,
306
+			),
307
+		),
308
+		'requestAdminTools' => array(
309
+			'_hidden'                   => true,
310
+			PageBan::class              => array(
311
+				self::MAIN => self::ACCESS_ALLOW,
312
+				'set'      => self::ACCESS_ALLOW,
313
+				'remove'   => self::ACCESS_ALLOW,
314
+			),
315
+			PageEditComment::class      => array(
316
+				'editOthers' => self::ACCESS_ALLOW,
317
+			),
318
+			PageBreakReservation::class => array(
319
+				'force' => self::ACCESS_ALLOW,
320
+			),
321
+			PageCustomClose::class      => array(
322
+				'skipCcMailingList' => self::ACCESS_ALLOW,
323
+			),
324
+			'RequestData'               => array(
325
+				'reopenOldRequest'      => self::ACCESS_ALLOW,
326
+				'alwaysSeePrivateData'  => self::ACCESS_ALLOW,
327
+				'alwaysSeeHash'         => self::ACCESS_ALLOW,
328
+				'seeRestrictedComments' => self::ACCESS_ALLOW,
329
+			),
330
+		),
331
+	);
332
+	/** @var array
333
+	 * List of roles which are *exempt* from the identification requirements
334
+	 *
335
+	 * Think twice about adding roles to this list.
336
+	 *
337
+	 * @category Security-Critical
338
+	 */
339
+	private $identificationExempt = array('public', 'loggedIn');
340 340
 
341
-    /**
342
-     * RoleConfiguration constructor.
343
-     *
344
-     * @param array $roleConfig           Set to non-null to override the default configuration.
345
-     * @param array $identificationExempt Set to non-null to override the default configuration.
346
-     */
347
-    public function __construct(array $roleConfig = null, array $identificationExempt = null)
348
-    {
349
-        if ($roleConfig !== null) {
350
-            $this->roleConfig = $roleConfig;
351
-        }
341
+	/**
342
+	 * RoleConfiguration constructor.
343
+	 *
344
+	 * @param array $roleConfig           Set to non-null to override the default configuration.
345
+	 * @param array $identificationExempt Set to non-null to override the default configuration.
346
+	 */
347
+	public function __construct(array $roleConfig = null, array $identificationExempt = null)
348
+	{
349
+		if ($roleConfig !== null) {
350
+			$this->roleConfig = $roleConfig;
351
+		}
352 352
 
353
-        if ($identificationExempt !== null) {
354
-            $this->identificationExempt = $identificationExempt;
355
-        }
356
-    }
353
+		if ($identificationExempt !== null) {
354
+			$this->identificationExempt = $identificationExempt;
355
+		}
356
+	}
357 357
 
358
-    /**
359
-     * @param array $roles The roles to check
360
-     *
361
-     * @return array
362
-     */
363
-    public function getApplicableRoles(array $roles)
364
-    {
365
-        $available = array();
358
+	/**
359
+	 * @param array $roles The roles to check
360
+	 *
361
+	 * @return array
362
+	 */
363
+	public function getApplicableRoles(array $roles)
364
+	{
365
+		$available = array();
366 366
 
367
-        foreach ($roles as $role) {
368
-            if (!isset($this->roleConfig[$role])) {
369
-                // wat
370
-                continue;
371
-            }
367
+		foreach ($roles as $role) {
368
+			if (!isset($this->roleConfig[$role])) {
369
+				// wat
370
+				continue;
371
+			}
372 372
 
373
-            $available[$role] = $this->roleConfig[$role];
373
+			$available[$role] = $this->roleConfig[$role];
374 374
 
375
-            if (isset($available[$role]['_childRoles'])) {
376
-                $childRoles = self::getApplicableRoles($available[$role]['_childRoles']);
377
-                $available = array_merge($available, $childRoles);
375
+			if (isset($available[$role]['_childRoles'])) {
376
+				$childRoles = self::getApplicableRoles($available[$role]['_childRoles']);
377
+				$available = array_merge($available, $childRoles);
378 378
 
379
-                unset($available[$role]['_childRoles']);
380
-            }
379
+				unset($available[$role]['_childRoles']);
380
+			}
381 381
 
382
-            foreach (array('_hidden', '_editableBy', '_description') as $item) {
383
-                if (isset($available[$role][$item])) {
384
-                    unset($available[$role][$item]);
385
-                }
386
-            }
387
-        }
382
+			foreach (array('_hidden', '_editableBy', '_description') as $item) {
383
+				if (isset($available[$role][$item])) {
384
+					unset($available[$role][$item]);
385
+				}
386
+			}
387
+		}
388 388
 
389
-        return $available;
390
-    }
389
+		return $available;
390
+	}
391 391
 
392
-    public function getAvailableRoles()
393
-    {
394
-        $possible = array_diff(array_keys($this->roleConfig), array('public', 'loggedIn'));
392
+	public function getAvailableRoles()
393
+	{
394
+		$possible = array_diff(array_keys($this->roleConfig), array('public', 'loggedIn'));
395 395
 
396
-        $actual = array();
396
+		$actual = array();
397 397
 
398
-        foreach ($possible as $role) {
399
-            if (!isset($this->roleConfig[$role]['_hidden'])) {
400
-                $actual[$role] = array(
401
-                    'description' => $this->roleConfig[$role]['_description'],
402
-                    'editableBy'  => $this->roleConfig[$role]['_editableBy'],
403
-                );
404
-            }
405
-        }
398
+		foreach ($possible as $role) {
399
+			if (!isset($this->roleConfig[$role]['_hidden'])) {
400
+				$actual[$role] = array(
401
+					'description' => $this->roleConfig[$role]['_description'],
402
+					'editableBy'  => $this->roleConfig[$role]['_editableBy'],
403
+				);
404
+			}
405
+		}
406 406
 
407
-        return $actual;
408
-    }
407
+		return $actual;
408
+	}
409 409
 
410
-    /**
411
-     * @param string $role
412
-     *
413
-     * @return bool
414
-     */
415
-    public function roleNeedsIdentification($role)
416
-    {
417
-        if (in_array($role, $this->identificationExempt)) {
418
-            return false;
419
-        }
410
+	/**
411
+	 * @param string $role
412
+	 *
413
+	 * @return bool
414
+	 */
415
+	public function roleNeedsIdentification($role)
416
+	{
417
+		if (in_array($role, $this->identificationExempt)) {
418
+			return false;
419
+		}
420 420
 
421
-        return true;
422
-    }
421
+		return true;
422
+	}
423 423
 }
Please login to merge, or discard this patch.
includes/WebRequest.php 1 patch
Indentation   +563 added lines, -563 removed lines patch added patch discarded remove patch
@@ -22,567 +22,567 @@
 block discarded – undo
22 22
  */
23 23
 class WebRequest
24 24
 {
25
-    /**
26
-     * @var \Waca\Providers\GlobalState\IGlobalStateProvider Provides access to the global state.
27
-     */
28
-    private static $globalStateProvider;
29
-
30
-    /**
31
-     * Returns a boolean value if the request was submitted with the HTTP POST method.
32
-     * @return bool
33
-     */
34
-    public static function wasPosted()
35
-    {
36
-        return self::method() === 'POST';
37
-    }
38
-
39
-    /**
40
-     * Gets the HTTP Method used
41
-     * @return string|null
42
-     */
43
-    public static function method()
44
-    {
45
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
46
-
47
-        if (isset($server['REQUEST_METHOD'])) {
48
-            return $server['REQUEST_METHOD'];
49
-        }
50
-
51
-        return null;
52
-    }
53
-
54
-    /**
55
-     * Gets a boolean value stating whether the request was served over HTTPS or not.
56
-     * @return bool
57
-     */
58
-    public static function isHttps()
59
-    {
60
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
61
-
62
-        if (isset($server['HTTP_X_FORWARDED_PROTO'])) {
63
-            if ($server['HTTP_X_FORWARDED_PROTO'] === 'https') {
64
-                // Client <=> Proxy is encrypted
65
-                return true;
66
-            }
67
-            else {
68
-                // Proxy <=> Server link unknown, Client <=> Proxy is not encrypted.
69
-                return false;
70
-            }
71
-        }
72
-
73
-        if (isset($server['HTTPS'])) {
74
-            if ($server['HTTPS'] === 'off') {
75
-                // ISAPI on IIS breaks the spec. :(
76
-                return false;
77
-            }
78
-
79
-            if ($server['HTTPS'] !== '') {
80
-                // Set to a non-empty value
81
-                return true;
82
-            }
83
-        }
84
-
85
-        return false;
86
-    }
87
-
88
-    /**
89
-     * Gets the path info
90
-     *
91
-     * @return array Array of path info segments
92
-     */
93
-    public static function pathInfo()
94
-    {
95
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
96
-        if (!isset($server['PATH_INFO'])) {
97
-            return array();
98
-        }
99
-
100
-        $exploded = explode('/', $server['PATH_INFO']);
101
-
102
-        // filter out empty values, and reindex from zero. Notably, the first element is always zero, since it starts
103
-        // with a /
104
-        return array_values(array_filter($exploded));
105
-    }
106
-
107
-    /**
108
-     * Gets the remote address of the web request
109
-     * @return null|string
110
-     */
111
-    public static function remoteAddress()
112
-    {
113
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
114
-
115
-        if (isset($server['REMOTE_ADDR'])) {
116
-            return $server['REMOTE_ADDR'];
117
-        }
118
-
119
-        return null;
120
-    }
121
-
122
-    /**
123
-     * Gets the remote address of the web request
124
-     * @return null|string
125
-     */
126
-    public static function httpHost()
127
-    {
128
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
129
-
130
-        if (isset($server['HTTP_HOST'])) {
131
-            return $server['HTTP_HOST'];
132
-        }
133
-
134
-        return null;
135
-    }
136
-
137
-    /**
138
-     * Gets the XFF header contents for the web request
139
-     * @return null|string
140
-     */
141
-    public static function forwardedAddress()
142
-    {
143
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
144
-
145
-        if (isset($server['HTTP_X_FORWARDED_FOR'])) {
146
-            return $server['HTTP_X_FORWARDED_FOR'];
147
-        }
148
-
149
-        return null;
150
-    }
151
-
152
-    /**
153
-     * Sets the global state provider.
154
-     *
155
-     * Almost guaranteed this is not the method you want in production code.
156
-     *
157
-     * @param \Waca\Providers\GlobalState\IGlobalStateProvider $globalState
158
-     */
159
-    public static function setGlobalStateProvider($globalState)
160
-    {
161
-        self::$globalStateProvider = $globalState;
162
-    }
163
-
164
-    #region POST variables
165
-
166
-    /**
167
-     * @param string $key
168
-     *
169
-     * @return null|string
170
-     */
171
-    public static function postString($key)
172
-    {
173
-        $post = &self::$globalStateProvider->getPostSuperGlobal();
174
-        if (!array_key_exists($key, $post)) {
175
-            return null;
176
-        }
177
-
178
-        if ($post[$key] === "") {
179
-            return null;
180
-        }
181
-
182
-        return (string)$post[$key];
183
-    }
184
-
185
-    /**
186
-     * @param string $key
187
-     *
188
-     * @return null|string
189
-     */
190
-    public static function postEmail($key)
191
-    {
192
-        $post = &self::$globalStateProvider->getPostSuperGlobal();
193
-        if (!array_key_exists($key, $post)) {
194
-            return null;
195
-        }
196
-
197
-        $filteredValue = filter_var($post[$key], FILTER_SANITIZE_EMAIL);
198
-
199
-        if ($filteredValue === false) {
200
-            return null;
201
-        }
202
-
203
-        return (string)$filteredValue;
204
-    }
205
-
206
-    /**
207
-     * @param string $key
208
-     *
209
-     * @return int|null
210
-     */
211
-    public static function postInt($key)
212
-    {
213
-        $post = &self::$globalStateProvider->getPostSuperGlobal();
214
-        if (!array_key_exists($key, $post)) {
215
-            return null;
216
-        }
217
-
218
-        $filteredValue = filter_var($post[$key], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
219
-
220
-        if ($filteredValue === null) {
221
-            return null;
222
-        }
223
-
224
-        return (int)$filteredValue;
225
-    }
226
-
227
-    /**
228
-     * @param string $key
229
-     *
230
-     * @return bool
231
-     */
232
-    public static function postBoolean($key)
233
-    {
234
-        $get = &self::$globalStateProvider->getPostSuperGlobal();
235
-        if (!array_key_exists($key, $get)) {
236
-            return false;
237
-        }
238
-
239
-        // presence of parameter only
240
-        if ($get[$key] === "") {
241
-            return true;
242
-        }
243
-
244
-        if (in_array($get[$key], array(false, 'no', 'off', 0, 'false'), true)) {
245
-            return false;
246
-        }
247
-
248
-        return true;
249
-    }
250
-
251
-    #endregion
252
-
253
-    #region GET variables
254
-
255
-    /**
256
-     * @param string $key
257
-     *
258
-     * @return bool
259
-     */
260
-    public static function getBoolean($key)
261
-    {
262
-        $get = &self::$globalStateProvider->getGetSuperGlobal();
263
-        if (!array_key_exists($key, $get)) {
264
-            return false;
265
-        }
266
-
267
-        // presence of parameter only
268
-        if ($get[$key] === "") {
269
-            return true;
270
-        }
271
-
272
-        if (in_array($get[$key], array(false, 'no', 'off', 0, 'false'), true)) {
273
-            return false;
274
-        }
275
-
276
-        return true;
277
-    }
278
-
279
-    /**
280
-     * @param string $key
281
-     *
282
-     * @return int|null
283
-     */
284
-    public static function getInt($key)
285
-    {
286
-        $get = &self::$globalStateProvider->getGetSuperGlobal();
287
-        if (!array_key_exists($key, $get)) {
288
-            return null;
289
-        }
290
-
291
-        $filteredValue = filter_var($get[$key], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
292
-
293
-        if ($filteredValue === null) {
294
-            return null;
295
-        }
296
-
297
-        return (int)$filteredValue;
298
-    }
299
-
300
-    /**
301
-     * @param string $key
302
-     *
303
-     * @return null|string
304
-     */
305
-    public static function getString($key)
306
-    {
307
-        $get = &self::$globalStateProvider->getGetSuperGlobal();
308
-        if (!array_key_exists($key, $get)) {
309
-            return null;
310
-        }
311
-
312
-        if ($get[$key] === "") {
313
-            return null;
314
-        }
315
-
316
-        return (string)$get[$key];
317
-    }
318
-
319
-    #endregion
320
-
321
-    /**
322
-     * Sets the logged-in user to the specified user.
323
-     *
324
-     * @param User $user
325
-     */
326
-    public static function setLoggedInUser(User $user)
327
-    {
328
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
329
-
330
-        $session['userID'] = $user->getId();
331
-        unset($session['partialLogin']);
332
-    }
333
-
334
-    /**
335
-     * Sets the post-login redirect
336
-     *
337
-     * @param string|null $uri The URI to redirect to
338
-     */
339
-    public static function setPostLoginRedirect($uri = null)
340
-    {
341
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
342
-
343
-        if ($uri === null) {
344
-            $uri = self::requestUri();
345
-        }
346
-
347
-        $session['returnTo'] = $uri;
348
-    }
349
-
350
-    /**
351
-     * @return string|null
352
-     */
353
-    public static function requestUri()
354
-    {
355
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
356
-
357
-        if (isset($server['REQUEST_URI'])) {
358
-            return $server['REQUEST_URI'];
359
-        }
360
-
361
-        return null;
362
-    }
363
-
364
-    /**
365
-     * Clears the post-login redirect
366
-     * @return string
367
-     */
368
-    public static function clearPostLoginRedirect()
369
-    {
370
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
371
-        if (array_key_exists('returnTo', $session)) {
372
-            $path = $session['returnTo'];
373
-            unset($session['returnTo']);
374
-
375
-            return $path;
376
-        }
377
-
378
-        return null;
379
-    }
380
-
381
-    /**
382
-     * @return string|null
383
-     */
384
-    public static function serverName()
385
-    {
386
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
387
-
388
-        if (isset($server['SERVER_NAME'])) {
389
-            return $server['SERVER_NAME'];
390
-        }
391
-
392
-        return null;
393
-    }
394
-
395
-    /**
396
-     * You probably only want to deal with this through SessionAlert.
397
-     * @return void
398
-     */
399
-    public static function clearSessionAlertData()
400
-    {
401
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
402
-        if (array_key_exists('alerts', $session)) {
403
-            unset($session['alerts']);
404
-        }
405
-    }
406
-
407
-    /**
408
-     * You probably only want to deal with this through SessionAlert.
409
-     *
410
-     * @return string[]
411
-     */
412
-    public static function getSessionAlertData()
413
-    {
414
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
415
-        if (array_key_exists('alerts', $session)) {
416
-            return $session['alerts'];
417
-        }
418
-
419
-        return array();
420
-    }
421
-
422
-    /**
423
-     * You probably only want to deal with this through SessionAlert.
424
-     *
425
-     * @param string[] $data
426
-     */
427
-    public static function setSessionAlertData($data)
428
-    {
429
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
430
-        $session['alerts'] = $data;
431
-    }
432
-
433
-    /**
434
-     * You probably only want to deal with this through TokenManager.
435
-     *
436
-     * @return string[]
437
-     */
438
-    public static function getSessionTokenData()
439
-    {
440
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
441
-        if (array_key_exists('tokens', $session)) {
442
-            return $session['tokens'];
443
-        }
444
-
445
-        return array();
446
-    }
447
-
448
-    /**
449
-     * You probably only want to deal with this through TokenManager.
450
-     *
451
-     * @param string[] $data
452
-     */
453
-    public static function setSessionTokenData($data)
454
-    {
455
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
456
-        $session['tokens'] = $data;
457
-    }
458
-
459
-    /**
460
-     * @param string $key
461
-     *
462
-     * @return mixed
463
-     */
464
-    public static function getSessionContext($key)
465
-    {
466
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
467
-
468
-        if (!isset($session['context'])) {
469
-            $session['context'] = array();
470
-        }
471
-
472
-        if (!isset($session['context'][$key])) {
473
-            return null;
474
-        }
475
-
476
-        return $session['context'][$key];
477
-    }
478
-
479
-    /**
480
-     * @param string $key
481
-     * @param mixed  $data
482
-     */
483
-    public static function setSessionContext($key, $data)
484
-    {
485
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
486
-
487
-        if (!isset($session['context'])) {
488
-            $session['context'] = array();
489
-        }
490
-
491
-        $session['context'][$key] = $data;
492
-    }
493
-
494
-    /**
495
-     * @return int|null
496
-     */
497
-    public static function getSessionUserId()
498
-    {
499
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
500
-
501
-        return isset($session['userID']) ? (int)$session['userID'] : null;
502
-    }
503
-
504
-    /**
505
-     * @param User $user
506
-     */
507
-    public static function setOAuthPartialLogin(User $user)
508
-    {
509
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
510
-        $session['oauthPartialLogin'] = $user->getId();
511
-    }
512
-
513
-    /**
514
-     * @return int|null
515
-     */
516
-    public static function getOAuthPartialLogin()
517
-    {
518
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
519
-
520
-        return isset($session['oauthPartialLogin']) ? (int)$session['oauthPartialLogin'] : null;
521
-    }
522
-
523
-    public static function setAuthPartialLogin($userId, $stage)
524
-    {
525
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
526
-        $session['authPartialLoginId'] = $userId;
527
-        $session['authPartialLoginStage'] = $stage;
528
-    }
529
-
530
-    public static function getAuthPartialLogin()
531
-    {
532
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
533
-
534
-        $userId = isset($session['authPartialLoginId']) ? (int)$session['authPartialLoginId'] : null;
535
-        $stage = isset($session['authPartialLoginStage']) ? (int)$session['authPartialLoginStage'] : null;
536
-
537
-        return array($userId, $stage);
538
-    }
539
-
540
-    public static function clearAuthPartialLogin()
541
-    {
542
-        $session = &self::$globalStateProvider->getSessionSuperGlobal();
543
-        unset($session['authPartialLoginId']);
544
-        unset($session['authPartialLoginStage']);
545
-    }
546
-
547
-    /**
548
-     * @return null|string
549
-     */
550
-    public static function userAgent()
551
-    {
552
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
553
-
554
-        if (isset($server['HTTP_USER_AGENT'])) {
555
-            return $server['HTTP_USER_AGENT'];
556
-        }
557
-
558
-        return null;
559
-    }
560
-
561
-    /**
562
-     * @return null|string
563
-     */
564
-    public static function scriptName()
565
-    {
566
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
567
-
568
-        if (isset($server['SCRIPT_NAME'])) {
569
-            return $server['SCRIPT_NAME'];
570
-        }
571
-
572
-        return null;
573
-    }
574
-
575
-    /**
576
-     * @return null|string
577
-     */
578
-    public static function origin()
579
-    {
580
-        $server = &self::$globalStateProvider->getServerSuperGlobal();
581
-
582
-        if (isset($server['HTTP_ORIGIN'])) {
583
-            return $server['HTTP_ORIGIN'];
584
-        }
585
-
586
-        return null;
587
-    }
25
+	/**
26
+	 * @var \Waca\Providers\GlobalState\IGlobalStateProvider Provides access to the global state.
27
+	 */
28
+	private static $globalStateProvider;
29
+
30
+	/**
31
+	 * Returns a boolean value if the request was submitted with the HTTP POST method.
32
+	 * @return bool
33
+	 */
34
+	public static function wasPosted()
35
+	{
36
+		return self::method() === 'POST';
37
+	}
38
+
39
+	/**
40
+	 * Gets the HTTP Method used
41
+	 * @return string|null
42
+	 */
43
+	public static function method()
44
+	{
45
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
46
+
47
+		if (isset($server['REQUEST_METHOD'])) {
48
+			return $server['REQUEST_METHOD'];
49
+		}
50
+
51
+		return null;
52
+	}
53
+
54
+	/**
55
+	 * Gets a boolean value stating whether the request was served over HTTPS or not.
56
+	 * @return bool
57
+	 */
58
+	public static function isHttps()
59
+	{
60
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
61
+
62
+		if (isset($server['HTTP_X_FORWARDED_PROTO'])) {
63
+			if ($server['HTTP_X_FORWARDED_PROTO'] === 'https') {
64
+				// Client <=> Proxy is encrypted
65
+				return true;
66
+			}
67
+			else {
68
+				// Proxy <=> Server link unknown, Client <=> Proxy is not encrypted.
69
+				return false;
70
+			}
71
+		}
72
+
73
+		if (isset($server['HTTPS'])) {
74
+			if ($server['HTTPS'] === 'off') {
75
+				// ISAPI on IIS breaks the spec. :(
76
+				return false;
77
+			}
78
+
79
+			if ($server['HTTPS'] !== '') {
80
+				// Set to a non-empty value
81
+				return true;
82
+			}
83
+		}
84
+
85
+		return false;
86
+	}
87
+
88
+	/**
89
+	 * Gets the path info
90
+	 *
91
+	 * @return array Array of path info segments
92
+	 */
93
+	public static function pathInfo()
94
+	{
95
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
96
+		if (!isset($server['PATH_INFO'])) {
97
+			return array();
98
+		}
99
+
100
+		$exploded = explode('/', $server['PATH_INFO']);
101
+
102
+		// filter out empty values, and reindex from zero. Notably, the first element is always zero, since it starts
103
+		// with a /
104
+		return array_values(array_filter($exploded));
105
+	}
106
+
107
+	/**
108
+	 * Gets the remote address of the web request
109
+	 * @return null|string
110
+	 */
111
+	public static function remoteAddress()
112
+	{
113
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
114
+
115
+		if (isset($server['REMOTE_ADDR'])) {
116
+			return $server['REMOTE_ADDR'];
117
+		}
118
+
119
+		return null;
120
+	}
121
+
122
+	/**
123
+	 * Gets the remote address of the web request
124
+	 * @return null|string
125
+	 */
126
+	public static function httpHost()
127
+	{
128
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
129
+
130
+		if (isset($server['HTTP_HOST'])) {
131
+			return $server['HTTP_HOST'];
132
+		}
133
+
134
+		return null;
135
+	}
136
+
137
+	/**
138
+	 * Gets the XFF header contents for the web request
139
+	 * @return null|string
140
+	 */
141
+	public static function forwardedAddress()
142
+	{
143
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
144
+
145
+		if (isset($server['HTTP_X_FORWARDED_FOR'])) {
146
+			return $server['HTTP_X_FORWARDED_FOR'];
147
+		}
148
+
149
+		return null;
150
+	}
151
+
152
+	/**
153
+	 * Sets the global state provider.
154
+	 *
155
+	 * Almost guaranteed this is not the method you want in production code.
156
+	 *
157
+	 * @param \Waca\Providers\GlobalState\IGlobalStateProvider $globalState
158
+	 */
159
+	public static function setGlobalStateProvider($globalState)
160
+	{
161
+		self::$globalStateProvider = $globalState;
162
+	}
163
+
164
+	#region POST variables
165
+
166
+	/**
167
+	 * @param string $key
168
+	 *
169
+	 * @return null|string
170
+	 */
171
+	public static function postString($key)
172
+	{
173
+		$post = &self::$globalStateProvider->getPostSuperGlobal();
174
+		if (!array_key_exists($key, $post)) {
175
+			return null;
176
+		}
177
+
178
+		if ($post[$key] === "") {
179
+			return null;
180
+		}
181
+
182
+		return (string)$post[$key];
183
+	}
184
+
185
+	/**
186
+	 * @param string $key
187
+	 *
188
+	 * @return null|string
189
+	 */
190
+	public static function postEmail($key)
191
+	{
192
+		$post = &self::$globalStateProvider->getPostSuperGlobal();
193
+		if (!array_key_exists($key, $post)) {
194
+			return null;
195
+		}
196
+
197
+		$filteredValue = filter_var($post[$key], FILTER_SANITIZE_EMAIL);
198
+
199
+		if ($filteredValue === false) {
200
+			return null;
201
+		}
202
+
203
+		return (string)$filteredValue;
204
+	}
205
+
206
+	/**
207
+	 * @param string $key
208
+	 *
209
+	 * @return int|null
210
+	 */
211
+	public static function postInt($key)
212
+	{
213
+		$post = &self::$globalStateProvider->getPostSuperGlobal();
214
+		if (!array_key_exists($key, $post)) {
215
+			return null;
216
+		}
217
+
218
+		$filteredValue = filter_var($post[$key], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
219
+
220
+		if ($filteredValue === null) {
221
+			return null;
222
+		}
223
+
224
+		return (int)$filteredValue;
225
+	}
226
+
227
+	/**
228
+	 * @param string $key
229
+	 *
230
+	 * @return bool
231
+	 */
232
+	public static function postBoolean($key)
233
+	{
234
+		$get = &self::$globalStateProvider->getPostSuperGlobal();
235
+		if (!array_key_exists($key, $get)) {
236
+			return false;
237
+		}
238
+
239
+		// presence of parameter only
240
+		if ($get[$key] === "") {
241
+			return true;
242
+		}
243
+
244
+		if (in_array($get[$key], array(false, 'no', 'off', 0, 'false'), true)) {
245
+			return false;
246
+		}
247
+
248
+		return true;
249
+	}
250
+
251
+	#endregion
252
+
253
+	#region GET variables
254
+
255
+	/**
256
+	 * @param string $key
257
+	 *
258
+	 * @return bool
259
+	 */
260
+	public static function getBoolean($key)
261
+	{
262
+		$get = &self::$globalStateProvider->getGetSuperGlobal();
263
+		if (!array_key_exists($key, $get)) {
264
+			return false;
265
+		}
266
+
267
+		// presence of parameter only
268
+		if ($get[$key] === "") {
269
+			return true;
270
+		}
271
+
272
+		if (in_array($get[$key], array(false, 'no', 'off', 0, 'false'), true)) {
273
+			return false;
274
+		}
275
+
276
+		return true;
277
+	}
278
+
279
+	/**
280
+	 * @param string $key
281
+	 *
282
+	 * @return int|null
283
+	 */
284
+	public static function getInt($key)
285
+	{
286
+		$get = &self::$globalStateProvider->getGetSuperGlobal();
287
+		if (!array_key_exists($key, $get)) {
288
+			return null;
289
+		}
290
+
291
+		$filteredValue = filter_var($get[$key], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
292
+
293
+		if ($filteredValue === null) {
294
+			return null;
295
+		}
296
+
297
+		return (int)$filteredValue;
298
+	}
299
+
300
+	/**
301
+	 * @param string $key
302
+	 *
303
+	 * @return null|string
304
+	 */
305
+	public static function getString($key)
306
+	{
307
+		$get = &self::$globalStateProvider->getGetSuperGlobal();
308
+		if (!array_key_exists($key, $get)) {
309
+			return null;
310
+		}
311
+
312
+		if ($get[$key] === "") {
313
+			return null;
314
+		}
315
+
316
+		return (string)$get[$key];
317
+	}
318
+
319
+	#endregion
320
+
321
+	/**
322
+	 * Sets the logged-in user to the specified user.
323
+	 *
324
+	 * @param User $user
325
+	 */
326
+	public static function setLoggedInUser(User $user)
327
+	{
328
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
329
+
330
+		$session['userID'] = $user->getId();
331
+		unset($session['partialLogin']);
332
+	}
333
+
334
+	/**
335
+	 * Sets the post-login redirect
336
+	 *
337
+	 * @param string|null $uri The URI to redirect to
338
+	 */
339
+	public static function setPostLoginRedirect($uri = null)
340
+	{
341
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
342
+
343
+		if ($uri === null) {
344
+			$uri = self::requestUri();
345
+		}
346
+
347
+		$session['returnTo'] = $uri;
348
+	}
349
+
350
+	/**
351
+	 * @return string|null
352
+	 */
353
+	public static function requestUri()
354
+	{
355
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
356
+
357
+		if (isset($server['REQUEST_URI'])) {
358
+			return $server['REQUEST_URI'];
359
+		}
360
+
361
+		return null;
362
+	}
363
+
364
+	/**
365
+	 * Clears the post-login redirect
366
+	 * @return string
367
+	 */
368
+	public static function clearPostLoginRedirect()
369
+	{
370
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
371
+		if (array_key_exists('returnTo', $session)) {
372
+			$path = $session['returnTo'];
373
+			unset($session['returnTo']);
374
+
375
+			return $path;
376
+		}
377
+
378
+		return null;
379
+	}
380
+
381
+	/**
382
+	 * @return string|null
383
+	 */
384
+	public static function serverName()
385
+	{
386
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
387
+
388
+		if (isset($server['SERVER_NAME'])) {
389
+			return $server['SERVER_NAME'];
390
+		}
391
+
392
+		return null;
393
+	}
394
+
395
+	/**
396
+	 * You probably only want to deal with this through SessionAlert.
397
+	 * @return void
398
+	 */
399
+	public static function clearSessionAlertData()
400
+	{
401
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
402
+		if (array_key_exists('alerts', $session)) {
403
+			unset($session['alerts']);
404
+		}
405
+	}
406
+
407
+	/**
408
+	 * You probably only want to deal with this through SessionAlert.
409
+	 *
410
+	 * @return string[]
411
+	 */
412
+	public static function getSessionAlertData()
413
+	{
414
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
415
+		if (array_key_exists('alerts', $session)) {
416
+			return $session['alerts'];
417
+		}
418
+
419
+		return array();
420
+	}
421
+
422
+	/**
423
+	 * You probably only want to deal with this through SessionAlert.
424
+	 *
425
+	 * @param string[] $data
426
+	 */
427
+	public static function setSessionAlertData($data)
428
+	{
429
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
430
+		$session['alerts'] = $data;
431
+	}
432
+
433
+	/**
434
+	 * You probably only want to deal with this through TokenManager.
435
+	 *
436
+	 * @return string[]
437
+	 */
438
+	public static function getSessionTokenData()
439
+	{
440
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
441
+		if (array_key_exists('tokens', $session)) {
442
+			return $session['tokens'];
443
+		}
444
+
445
+		return array();
446
+	}
447
+
448
+	/**
449
+	 * You probably only want to deal with this through TokenManager.
450
+	 *
451
+	 * @param string[] $data
452
+	 */
453
+	public static function setSessionTokenData($data)
454
+	{
455
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
456
+		$session['tokens'] = $data;
457
+	}
458
+
459
+	/**
460
+	 * @param string $key
461
+	 *
462
+	 * @return mixed
463
+	 */
464
+	public static function getSessionContext($key)
465
+	{
466
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
467
+
468
+		if (!isset($session['context'])) {
469
+			$session['context'] = array();
470
+		}
471
+
472
+		if (!isset($session['context'][$key])) {
473
+			return null;
474
+		}
475
+
476
+		return $session['context'][$key];
477
+	}
478
+
479
+	/**
480
+	 * @param string $key
481
+	 * @param mixed  $data
482
+	 */
483
+	public static function setSessionContext($key, $data)
484
+	{
485
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
486
+
487
+		if (!isset($session['context'])) {
488
+			$session['context'] = array();
489
+		}
490
+
491
+		$session['context'][$key] = $data;
492
+	}
493
+
494
+	/**
495
+	 * @return int|null
496
+	 */
497
+	public static function getSessionUserId()
498
+	{
499
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
500
+
501
+		return isset($session['userID']) ? (int)$session['userID'] : null;
502
+	}
503
+
504
+	/**
505
+	 * @param User $user
506
+	 */
507
+	public static function setOAuthPartialLogin(User $user)
508
+	{
509
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
510
+		$session['oauthPartialLogin'] = $user->getId();
511
+	}
512
+
513
+	/**
514
+	 * @return int|null
515
+	 */
516
+	public static function getOAuthPartialLogin()
517
+	{
518
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
519
+
520
+		return isset($session['oauthPartialLogin']) ? (int)$session['oauthPartialLogin'] : null;
521
+	}
522
+
523
+	public static function setAuthPartialLogin($userId, $stage)
524
+	{
525
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
526
+		$session['authPartialLoginId'] = $userId;
527
+		$session['authPartialLoginStage'] = $stage;
528
+	}
529
+
530
+	public static function getAuthPartialLogin()
531
+	{
532
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
533
+
534
+		$userId = isset($session['authPartialLoginId']) ? (int)$session['authPartialLoginId'] : null;
535
+		$stage = isset($session['authPartialLoginStage']) ? (int)$session['authPartialLoginStage'] : null;
536
+
537
+		return array($userId, $stage);
538
+	}
539
+
540
+	public static function clearAuthPartialLogin()
541
+	{
542
+		$session = &self::$globalStateProvider->getSessionSuperGlobal();
543
+		unset($session['authPartialLoginId']);
544
+		unset($session['authPartialLoginStage']);
545
+	}
546
+
547
+	/**
548
+	 * @return null|string
549
+	 */
550
+	public static function userAgent()
551
+	{
552
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
553
+
554
+		if (isset($server['HTTP_USER_AGENT'])) {
555
+			return $server['HTTP_USER_AGENT'];
556
+		}
557
+
558
+		return null;
559
+	}
560
+
561
+	/**
562
+	 * @return null|string
563
+	 */
564
+	public static function scriptName()
565
+	{
566
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
567
+
568
+		if (isset($server['SCRIPT_NAME'])) {
569
+			return $server['SCRIPT_NAME'];
570
+		}
571
+
572
+		return null;
573
+	}
574
+
575
+	/**
576
+	 * @return null|string
577
+	 */
578
+	public static function origin()
579
+	{
580
+		$server = &self::$globalStateProvider->getServerSuperGlobal();
581
+
582
+		if (isset($server['HTTP_ORIGIN'])) {
583
+			return $server['HTTP_ORIGIN'];
584
+		}
585
+
586
+		return null;
587
+	}
588 588
 }
Please login to merge, or discard this patch.
includes/Router/RequestRouter.php 1 patch
Indentation   +439 added lines, -439 removed lines patch added patch discarded remove patch
@@ -63,443 +63,443 @@
 block discarded – undo
63 63
  */
64 64
 class RequestRouter implements IRequestRouter
65 65
 {
66
-    /**
67
-     * This is the core routing table for the application. The basic idea is:
68
-     *
69
-     *      array(
70
-     *          "foo" =>
71
-     *              array(
72
-     *                  "class"   => PageFoo::class,
73
-     *                  "actions" => array("bar", "other")
74
-     *              ),
75
-     * );
76
-     *
77
-     * Things to note:
78
-     *     - If no page is requested, we go to PageMain. PageMain can't have actions defined.
79
-     *
80
-     *     - If a page is defined and requested, but no action is requested, go to that page's main() method
81
-     *     - If a page is defined and requested, and an action is defined and requested, go to that action's method.
82
-     *     - If a page is defined and requested, and an action NOT defined and requested, go to Page404 and it's main()
83
-     *       method.
84
-     *     - If a page is NOT defined and requested, go to Page404 and it's main() method.
85
-     *
86
-     *     - Query parameters are ignored.
87
-     *
88
-     * The key point here is request routing with validation that this is allowed, before we start hitting the
89
-     * filesystem through the AutoLoader, and opening random files. Also, so that we validate the action requested
90
-     * before we start calling random methods through the web UI.
91
-     *
92
-     * Examples:
93
-     * /internal.php                => returns instance of PageMain, routed to main()
94
-     * /internal.php?query          => returns instance of PageMain, routed to main()
95
-     * /internal.php/foo            => returns instance of PageFoo, routed to main()
96
-     * /internal.php/foo?query      => returns instance of PageFoo, routed to main()
97
-     * /internal.php/foo/bar        => returns instance of PageFoo, routed to bar()
98
-     * /internal.php/foo/bar?query  => returns instance of PageFoo, routed to bar()
99
-     * /internal.php/foo/baz        => returns instance of Page404, routed to main()
100
-     * /internal.php/foo/baz?query  => returns instance of Page404, routed to main()
101
-     * /internal.php/bar            => returns instance of Page404, routed to main()
102
-     * /internal.php/bar?query      => returns instance of Page404, routed to main()
103
-     * /internal.php/bar/baz        => returns instance of Page404, routed to main()
104
-     * /internal.php/bar/baz?query  => returns instance of Page404, routed to main()
105
-     *
106
-     * Take care when changing this - a lot of places rely on the array key for redirects and other links. If you need
107
-     * to change the key, then you'll likely have to update a lot of files.
108
-     *
109
-     * @var array
110
-     */
111
-    private $routeMap = array(
112
-
113
-        //////////////////////////////////////////////////////////////////////////////////////////////////
114
-        // Login and registration
115
-        'logout'                      =>
116
-            array(
117
-                'class'   => PageLogout::class,
118
-                'actions' => array(),
119
-            ),
120
-        'login'                       =>
121
-            array(
122
-                'class'   => PagePasswordLogin::class,
123
-                'actions' => array(),
124
-            ),
125
-        'login/otp'                   =>
126
-            array(
127
-                'class'   => PageOtpLogin::class,
128
-                'actions' => array(),
129
-            ),
130
-        'login/u2f'                   =>
131
-            array(
132
-                'class'   => PageU2FLogin::class,
133
-                'actions' => array(),
134
-            ),
135
-        'forgotPassword'              =>
136
-            array(
137
-                'class'   => PageForgotPassword::class,
138
-                'actions' => array('reset'),
139
-            ),
140
-        'register'                    =>
141
-            array(
142
-                'class'   => PageRegisterOption::class,
143
-                'actions' => array(),
144
-            ),
145
-        'register/standard'           =>
146
-            array(
147
-                'class'   => PageRegisterStandard::class,
148
-                'actions' => array('done'),
149
-            ),
150
-
151
-        //////////////////////////////////////////////////////////////////////////////////////////////////
152
-        // Discovery
153
-        'search'                      =>
154
-            array(
155
-                'class'   => PageSearch::class,
156
-                'actions' => array(),
157
-            ),
158
-        'logs'                        =>
159
-            array(
160
-                'class'   => PageLog::class,
161
-                'actions' => array(),
162
-            ),
163
-
164
-        //////////////////////////////////////////////////////////////////////////////////////////////////
165
-        // Administration
166
-        'bans'                        =>
167
-            array(
168
-                'class'   => PageBan::class,
169
-                'actions' => array('set', 'remove'),
170
-            ),
171
-        'userManagement'              =>
172
-            array(
173
-                'class'   => PageUserManagement::class,
174
-                'actions' => array(
175
-                    'approve',
176
-                    'decline',
177
-                    'rename',
178
-                    'editUser',
179
-                    'suspend',
180
-                    'editRoles',
181
-                ),
182
-            ),
183
-        'siteNotice'                  =>
184
-            array(
185
-                'class'   => PageSiteNotice::class,
186
-                'actions' => array(),
187
-            ),
188
-        'emailManagement'             =>
189
-            array(
190
-                'class'   => PageEmailManagement::class,
191
-                'actions' => array('create', 'edit', 'view'),
192
-            ),
193
-        'jobQueue'                    =>
194
-            array(
195
-                'class'   => PageJobQueue::class,
196
-                'actions' => array('acknowledge', 'requeue', 'view', 'all'),
197
-            ),
198
-
199
-        //////////////////////////////////////////////////////////////////////////////////////////////////
200
-        // Personal preferences
201
-        'preferences'                 =>
202
-            array(
203
-                'class'   => PagePreferences::class,
204
-                'actions' => array(),
205
-            ),
206
-        'changePassword'              =>
207
-            array(
208
-                'class'   => PageChangePassword::class,
209
-                'actions' => array(),
210
-            ),
211
-        'multiFactor'                 =>
212
-            array(
213
-                'class'   => PageMultiFactor::class,
214
-                'actions' => array(
215
-                    'scratch',
216
-                    'enableYubikeyOtp',
217
-                    'disableYubikeyOtp',
218
-                    'enableTotp',
219
-                    'disableTotp',
220
-                    'enableU2F',
221
-                    'disableU2F',
222
-                ),
223
-            ),
224
-        'oauth'                       =>
225
-            array(
226
-                'class'   => PageOAuth::class,
227
-                'actions' => array('detach', 'attach'),
228
-            ),
229
-        'oauth/callback'              =>
230
-            array(
231
-                'class'   => PageOAuthCallback::class,
232
-                'actions' => array('authorise', 'create'),
233
-            ),
234
-
235
-        //////////////////////////////////////////////////////////////////////////////////////////////////
236
-        // Welcomer configuration
237
-        'welcomeTemplates'            =>
238
-            array(
239
-                'class'   => PageWelcomeTemplateManagement::class,
240
-                'actions' => array('select', 'edit', 'delete', 'add', 'view'),
241
-            ),
242
-
243
-        //////////////////////////////////////////////////////////////////////////////////////////////////
244
-        // Statistics
245
-        'statistics'                  =>
246
-            array(
247
-                'class'   => StatsMain::class,
248
-                'actions' => array(),
249
-            ),
250
-        'statistics/fastCloses'       =>
251
-            array(
252
-                'class'   => StatsFastCloses::class,
253
-                'actions' => array(),
254
-            ),
255
-        'statistics/inactiveUsers'    =>
256
-            array(
257
-                'class'   => StatsInactiveUsers::class,
258
-                'actions' => array(),
259
-            ),
260
-        'statistics/monthlyStats'     =>
261
-            array(
262
-                'class'   => StatsMonthlyStats::class,
263
-                'actions' => array(),
264
-            ),
265
-        'statistics/reservedRequests' =>
266
-            array(
267
-                'class'   => StatsReservedRequests::class,
268
-                'actions' => array(),
269
-            ),
270
-        'statistics/templateStats'    =>
271
-            array(
272
-                'class'   => StatsTemplateStats::class,
273
-                'actions' => array(),
274
-            ),
275
-        'statistics/topCreators'      =>
276
-            array(
277
-                'class'   => StatsTopCreators::class,
278
-                'actions' => array(),
279
-            ),
280
-        'statistics/users'            =>
281
-            array(
282
-                'class'   => StatsUsers::class,
283
-                'actions' => array('detail'),
284
-            ),
285
-
286
-        //////////////////////////////////////////////////////////////////////////////////////////////////
287
-        // Zoom page
288
-        'viewRequest'                 =>
289
-            array(
290
-                'class'   => PageViewRequest::class,
291
-                'actions' => array(),
292
-            ),
293
-        'viewRequest/reserve'         =>
294
-            array(
295
-                'class'   => PageReservation::class,
296
-                'actions' => array(),
297
-            ),
298
-        'viewRequest/breakReserve'    =>
299
-            array(
300
-                'class'   => PageBreakReservation::class,
301
-                'actions' => array(),
302
-            ),
303
-        'viewRequest/defer'           =>
304
-            array(
305
-                'class'   => PageDeferRequest::class,
306
-                'actions' => array(),
307
-            ),
308
-        'viewRequest/comment'         =>
309
-            array(
310
-                'class'   => PageComment::class,
311
-                'actions' => array(),
312
-            ),
313
-        'viewRequest/sendToUser'      =>
314
-            array(
315
-                'class'   => PageSendToUser::class,
316
-                'actions' => array(),
317
-            ),
318
-        'viewRequest/close'           =>
319
-            array(
320
-                'class'   => PageCloseRequest::class,
321
-                'actions' => array(),
322
-            ),
323
-        'viewRequest/create'          =>
324
-            array(
325
-                'class'   => PageCreateRequest::class,
326
-                'actions' => array(),
327
-            ),
328
-        'viewRequest/drop'            =>
329
-            array(
330
-                'class'   => PageDropRequest::class,
331
-                'actions' => array(),
332
-            ),
333
-        'viewRequest/custom'          =>
334
-            array(
335
-                'class'   => PageCustomClose::class,
336
-                'actions' => array(),
337
-            ),
338
-        'editComment'                 =>
339
-            array(
340
-                'class'   => PageEditComment::class,
341
-                'actions' => array(),
342
-            ),
343
-
344
-        //////////////////////////////////////////////////////////////////////////////////////////////////
345
-        // Misc stuff
346
-        'team'                        =>
347
-            array(
348
-                'class'   => PageTeam::class,
349
-                'actions' => array(),
350
-            ),
351
-        'requestList'                 =>
352
-            array(
353
-                'class'   => PageExpandedRequestList::class,
354
-                'actions' => array(),
355
-            ),
356
-        'xffdemo'                     =>
357
-            array(
358
-                'class'   => PageXffDemo::class,
359
-                'actions' => array(),
360
-            ),
361
-    );
362
-
363
-    /**
364
-     * @return IRoutedTask
365
-     * @throws Exception
366
-     */
367
-    final public function route()
368
-    {
369
-        $pathInfo = WebRequest::pathInfo();
370
-
371
-        list($pageClass, $action) = $this->getRouteFromPath($pathInfo);
372
-
373
-        /** @var IRoutedTask $page */
374
-        $page = new $pageClass();
375
-
376
-        // Dynamic creation, so we've got to be careful here. We can't use built-in language type protection, so
377
-        // let's use our own.
378
-        if (!($page instanceof IRoutedTask)) {
379
-            throw new Exception('Expected a page, but this is not a page.');
380
-        }
381
-
382
-        // OK, I'm happy at this point that we know we're running a page, and we know it's probably what we want if it
383
-        // inherits PageBase and has been created from the routing map.
384
-        $page->setRoute($action);
385
-
386
-        return $page;
387
-    }
388
-
389
-    /**
390
-     * @param $pathInfo
391
-     *
392
-     * @return array
393
-     */
394
-    protected function getRouteFromPath($pathInfo)
395
-    {
396
-        if (count($pathInfo) === 0) {
397
-            // No pathInfo, so no page to load. Load the main page.
398
-            return $this->getDefaultRoute();
399
-        }
400
-        elseif (count($pathInfo) === 1) {
401
-            // Exactly one path info segment, it's got to be a page.
402
-            $classSegment = $pathInfo[0];
403
-
404
-            return $this->routeSinglePathSegment($classSegment);
405
-        }
406
-
407
-        // OK, we have two or more segments now.
408
-        if (count($pathInfo) > 2) {
409
-            // Let's handle more than two, and collapse it down into two.
410
-            $requestedAction = array_pop($pathInfo);
411
-            $classSegment = implode('/', $pathInfo);
412
-        }
413
-        else {
414
-            // Two path info segments.
415
-            $classSegment = $pathInfo[0];
416
-            $requestedAction = $pathInfo[1];
417
-        }
418
-
419
-        $routeMap = $this->routePathSegments($classSegment, $requestedAction);
420
-
421
-        if ($routeMap[0] === Page404::class) {
422
-            $routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
423
-        }
424
-
425
-        return $routeMap;
426
-    }
427
-
428
-    /**
429
-     * @param $classSegment
430
-     *
431
-     * @return array
432
-     */
433
-    final protected function routeSinglePathSegment($classSegment)
434
-    {
435
-        $routeMap = $this->getRouteMap();
436
-        if (array_key_exists($classSegment, $routeMap)) {
437
-            // Route exists, but we don't have an action in path info, so default to main.
438
-            $pageClass = $routeMap[$classSegment]['class'];
439
-            $action = 'main';
440
-
441
-            return array($pageClass, $action);
442
-        }
443
-        else {
444
-            // Doesn't exist in map. Fall back to 404
445
-            $pageClass = Page404::class;
446
-            $action = "main";
447
-
448
-            return array($pageClass, $action);
449
-        }
450
-    }
451
-
452
-    /**
453
-     * @param $classSegment
454
-     * @param $requestedAction
455
-     *
456
-     * @return array
457
-     */
458
-    final protected function routePathSegments($classSegment, $requestedAction)
459
-    {
460
-        $routeMap = $this->getRouteMap();
461
-        if (array_key_exists($classSegment, $routeMap)) {
462
-            // Route exists, but we don't have an action in path info, so default to main.
463
-
464
-            if (isset($routeMap[$classSegment]['actions'])
465
-                && array_search($requestedAction, $routeMap[$classSegment]['actions']) !== false
466
-            ) {
467
-                // Action exists in allowed action list. Allow both the page and the action
468
-                $pageClass = $routeMap[$classSegment]['class'];
469
-                $action = $requestedAction;
470
-
471
-                return array($pageClass, $action);
472
-            }
473
-            else {
474
-                // Valid page, invalid action. 404 our way out.
475
-                $pageClass = Page404::class;
476
-                $action = 'main';
477
-
478
-                return array($pageClass, $action);
479
-            }
480
-        }
481
-        else {
482
-            // Class doesn't exist in map. Fall back to 404
483
-            $pageClass = Page404::class;
484
-            $action = 'main';
485
-
486
-            return array($pageClass, $action);
487
-        }
488
-    }
489
-
490
-    /**
491
-     * @return array
492
-     */
493
-    protected function getRouteMap()
494
-    {
495
-        return $this->routeMap;
496
-    }
497
-
498
-    /**
499
-     * @return callable
500
-     */
501
-    protected function getDefaultRoute()
502
-    {
503
-        return array(PageMain::class, "main");
504
-    }
66
+	/**
67
+	 * This is the core routing table for the application. The basic idea is:
68
+	 *
69
+	 *      array(
70
+	 *          "foo" =>
71
+	 *              array(
72
+	 *                  "class"   => PageFoo::class,
73
+	 *                  "actions" => array("bar", "other")
74
+	 *              ),
75
+	 * );
76
+	 *
77
+	 * Things to note:
78
+	 *     - If no page is requested, we go to PageMain. PageMain can't have actions defined.
79
+	 *
80
+	 *     - If a page is defined and requested, but no action is requested, go to that page's main() method
81
+	 *     - If a page is defined and requested, and an action is defined and requested, go to that action's method.
82
+	 *     - If a page is defined and requested, and an action NOT defined and requested, go to Page404 and it's main()
83
+	 *       method.
84
+	 *     - If a page is NOT defined and requested, go to Page404 and it's main() method.
85
+	 *
86
+	 *     - Query parameters are ignored.
87
+	 *
88
+	 * The key point here is request routing with validation that this is allowed, before we start hitting the
89
+	 * filesystem through the AutoLoader, and opening random files. Also, so that we validate the action requested
90
+	 * before we start calling random methods through the web UI.
91
+	 *
92
+	 * Examples:
93
+	 * /internal.php                => returns instance of PageMain, routed to main()
94
+	 * /internal.php?query          => returns instance of PageMain, routed to main()
95
+	 * /internal.php/foo            => returns instance of PageFoo, routed to main()
96
+	 * /internal.php/foo?query      => returns instance of PageFoo, routed to main()
97
+	 * /internal.php/foo/bar        => returns instance of PageFoo, routed to bar()
98
+	 * /internal.php/foo/bar?query  => returns instance of PageFoo, routed to bar()
99
+	 * /internal.php/foo/baz        => returns instance of Page404, routed to main()
100
+	 * /internal.php/foo/baz?query  => returns instance of Page404, routed to main()
101
+	 * /internal.php/bar            => returns instance of Page404, routed to main()
102
+	 * /internal.php/bar?query      => returns instance of Page404, routed to main()
103
+	 * /internal.php/bar/baz        => returns instance of Page404, routed to main()
104
+	 * /internal.php/bar/baz?query  => returns instance of Page404, routed to main()
105
+	 *
106
+	 * Take care when changing this - a lot of places rely on the array key for redirects and other links. If you need
107
+	 * to change the key, then you'll likely have to update a lot of files.
108
+	 *
109
+	 * @var array
110
+	 */
111
+	private $routeMap = array(
112
+
113
+		//////////////////////////////////////////////////////////////////////////////////////////////////
114
+		// Login and registration
115
+		'logout'                      =>
116
+			array(
117
+				'class'   => PageLogout::class,
118
+				'actions' => array(),
119
+			),
120
+		'login'                       =>
121
+			array(
122
+				'class'   => PagePasswordLogin::class,
123
+				'actions' => array(),
124
+			),
125
+		'login/otp'                   =>
126
+			array(
127
+				'class'   => PageOtpLogin::class,
128
+				'actions' => array(),
129
+			),
130
+		'login/u2f'                   =>
131
+			array(
132
+				'class'   => PageU2FLogin::class,
133
+				'actions' => array(),
134
+			),
135
+		'forgotPassword'              =>
136
+			array(
137
+				'class'   => PageForgotPassword::class,
138
+				'actions' => array('reset'),
139
+			),
140
+		'register'                    =>
141
+			array(
142
+				'class'   => PageRegisterOption::class,
143
+				'actions' => array(),
144
+			),
145
+		'register/standard'           =>
146
+			array(
147
+				'class'   => PageRegisterStandard::class,
148
+				'actions' => array('done'),
149
+			),
150
+
151
+		//////////////////////////////////////////////////////////////////////////////////////////////////
152
+		// Discovery
153
+		'search'                      =>
154
+			array(
155
+				'class'   => PageSearch::class,
156
+				'actions' => array(),
157
+			),
158
+		'logs'                        =>
159
+			array(
160
+				'class'   => PageLog::class,
161
+				'actions' => array(),
162
+			),
163
+
164
+		//////////////////////////////////////////////////////////////////////////////////////////////////
165
+		// Administration
166
+		'bans'                        =>
167
+			array(
168
+				'class'   => PageBan::class,
169
+				'actions' => array('set', 'remove'),
170
+			),
171
+		'userManagement'              =>
172
+			array(
173
+				'class'   => PageUserManagement::class,
174
+				'actions' => array(
175
+					'approve',
176
+					'decline',
177
+					'rename',
178
+					'editUser',
179
+					'suspend',
180
+					'editRoles',
181
+				),
182
+			),
183
+		'siteNotice'                  =>
184
+			array(
185
+				'class'   => PageSiteNotice::class,
186
+				'actions' => array(),
187
+			),
188
+		'emailManagement'             =>
189
+			array(
190
+				'class'   => PageEmailManagement::class,
191
+				'actions' => array('create', 'edit', 'view'),
192
+			),
193
+		'jobQueue'                    =>
194
+			array(
195
+				'class'   => PageJobQueue::class,
196
+				'actions' => array('acknowledge', 'requeue', 'view', 'all'),
197
+			),
198
+
199
+		//////////////////////////////////////////////////////////////////////////////////////////////////
200
+		// Personal preferences
201
+		'preferences'                 =>
202
+			array(
203
+				'class'   => PagePreferences::class,
204
+				'actions' => array(),
205
+			),
206
+		'changePassword'              =>
207
+			array(
208
+				'class'   => PageChangePassword::class,
209
+				'actions' => array(),
210
+			),
211
+		'multiFactor'                 =>
212
+			array(
213
+				'class'   => PageMultiFactor::class,
214
+				'actions' => array(
215
+					'scratch',
216
+					'enableYubikeyOtp',
217
+					'disableYubikeyOtp',
218
+					'enableTotp',
219
+					'disableTotp',
220
+					'enableU2F',
221
+					'disableU2F',
222
+				),
223
+			),
224
+		'oauth'                       =>
225
+			array(
226
+				'class'   => PageOAuth::class,
227
+				'actions' => array('detach', 'attach'),
228
+			),
229
+		'oauth/callback'              =>
230
+			array(
231
+				'class'   => PageOAuthCallback::class,
232
+				'actions' => array('authorise', 'create'),
233
+			),
234
+
235
+		//////////////////////////////////////////////////////////////////////////////////////////////////
236
+		// Welcomer configuration
237
+		'welcomeTemplates'            =>
238
+			array(
239
+				'class'   => PageWelcomeTemplateManagement::class,
240
+				'actions' => array('select', 'edit', 'delete', 'add', 'view'),
241
+			),
242
+
243
+		//////////////////////////////////////////////////////////////////////////////////////////////////
244
+		// Statistics
245
+		'statistics'                  =>
246
+			array(
247
+				'class'   => StatsMain::class,
248
+				'actions' => array(),
249
+			),
250
+		'statistics/fastCloses'       =>
251
+			array(
252
+				'class'   => StatsFastCloses::class,
253
+				'actions' => array(),
254
+			),
255
+		'statistics/inactiveUsers'    =>
256
+			array(
257
+				'class'   => StatsInactiveUsers::class,
258
+				'actions' => array(),
259
+			),
260
+		'statistics/monthlyStats'     =>
261
+			array(
262
+				'class'   => StatsMonthlyStats::class,
263
+				'actions' => array(),
264
+			),
265
+		'statistics/reservedRequests' =>
266
+			array(
267
+				'class'   => StatsReservedRequests::class,
268
+				'actions' => array(),
269
+			),
270
+		'statistics/templateStats'    =>
271
+			array(
272
+				'class'   => StatsTemplateStats::class,
273
+				'actions' => array(),
274
+			),
275
+		'statistics/topCreators'      =>
276
+			array(
277
+				'class'   => StatsTopCreators::class,
278
+				'actions' => array(),
279
+			),
280
+		'statistics/users'            =>
281
+			array(
282
+				'class'   => StatsUsers::class,
283
+				'actions' => array('detail'),
284
+			),
285
+
286
+		//////////////////////////////////////////////////////////////////////////////////////////////////
287
+		// Zoom page
288
+		'viewRequest'                 =>
289
+			array(
290
+				'class'   => PageViewRequest::class,
291
+				'actions' => array(),
292
+			),
293
+		'viewRequest/reserve'         =>
294
+			array(
295
+				'class'   => PageReservation::class,
296
+				'actions' => array(),
297
+			),
298
+		'viewRequest/breakReserve'    =>
299
+			array(
300
+				'class'   => PageBreakReservation::class,
301
+				'actions' => array(),
302
+			),
303
+		'viewRequest/defer'           =>
304
+			array(
305
+				'class'   => PageDeferRequest::class,
306
+				'actions' => array(),
307
+			),
308
+		'viewRequest/comment'         =>
309
+			array(
310
+				'class'   => PageComment::class,
311
+				'actions' => array(),
312
+			),
313
+		'viewRequest/sendToUser'      =>
314
+			array(
315
+				'class'   => PageSendToUser::class,
316
+				'actions' => array(),
317
+			),
318
+		'viewRequest/close'           =>
319
+			array(
320
+				'class'   => PageCloseRequest::class,
321
+				'actions' => array(),
322
+			),
323
+		'viewRequest/create'          =>
324
+			array(
325
+				'class'   => PageCreateRequest::class,
326
+				'actions' => array(),
327
+			),
328
+		'viewRequest/drop'            =>
329
+			array(
330
+				'class'   => PageDropRequest::class,
331
+				'actions' => array(),
332
+			),
333
+		'viewRequest/custom'          =>
334
+			array(
335
+				'class'   => PageCustomClose::class,
336
+				'actions' => array(),
337
+			),
338
+		'editComment'                 =>
339
+			array(
340
+				'class'   => PageEditComment::class,
341
+				'actions' => array(),
342
+			),
343
+
344
+		//////////////////////////////////////////////////////////////////////////////////////////////////
345
+		// Misc stuff
346
+		'team'                        =>
347
+			array(
348
+				'class'   => PageTeam::class,
349
+				'actions' => array(),
350
+			),
351
+		'requestList'                 =>
352
+			array(
353
+				'class'   => PageExpandedRequestList::class,
354
+				'actions' => array(),
355
+			),
356
+		'xffdemo'                     =>
357
+			array(
358
+				'class'   => PageXffDemo::class,
359
+				'actions' => array(),
360
+			),
361
+	);
362
+
363
+	/**
364
+	 * @return IRoutedTask
365
+	 * @throws Exception
366
+	 */
367
+	final public function route()
368
+	{
369
+		$pathInfo = WebRequest::pathInfo();
370
+
371
+		list($pageClass, $action) = $this->getRouteFromPath($pathInfo);
372
+
373
+		/** @var IRoutedTask $page */
374
+		$page = new $pageClass();
375
+
376
+		// Dynamic creation, so we've got to be careful here. We can't use built-in language type protection, so
377
+		// let's use our own.
378
+		if (!($page instanceof IRoutedTask)) {
379
+			throw new Exception('Expected a page, but this is not a page.');
380
+		}
381
+
382
+		// OK, I'm happy at this point that we know we're running a page, and we know it's probably what we want if it
383
+		// inherits PageBase and has been created from the routing map.
384
+		$page->setRoute($action);
385
+
386
+		return $page;
387
+	}
388
+
389
+	/**
390
+	 * @param $pathInfo
391
+	 *
392
+	 * @return array
393
+	 */
394
+	protected function getRouteFromPath($pathInfo)
395
+	{
396
+		if (count($pathInfo) === 0) {
397
+			// No pathInfo, so no page to load. Load the main page.
398
+			return $this->getDefaultRoute();
399
+		}
400
+		elseif (count($pathInfo) === 1) {
401
+			// Exactly one path info segment, it's got to be a page.
402
+			$classSegment = $pathInfo[0];
403
+
404
+			return $this->routeSinglePathSegment($classSegment);
405
+		}
406
+
407
+		// OK, we have two or more segments now.
408
+		if (count($pathInfo) > 2) {
409
+			// Let's handle more than two, and collapse it down into two.
410
+			$requestedAction = array_pop($pathInfo);
411
+			$classSegment = implode('/', $pathInfo);
412
+		}
413
+		else {
414
+			// Two path info segments.
415
+			$classSegment = $pathInfo[0];
416
+			$requestedAction = $pathInfo[1];
417
+		}
418
+
419
+		$routeMap = $this->routePathSegments($classSegment, $requestedAction);
420
+
421
+		if ($routeMap[0] === Page404::class) {
422
+			$routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
423
+		}
424
+
425
+		return $routeMap;
426
+	}
427
+
428
+	/**
429
+	 * @param $classSegment
430
+	 *
431
+	 * @return array
432
+	 */
433
+	final protected function routeSinglePathSegment($classSegment)
434
+	{
435
+		$routeMap = $this->getRouteMap();
436
+		if (array_key_exists($classSegment, $routeMap)) {
437
+			// Route exists, but we don't have an action in path info, so default to main.
438
+			$pageClass = $routeMap[$classSegment]['class'];
439
+			$action = 'main';
440
+
441
+			return array($pageClass, $action);
442
+		}
443
+		else {
444
+			// Doesn't exist in map. Fall back to 404
445
+			$pageClass = Page404::class;
446
+			$action = "main";
447
+
448
+			return array($pageClass, $action);
449
+		}
450
+	}
451
+
452
+	/**
453
+	 * @param $classSegment
454
+	 * @param $requestedAction
455
+	 *
456
+	 * @return array
457
+	 */
458
+	final protected function routePathSegments($classSegment, $requestedAction)
459
+	{
460
+		$routeMap = $this->getRouteMap();
461
+		if (array_key_exists($classSegment, $routeMap)) {
462
+			// Route exists, but we don't have an action in path info, so default to main.
463
+
464
+			if (isset($routeMap[$classSegment]['actions'])
465
+				&& array_search($requestedAction, $routeMap[$classSegment]['actions']) !== false
466
+			) {
467
+				// Action exists in allowed action list. Allow both the page and the action
468
+				$pageClass = $routeMap[$classSegment]['class'];
469
+				$action = $requestedAction;
470
+
471
+				return array($pageClass, $action);
472
+			}
473
+			else {
474
+				// Valid page, invalid action. 404 our way out.
475
+				$pageClass = Page404::class;
476
+				$action = 'main';
477
+
478
+				return array($pageClass, $action);
479
+			}
480
+		}
481
+		else {
482
+			// Class doesn't exist in map. Fall back to 404
483
+			$pageClass = Page404::class;
484
+			$action = 'main';
485
+
486
+			return array($pageClass, $action);
487
+		}
488
+	}
489
+
490
+	/**
491
+	 * @return array
492
+	 */
493
+	protected function getRouteMap()
494
+	{
495
+		return $this->routeMap;
496
+	}
497
+
498
+	/**
499
+	 * @return callable
500
+	 */
501
+	protected function getDefaultRoute()
502
+	{
503
+		return array(PageMain::class, "main");
504
+	}
505 505
 }
Please login to merge, or discard this patch.
includes/DataObjects/User.php 1 patch
Indentation   +558 added lines, -558 removed lines patch added patch discarded remove patch
@@ -21,160 +21,160 @@  discard block
 block discarded – undo
21 21
  */
22 22
 class User extends DataObject
23 23
 {
24
-    const STATUS_ACTIVE = 'Active';
25
-    const STATUS_SUSPENDED = 'Suspended';
26
-    const STATUS_DECLINED = 'Declined';
27
-    const STATUS_NEW = 'New';
28
-    const CREATION_MANUAL = 0;
29
-    const CREATION_OAUTH = 1;
30
-    const CREATION_BOT = 2;
31
-    private $username;
32
-    private $email;
33
-    private $status = self::STATUS_NEW;
34
-    private $onwikiname;
35
-    private $welcome_sig = "";
36
-    private $lastactive = "0000-00-00 00:00:00";
37
-    private $forcelogout = 0;
38
-    private $forceidentified = null;
39
-    private $welcome_template = 0;
40
-    private $abortpref = 0;
41
-    private $confirmationdiff = 0;
42
-    private $emailsig = "";
43
-    private $creationmode = 0;
44
-    private $skin = "main";
45
-    /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
46
-    private static $currentUser;
47
-    #region Object load methods
48
-
49
-    /**
50
-     * Gets the currently logged in user
51
-     *
52
-     * @param PdoDatabase $database
53
-     *
54
-     * @return User|CommunityUser
55
-     */
56
-    public static function getCurrent(PdoDatabase $database)
57
-    {
58
-        if (self::$currentUser === null) {
59
-            $sessionId = WebRequest::getSessionUserId();
60
-
61
-            if ($sessionId !== null) {
62
-                /** @var User $user */
63
-                $user = self::getById($sessionId, $database);
64
-
65
-                if ($user === false) {
66
-                    self::$currentUser = new CommunityUser();
67
-                }
68
-                else {
69
-                    self::$currentUser = $user;
70
-                }
71
-            }
72
-            else {
73
-                $anonymousCoward = new CommunityUser();
74
-
75
-                self::$currentUser = $anonymousCoward;
76
-            }
77
-        }
78
-
79
-        return self::$currentUser;
80
-    }
81
-
82
-    /**
83
-     * Gets a user by their user ID
84
-     *
85
-     * Pass -1 to get the community user.
86
-     *
87
-     * @param int|null    $id
88
-     * @param PdoDatabase $database
89
-     *
90
-     * @return User|false
91
-     */
92
-    public static function getById($id, PdoDatabase $database)
93
-    {
94
-        if ($id === null || $id == -1) {
95
-            return new CommunityUser();
96
-        }
97
-
98
-        /** @var User|false $user */
99
-        $user = parent::getById($id, $database);
100
-
101
-        return $user;
102
-    }
103
-
104
-    /**
105
-     * @return CommunityUser
106
-     */
107
-    public static function getCommunity()
108
-    {
109
-        return new CommunityUser();
110
-    }
111
-
112
-    /**
113
-     * Gets a user by their username
114
-     *
115
-     * @param  string      $username
116
-     * @param  PdoDatabase $database
117
-     *
118
-     * @return CommunityUser|User|false
119
-     */
120
-    public static function getByUsername($username, PdoDatabase $database)
121
-    {
122
-        global $communityUsername;
123
-        if ($username == $communityUsername) {
124
-            return new CommunityUser();
125
-        }
126
-
127
-        $statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
128
-        $statement->bindValue(":id", $username);
129
-
130
-        $statement->execute();
131
-
132
-        $resultObject = $statement->fetchObject(get_called_class());
133
-
134
-        if ($resultObject != false) {
135
-            $resultObject->setDatabase($database);
136
-        }
137
-
138
-        return $resultObject;
139
-    }
140
-
141
-    /**
142
-     * Gets a user by their on-wiki username.
143
-     *
144
-     * @param string      $username
145
-     * @param PdoDatabase $database
146
-     *
147
-     * @return User|false
148
-     */
149
-    public static function getByOnWikiUsername($username, PdoDatabase $database)
150
-    {
151
-        $statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
152
-        $statement->bindValue(":id", $username);
153
-        $statement->execute();
154
-
155
-        $resultObject = $statement->fetchObject(get_called_class());
156
-
157
-        if ($resultObject != false) {
158
-            $resultObject->setDatabase($database);
159
-
160
-            return $resultObject;
161
-        }
162
-
163
-        return false;
164
-    }
165
-
166
-    #endregion
167
-
168
-    /**
169
-     * Saves the current object
170
-     *
171
-     * @throws Exception
172
-     */
173
-    public function save()
174
-    {
175
-        if ($this->isNew()) {
176
-            // insert
177
-            $statement = $this->dbObject->prepare(<<<SQL
24
+	const STATUS_ACTIVE = 'Active';
25
+	const STATUS_SUSPENDED = 'Suspended';
26
+	const STATUS_DECLINED = 'Declined';
27
+	const STATUS_NEW = 'New';
28
+	const CREATION_MANUAL = 0;
29
+	const CREATION_OAUTH = 1;
30
+	const CREATION_BOT = 2;
31
+	private $username;
32
+	private $email;
33
+	private $status = self::STATUS_NEW;
34
+	private $onwikiname;
35
+	private $welcome_sig = "";
36
+	private $lastactive = "0000-00-00 00:00:00";
37
+	private $forcelogout = 0;
38
+	private $forceidentified = null;
39
+	private $welcome_template = 0;
40
+	private $abortpref = 0;
41
+	private $confirmationdiff = 0;
42
+	private $emailsig = "";
43
+	private $creationmode = 0;
44
+	private $skin = "main";
45
+	/** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
46
+	private static $currentUser;
47
+	#region Object load methods
48
+
49
+	/**
50
+	 * Gets the currently logged in user
51
+	 *
52
+	 * @param PdoDatabase $database
53
+	 *
54
+	 * @return User|CommunityUser
55
+	 */
56
+	public static function getCurrent(PdoDatabase $database)
57
+	{
58
+		if (self::$currentUser === null) {
59
+			$sessionId = WebRequest::getSessionUserId();
60
+
61
+			if ($sessionId !== null) {
62
+				/** @var User $user */
63
+				$user = self::getById($sessionId, $database);
64
+
65
+				if ($user === false) {
66
+					self::$currentUser = new CommunityUser();
67
+				}
68
+				else {
69
+					self::$currentUser = $user;
70
+				}
71
+			}
72
+			else {
73
+				$anonymousCoward = new CommunityUser();
74
+
75
+				self::$currentUser = $anonymousCoward;
76
+			}
77
+		}
78
+
79
+		return self::$currentUser;
80
+	}
81
+
82
+	/**
83
+	 * Gets a user by their user ID
84
+	 *
85
+	 * Pass -1 to get the community user.
86
+	 *
87
+	 * @param int|null    $id
88
+	 * @param PdoDatabase $database
89
+	 *
90
+	 * @return User|false
91
+	 */
92
+	public static function getById($id, PdoDatabase $database)
93
+	{
94
+		if ($id === null || $id == -1) {
95
+			return new CommunityUser();
96
+		}
97
+
98
+		/** @var User|false $user */
99
+		$user = parent::getById($id, $database);
100
+
101
+		return $user;
102
+	}
103
+
104
+	/**
105
+	 * @return CommunityUser
106
+	 */
107
+	public static function getCommunity()
108
+	{
109
+		return new CommunityUser();
110
+	}
111
+
112
+	/**
113
+	 * Gets a user by their username
114
+	 *
115
+	 * @param  string      $username
116
+	 * @param  PdoDatabase $database
117
+	 *
118
+	 * @return CommunityUser|User|false
119
+	 */
120
+	public static function getByUsername($username, PdoDatabase $database)
121
+	{
122
+		global $communityUsername;
123
+		if ($username == $communityUsername) {
124
+			return new CommunityUser();
125
+		}
126
+
127
+		$statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
128
+		$statement->bindValue(":id", $username);
129
+
130
+		$statement->execute();
131
+
132
+		$resultObject = $statement->fetchObject(get_called_class());
133
+
134
+		if ($resultObject != false) {
135
+			$resultObject->setDatabase($database);
136
+		}
137
+
138
+		return $resultObject;
139
+	}
140
+
141
+	/**
142
+	 * Gets a user by their on-wiki username.
143
+	 *
144
+	 * @param string      $username
145
+	 * @param PdoDatabase $database
146
+	 *
147
+	 * @return User|false
148
+	 */
149
+	public static function getByOnWikiUsername($username, PdoDatabase $database)
150
+	{
151
+		$statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
152
+		$statement->bindValue(":id", $username);
153
+		$statement->execute();
154
+
155
+		$resultObject = $statement->fetchObject(get_called_class());
156
+
157
+		if ($resultObject != false) {
158
+			$resultObject->setDatabase($database);
159
+
160
+			return $resultObject;
161
+		}
162
+
163
+		return false;
164
+	}
165
+
166
+	#endregion
167
+
168
+	/**
169
+	 * Saves the current object
170
+	 *
171
+	 * @throws Exception
172
+	 */
173
+	public function save()
174
+	{
175
+		if ($this->isNew()) {
176
+			// insert
177
+			$statement = $this->dbObject->prepare(<<<SQL
178 178
 				INSERT INTO `user` ( 
179 179
 					username, email, status, onwikiname, welcome_sig, 
180 180
 					lastactive, forcelogout, forceidentified,
@@ -185,32 +185,32 @@  discard block
 block discarded – undo
185 185
 					:welcome_template, :abortpref, :confirmationdiff, :emailsig, :creationmode, :skin
186 186
 				);
187 187
 SQL
188
-            );
189
-            $statement->bindValue(":username", $this->username);
190
-            $statement->bindValue(":email", $this->email);
191
-            $statement->bindValue(":status", $this->status);
192
-            $statement->bindValue(":onwikiname", $this->onwikiname);
193
-            $statement->bindValue(":welcome_sig", $this->welcome_sig);
194
-            $statement->bindValue(":lastactive", $this->lastactive);
195
-            $statement->bindValue(":forcelogout", $this->forcelogout);
196
-            $statement->bindValue(":forceidentified", $this->forceidentified);
197
-            $statement->bindValue(":welcome_template", $this->welcome_template);
198
-            $statement->bindValue(":abortpref", $this->abortpref);
199
-            $statement->bindValue(":confirmationdiff", $this->confirmationdiff);
200
-            $statement->bindValue(":emailsig", $this->emailsig);
201
-            $statement->bindValue(":creationmode", $this->creationmode);
202
-            $statement->bindValue(":skin", $this->skin);
203
-
204
-            if ($statement->execute()) {
205
-                $this->id = (int)$this->dbObject->lastInsertId();
206
-            }
207
-            else {
208
-                throw new Exception($statement->errorInfo());
209
-            }
210
-        }
211
-        else {
212
-            // update
213
-            $statement = $this->dbObject->prepare(<<<SQL
188
+			);
189
+			$statement->bindValue(":username", $this->username);
190
+			$statement->bindValue(":email", $this->email);
191
+			$statement->bindValue(":status", $this->status);
192
+			$statement->bindValue(":onwikiname", $this->onwikiname);
193
+			$statement->bindValue(":welcome_sig", $this->welcome_sig);
194
+			$statement->bindValue(":lastactive", $this->lastactive);
195
+			$statement->bindValue(":forcelogout", $this->forcelogout);
196
+			$statement->bindValue(":forceidentified", $this->forceidentified);
197
+			$statement->bindValue(":welcome_template", $this->welcome_template);
198
+			$statement->bindValue(":abortpref", $this->abortpref);
199
+			$statement->bindValue(":confirmationdiff", $this->confirmationdiff);
200
+			$statement->bindValue(":emailsig", $this->emailsig);
201
+			$statement->bindValue(":creationmode", $this->creationmode);
202
+			$statement->bindValue(":skin", $this->skin);
203
+
204
+			if ($statement->execute()) {
205
+				$this->id = (int)$this->dbObject->lastInsertId();
206
+			}
207
+			else {
208
+				throw new Exception($statement->errorInfo());
209
+			}
210
+		}
211
+		else {
212
+			// update
213
+			$statement = $this->dbObject->prepare(<<<SQL
214 214
 				UPDATE `user` SET 
215 215
 					username = :username, email = :email, 
216 216
 					status = :status,
@@ -223,378 +223,378 @@  discard block
 block discarded – undo
223 223
                     updateversion = updateversion + 1
224 224
 				WHERE id = :id AND updateversion = :updateversion;
225 225
 SQL
226
-            );
227
-            $statement->bindValue(":forceidentified", $this->forceidentified);
228
-
229
-            $statement->bindValue(':id', $this->id);
230
-            $statement->bindValue(':updateversion', $this->updateversion);
231
-
232
-            $statement->bindValue(':username', $this->username);
233
-            $statement->bindValue(':email', $this->email);
234
-            $statement->bindValue(':status', $this->status);
235
-            $statement->bindValue(':onwikiname', $this->onwikiname);
236
-            $statement->bindValue(':welcome_sig', $this->welcome_sig);
237
-            $statement->bindValue(':lastactive', $this->lastactive);
238
-            $statement->bindValue(':forcelogout', $this->forcelogout);
239
-            $statement->bindValue(':forceidentified', $this->forceidentified);
240
-            $statement->bindValue(':welcome_template', $this->welcome_template);
241
-            $statement->bindValue(':abortpref', $this->abortpref);
242
-            $statement->bindValue(':confirmationdiff', $this->confirmationdiff);
243
-            $statement->bindValue(':emailsig', $this->emailsig);
244
-            $statement->bindValue(':creationmode', $this->creationmode);
245
-            $statement->bindValue(':skin', $this->skin);
246
-
247
-            if (!$statement->execute()) {
248
-                throw new Exception($statement->errorInfo());
249
-            }
250
-
251
-            if ($statement->rowCount() !== 1) {
252
-                throw new OptimisticLockFailedException();
253
-            }
254
-
255
-            $this->updateversion++;
256
-        }
257
-    }
258
-
259
-    #region properties
260
-
261
-    /**
262
-     * Gets the tool username
263
-     * @return string
264
-     */
265
-    public function getUsername()
266
-    {
267
-        return $this->username;
268
-    }
269
-
270
-    /**
271
-     * Sets the tool username
272
-     *
273
-     * @param string $username
274
-     */
275
-    public function setUsername($username)
276
-    {
277
-        $this->username = $username;
278
-
279
-        // If this isn't a brand new user, then it's a rename, force the logout
280
-        if (!$this->isNew()) {
281
-            $this->forcelogout = 1;
282
-        }
283
-    }
284
-
285
-    /**
286
-     * Gets the user's email address
287
-     * @return string
288
-     */
289
-    public function getEmail()
290
-    {
291
-        return $this->email;
292
-    }
293
-
294
-    /**
295
-     * Sets the user's email address
296
-     *
297
-     * @param string $email
298
-     */
299
-    public function setEmail($email)
300
-    {
301
-        $this->email = $email;
302
-    }
303
-
304
-    /**
305
-     * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user.
306
-     * @return string
307
-     */
308
-    public function getStatus()
309
-    {
310
-        return $this->status;
311
-    }
312
-
313
-    /**
314
-     * @param string $status
315
-     */
316
-    public function setStatus($status)
317
-    {
318
-        $this->status = $status;
319
-    }
320
-
321
-    /**
322
-     * Gets the user's on-wiki name
323
-     * @return string
324
-     */
325
-    public function getOnWikiName()
326
-    {
327
-        return $this->onwikiname;
328
-    }
329
-
330
-    /**
331
-     * Sets the user's on-wiki name
332
-     *
333
-     * This can have interesting side-effects with OAuth.
334
-     *
335
-     * @param string $onWikiName
336
-     */
337
-    public function setOnWikiName($onWikiName)
338
-    {
339
-        $this->onwikiname = $onWikiName;
340
-    }
341
-
342
-    /**
343
-     * Gets the welcome signature
344
-     * @return string
345
-     */
346
-    public function getWelcomeSig()
347
-    {
348
-        return $this->welcome_sig;
349
-    }
350
-
351
-    /**
352
-     * Sets the welcome signature
353
-     *
354
-     * @param string $welcomeSig
355
-     */
356
-    public function setWelcomeSig($welcomeSig)
357
-    {
358
-        $this->welcome_sig = $welcomeSig;
359
-    }
360
-
361
-    /**
362
-     * Gets the last activity date for the user
363
-     *
364
-     * @return string
365
-     * @todo This should probably return an instance of DateTime
366
-     */
367
-    public function getLastActive()
368
-    {
369
-        return $this->lastactive;
370
-    }
371
-
372
-    /**
373
-     * Gets the user's forced logout status
374
-     *
375
-     * @return bool
376
-     */
377
-    public function getForceLogout()
378
-    {
379
-        return $this->forcelogout == 1;
380
-    }
381
-
382
-    /**
383
-     * Sets the user's forced logout status
384
-     *
385
-     * @param bool $forceLogout
386
-     */
387
-    public function setForceLogout($forceLogout)
388
-    {
389
-        $this->forcelogout = $forceLogout ? 1 : 0;
390
-    }
391
-
392
-    /**
393
-     * Returns the ID of the welcome template used.
394
-     * @return int
395
-     */
396
-    public function getWelcomeTemplate()
397
-    {
398
-        return $this->welcome_template;
399
-    }
400
-
401
-    /**
402
-     * Sets the ID of the welcome template used.
403
-     *
404
-     * @param int $welcomeTemplate
405
-     */
406
-    public function setWelcomeTemplate($welcomeTemplate)
407
-    {
408
-        $this->welcome_template = $welcomeTemplate;
409
-    }
410
-
411
-    /**
412
-     * Gets the user's abort preference
413
-     * @todo this is badly named too! Also a bool that's actually an int.
414
-     * @return int
415
-     */
416
-    public function getAbortPref()
417
-    {
418
-        return $this->abortpref;
419
-    }
420
-
421
-    /**
422
-     * Sets the user's abort preference
423
-     * @todo rename, retype, and re-comment.
424
-     *
425
-     * @param int $abortPreference
426
-     */
427
-    public function setAbortPref($abortPreference)
428
-    {
429
-        $this->abortpref = $abortPreference;
430
-    }
431
-
432
-    /**
433
-     * Gets the user's confirmation diff. Unused if OAuth is in use.
434
-     * @return int the diff ID
435
-     */
436
-    public function getConfirmationDiff()
437
-    {
438
-        return $this->confirmationdiff;
439
-    }
440
-
441
-    /**
442
-     * Sets the user's confirmation diff.
443
-     *
444
-     * @param int $confirmationDiff
445
-     */
446
-    public function setConfirmationDiff($confirmationDiff)
447
-    {
448
-        $this->confirmationdiff = $confirmationDiff;
449
-    }
450
-
451
-    /**
452
-     * Gets the users' email signature used on outbound mail.
453
-     * @todo rename me!
454
-     * @return string
455
-     */
456
-    public function getEmailSig()
457
-    {
458
-        return $this->emailsig;
459
-    }
460
-
461
-    /**
462
-     * Sets the user's email signature for outbound mail.
463
-     *
464
-     * @param string $emailSignature
465
-     */
466
-    public function setEmailSig($emailSignature)
467
-    {
468
-        $this->emailsig = $emailSignature;
469
-    }
470
-
471
-    /**
472
-     * @return int
473
-     */
474
-    public function getCreationMode()
475
-    {
476
-        return $this->creationmode;
477
-    }
478
-
479
-    /**
480
-     * @param $creationMode int
481
-     */
482
-    public function setCreationMode($creationMode)
483
-    {
484
-        $this->creationmode = $creationMode;
485
-    }
486
-
487
-    /**
488
-     * @return boolean
489
-     */
490
-    public function getUseAlternateSkin()
491
-    {
492
-        return $this->skin === 'alt';
493
-    }
494
-
495
-    /**
496
-     * @return string
497
-     */
498
-    public function getSkin()
499
-    {
500
-        return $this->skin;
501
-    }
502
-
503
-    /**
504
-     * @param $skin string
505
-     */
506
-    public function setSkin($skin)
507
-    {
508
-        $this->skin = $skin;
509
-    }
510
-
511
-    #endregion
512
-
513
-    #region user access checks
514
-
515
-    public function isActive()
516
-    {
517
-        return $this->status == self::STATUS_ACTIVE;
518
-    }
519
-
520
-    /**
521
-     * Tests if the user is identified
522
-     *
523
-     * @param IdentificationVerifier $iv
524
-     *
525
-     * @return bool
526
-     * @todo     Figure out what on earth is going on with PDO's typecasting here.  Apparently, it returns string("0") for
527
-     *       the force-unidentified case, and int(1) for the identified case?!  This is quite ugly, but probably needed
528
-     *       to play it safe for now.
529
-     * @category Security-Critical
530
-     */
531
-    public function isIdentified(IdentificationVerifier $iv)
532
-    {
533
-        if ($this->forceidentified === 0 || $this->forceidentified === "0") {
534
-            // User forced to unidentified in the database.
535
-            return false;
536
-        }
537
-        elseif ($this->forceidentified === 1 || $this->forceidentified === "1") {
538
-            // User forced to identified in the database.
539
-            return true;
540
-        }
541
-        else {
542
-            // User not forced to any particular identified status; consult IdentificationVerifier
543
-            return $iv->isUserIdentified($this->getOnWikiName());
544
-        }
545
-    }
546
-
547
-    /**
548
-     * Tests if the user is suspended
549
-     * @return bool
550
-     * @category Security-Critical
551
-     */
552
-    public function isSuspended()
553
-    {
554
-        return $this->status == self::STATUS_SUSPENDED;
555
-    }
556
-
557
-    /**
558
-     * Tests if the user is new
559
-     * @return bool
560
-     * @category Security-Critical
561
-     */
562
-    public function isNewUser()
563
-    {
564
-        return $this->status == self::STATUS_NEW;
565
-    }
566
-
567
-    /**
568
-     * Tests if the user has been declined access to the tool
569
-     * @return bool
570
-     * @category Security-Critical
571
-     */
572
-    public function isDeclined()
573
-    {
574
-        return $this->status == self::STATUS_DECLINED;
575
-    }
576
-
577
-    /**
578
-     * Tests if the user is the community user
579
-     *
580
-     * @todo     decide if this means logged out. I think it usually does.
581
-     * @return bool
582
-     * @category Security-Critical
583
-     */
584
-    public function isCommunityUser()
585
-    {
586
-        return false;
587
-    }
588
-
589
-    #endregion 
590
-
591
-    /**
592
-     * Gets the approval date of the user
593
-     * @return DateTime|false
594
-     */
595
-    public function getApprovalDate()
596
-    {
597
-        $query = $this->dbObject->prepare(<<<SQL
226
+			);
227
+			$statement->bindValue(":forceidentified", $this->forceidentified);
228
+
229
+			$statement->bindValue(':id', $this->id);
230
+			$statement->bindValue(':updateversion', $this->updateversion);
231
+
232
+			$statement->bindValue(':username', $this->username);
233
+			$statement->bindValue(':email', $this->email);
234
+			$statement->bindValue(':status', $this->status);
235
+			$statement->bindValue(':onwikiname', $this->onwikiname);
236
+			$statement->bindValue(':welcome_sig', $this->welcome_sig);
237
+			$statement->bindValue(':lastactive', $this->lastactive);
238
+			$statement->bindValue(':forcelogout', $this->forcelogout);
239
+			$statement->bindValue(':forceidentified', $this->forceidentified);
240
+			$statement->bindValue(':welcome_template', $this->welcome_template);
241
+			$statement->bindValue(':abortpref', $this->abortpref);
242
+			$statement->bindValue(':confirmationdiff', $this->confirmationdiff);
243
+			$statement->bindValue(':emailsig', $this->emailsig);
244
+			$statement->bindValue(':creationmode', $this->creationmode);
245
+			$statement->bindValue(':skin', $this->skin);
246
+
247
+			if (!$statement->execute()) {
248
+				throw new Exception($statement->errorInfo());
249
+			}
250
+
251
+			if ($statement->rowCount() !== 1) {
252
+				throw new OptimisticLockFailedException();
253
+			}
254
+
255
+			$this->updateversion++;
256
+		}
257
+	}
258
+
259
+	#region properties
260
+
261
+	/**
262
+	 * Gets the tool username
263
+	 * @return string
264
+	 */
265
+	public function getUsername()
266
+	{
267
+		return $this->username;
268
+	}
269
+
270
+	/**
271
+	 * Sets the tool username
272
+	 *
273
+	 * @param string $username
274
+	 */
275
+	public function setUsername($username)
276
+	{
277
+		$this->username = $username;
278
+
279
+		// If this isn't a brand new user, then it's a rename, force the logout
280
+		if (!$this->isNew()) {
281
+			$this->forcelogout = 1;
282
+		}
283
+	}
284
+
285
+	/**
286
+	 * Gets the user's email address
287
+	 * @return string
288
+	 */
289
+	public function getEmail()
290
+	{
291
+		return $this->email;
292
+	}
293
+
294
+	/**
295
+	 * Sets the user's email address
296
+	 *
297
+	 * @param string $email
298
+	 */
299
+	public function setEmail($email)
300
+	{
301
+		$this->email = $email;
302
+	}
303
+
304
+	/**
305
+	 * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user.
306
+	 * @return string
307
+	 */
308
+	public function getStatus()
309
+	{
310
+		return $this->status;
311
+	}
312
+
313
+	/**
314
+	 * @param string $status
315
+	 */
316
+	public function setStatus($status)
317
+	{
318
+		$this->status = $status;
319
+	}
320
+
321
+	/**
322
+	 * Gets the user's on-wiki name
323
+	 * @return string
324
+	 */
325
+	public function getOnWikiName()
326
+	{
327
+		return $this->onwikiname;
328
+	}
329
+
330
+	/**
331
+	 * Sets the user's on-wiki name
332
+	 *
333
+	 * This can have interesting side-effects with OAuth.
334
+	 *
335
+	 * @param string $onWikiName
336
+	 */
337
+	public function setOnWikiName($onWikiName)
338
+	{
339
+		$this->onwikiname = $onWikiName;
340
+	}
341
+
342
+	/**
343
+	 * Gets the welcome signature
344
+	 * @return string
345
+	 */
346
+	public function getWelcomeSig()
347
+	{
348
+		return $this->welcome_sig;
349
+	}
350
+
351
+	/**
352
+	 * Sets the welcome signature
353
+	 *
354
+	 * @param string $welcomeSig
355
+	 */
356
+	public function setWelcomeSig($welcomeSig)
357
+	{
358
+		$this->welcome_sig = $welcomeSig;
359
+	}
360
+
361
+	/**
362
+	 * Gets the last activity date for the user
363
+	 *
364
+	 * @return string
365
+	 * @todo This should probably return an instance of DateTime
366
+	 */
367
+	public function getLastActive()
368
+	{
369
+		return $this->lastactive;
370
+	}
371
+
372
+	/**
373
+	 * Gets the user's forced logout status
374
+	 *
375
+	 * @return bool
376
+	 */
377
+	public function getForceLogout()
378
+	{
379
+		return $this->forcelogout == 1;
380
+	}
381
+
382
+	/**
383
+	 * Sets the user's forced logout status
384
+	 *
385
+	 * @param bool $forceLogout
386
+	 */
387
+	public function setForceLogout($forceLogout)
388
+	{
389
+		$this->forcelogout = $forceLogout ? 1 : 0;
390
+	}
391
+
392
+	/**
393
+	 * Returns the ID of the welcome template used.
394
+	 * @return int
395
+	 */
396
+	public function getWelcomeTemplate()
397
+	{
398
+		return $this->welcome_template;
399
+	}
400
+
401
+	/**
402
+	 * Sets the ID of the welcome template used.
403
+	 *
404
+	 * @param int $welcomeTemplate
405
+	 */
406
+	public function setWelcomeTemplate($welcomeTemplate)
407
+	{
408
+		$this->welcome_template = $welcomeTemplate;
409
+	}
410
+
411
+	/**
412
+	 * Gets the user's abort preference
413
+	 * @todo this is badly named too! Also a bool that's actually an int.
414
+	 * @return int
415
+	 */
416
+	public function getAbortPref()
417
+	{
418
+		return $this->abortpref;
419
+	}
420
+
421
+	/**
422
+	 * Sets the user's abort preference
423
+	 * @todo rename, retype, and re-comment.
424
+	 *
425
+	 * @param int $abortPreference
426
+	 */
427
+	public function setAbortPref($abortPreference)
428
+	{
429
+		$this->abortpref = $abortPreference;
430
+	}
431
+
432
+	/**
433
+	 * Gets the user's confirmation diff. Unused if OAuth is in use.
434
+	 * @return int the diff ID
435
+	 */
436
+	public function getConfirmationDiff()
437
+	{
438
+		return $this->confirmationdiff;
439
+	}
440
+
441
+	/**
442
+	 * Sets the user's confirmation diff.
443
+	 *
444
+	 * @param int $confirmationDiff
445
+	 */
446
+	public function setConfirmationDiff($confirmationDiff)
447
+	{
448
+		$this->confirmationdiff = $confirmationDiff;
449
+	}
450
+
451
+	/**
452
+	 * Gets the users' email signature used on outbound mail.
453
+	 * @todo rename me!
454
+	 * @return string
455
+	 */
456
+	public function getEmailSig()
457
+	{
458
+		return $this->emailsig;
459
+	}
460
+
461
+	/**
462
+	 * Sets the user's email signature for outbound mail.
463
+	 *
464
+	 * @param string $emailSignature
465
+	 */
466
+	public function setEmailSig($emailSignature)
467
+	{
468
+		$this->emailsig = $emailSignature;
469
+	}
470
+
471
+	/**
472
+	 * @return int
473
+	 */
474
+	public function getCreationMode()
475
+	{
476
+		return $this->creationmode;
477
+	}
478
+
479
+	/**
480
+	 * @param $creationMode int
481
+	 */
482
+	public function setCreationMode($creationMode)
483
+	{
484
+		$this->creationmode = $creationMode;
485
+	}
486
+
487
+	/**
488
+	 * @return boolean
489
+	 */
490
+	public function getUseAlternateSkin()
491
+	{
492
+		return $this->skin === 'alt';
493
+	}
494
+
495
+	/**
496
+	 * @return string
497
+	 */
498
+	public function getSkin()
499
+	{
500
+		return $this->skin;
501
+	}
502
+
503
+	/**
504
+	 * @param $skin string
505
+	 */
506
+	public function setSkin($skin)
507
+	{
508
+		$this->skin = $skin;
509
+	}
510
+
511
+	#endregion
512
+
513
+	#region user access checks
514
+
515
+	public function isActive()
516
+	{
517
+		return $this->status == self::STATUS_ACTIVE;
518
+	}
519
+
520
+	/**
521
+	 * Tests if the user is identified
522
+	 *
523
+	 * @param IdentificationVerifier $iv
524
+	 *
525
+	 * @return bool
526
+	 * @todo     Figure out what on earth is going on with PDO's typecasting here.  Apparently, it returns string("0") for
527
+	 *       the force-unidentified case, and int(1) for the identified case?!  This is quite ugly, but probably needed
528
+	 *       to play it safe for now.
529
+	 * @category Security-Critical
530
+	 */
531
+	public function isIdentified(IdentificationVerifier $iv)
532
+	{
533
+		if ($this->forceidentified === 0 || $this->forceidentified === "0") {
534
+			// User forced to unidentified in the database.
535
+			return false;
536
+		}
537
+		elseif ($this->forceidentified === 1 || $this->forceidentified === "1") {
538
+			// User forced to identified in the database.
539
+			return true;
540
+		}
541
+		else {
542
+			// User not forced to any particular identified status; consult IdentificationVerifier
543
+			return $iv->isUserIdentified($this->getOnWikiName());
544
+		}
545
+	}
546
+
547
+	/**
548
+	 * Tests if the user is suspended
549
+	 * @return bool
550
+	 * @category Security-Critical
551
+	 */
552
+	public function isSuspended()
553
+	{
554
+		return $this->status == self::STATUS_SUSPENDED;
555
+	}
556
+
557
+	/**
558
+	 * Tests if the user is new
559
+	 * @return bool
560
+	 * @category Security-Critical
561
+	 */
562
+	public function isNewUser()
563
+	{
564
+		return $this->status == self::STATUS_NEW;
565
+	}
566
+
567
+	/**
568
+	 * Tests if the user has been declined access to the tool
569
+	 * @return bool
570
+	 * @category Security-Critical
571
+	 */
572
+	public function isDeclined()
573
+	{
574
+		return $this->status == self::STATUS_DECLINED;
575
+	}
576
+
577
+	/**
578
+	 * Tests if the user is the community user
579
+	 *
580
+	 * @todo     decide if this means logged out. I think it usually does.
581
+	 * @return bool
582
+	 * @category Security-Critical
583
+	 */
584
+	public function isCommunityUser()
585
+	{
586
+		return false;
587
+	}
588
+
589
+	#endregion 
590
+
591
+	/**
592
+	 * Gets the approval date of the user
593
+	 * @return DateTime|false
594
+	 */
595
+	public function getApprovalDate()
596
+	{
597
+		$query = $this->dbObject->prepare(<<<SQL
598 598
 			SELECT timestamp 
599 599
 			FROM log 
600 600
 			WHERE objectid = :userid
@@ -603,12 +603,12 @@  discard block
 block discarded – undo
603 603
 			ORDER BY id DESC 
604 604
 			LIMIT 1;
605 605
 SQL
606
-        );
607
-        $query->execute(array(":userid" => $this->id));
606
+		);
607
+		$query->execute(array(":userid" => $this->id));
608 608
 
609
-        $data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
610
-        $query->closeCursor();
609
+		$data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
610
+		$query->closeCursor();
611 611
 
612
-        return $data;
613
-    }
612
+		return $data;
613
+	}
614 614
 }
Please login to merge, or discard this patch.
includes/Pages/PageUserManagement.php 2 patches
Indentation   +548 added lines, -548 removed lines patch added patch discarded remove patch
@@ -24,552 +24,552 @@
 block discarded – undo
24 24
  */
25 25
 class PageUserManagement extends InternalPageBase
26 26
 {
27
-    /** @var string */
28
-    private $adminMailingList = '[email protected]';
29
-
30
-    /**
31
-     * Main function for this page, when no specific actions are called.
32
-     */
33
-    protected function main()
34
-    {
35
-        $this->setHtmlTitle('User Management');
36
-
37
-        $database = $this->getDatabase();
38
-        $currentUser = User::getCurrent($database);
39
-
40
-        $userSearchRequest = WebRequest::getString('usersearch');
41
-        if ($userSearchRequest !== null) {
42
-            $searchedUser = User::getByUsername($userSearchRequest, $database);
43
-            if($searchedUser !== false) {
44
-                $this->redirect('statistics/users', 'detail', ['user' => $searchedUser->getId()]);
45
-                return;
46
-            }
47
-        }
48
-
49
-        // A bit hacky, but it's better than my last solution of creating an object for each user and passing that to
50
-        // the template. I still don't have a particularly good way of handling this.
51
-        OAuthUserHelper::prepareTokenCountStatement($database);
52
-
53
-        if (WebRequest::getBoolean("showAll")) {
54
-            $this->assign("showAll", true);
55
-
56
-            $suspendedUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_SUSPENDED)->fetch();
57
-            $this->assign("suspendedUsers", $suspendedUsers);
58
-
59
-            $declinedUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_DECLINED)->fetch();
60
-            $this->assign("declinedUsers", $declinedUsers);
61
-
62
-            UserSearchHelper::get($database)->getRoleMap($roleMap);
63
-        }
64
-        else {
65
-            $this->assign("showAll", false);
66
-            $this->assign("suspendedUsers", array());
67
-            $this->assign("declinedUsers", array());
68
-
69
-            UserSearchHelper::get($database)->statusIn(array('New', 'Active'))->getRoleMap($roleMap);
70
-        }
71
-
72
-        $newUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_NEW)->fetch();
73
-        $normalUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('user')->fetch();
74
-        $adminUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('admin')->fetch();
75
-        $checkUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('checkuser')->fetch();
76
-        $toolRoots = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('toolRoot')->fetch();
77
-        $this->assign('newUsers', $newUsers);
78
-        $this->assign('normalUsers', $normalUsers);
79
-        $this->assign('adminUsers', $adminUsers);
80
-        $this->assign('checkUsers', $checkUsers);
81
-        $this->assign('toolRoots', $toolRoots);
82
-
83
-        $this->assign('roles', $roleMap);
84
-
85
-        $this->addJs("/api.php?action=users&all=true&targetVariable=typeaheaddata");
86
-
87
-        $this->assign('canApprove', $this->barrierTest('approve', $currentUser));
88
-        $this->assign('canDecline', $this->barrierTest('decline', $currentUser));
89
-        $this->assign('canRename', $this->barrierTest('rename', $currentUser));
90
-        $this->assign('canEditUser', $this->barrierTest('editUser', $currentUser));
91
-        $this->assign('canSuspend', $this->barrierTest('suspend', $currentUser));
92
-        $this->assign('canEditRoles', $this->barrierTest('editRoles', $currentUser));
93
-
94
-        $this->setTemplate("usermanagement/main.tpl");
95
-    }
96
-
97
-    #region Access control
98
-
99
-    /**
100
-     * Action target for editing the roles assigned to a user
101
-     */
102
-    protected function editRoles()
103
-    {
104
-        $this->setHtmlTitle('User Management');
105
-        $database = $this->getDatabase();
106
-        $userId = WebRequest::getInt('user');
107
-
108
-        /** @var User $user */
109
-        $user = User::getById($userId, $database);
110
-
111
-        if ($user === false) {
112
-            throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
113
-        }
114
-
115
-        $roleData = $this->getRoleData(UserRole::getForUser($user->getId(), $database));
116
-
117
-        // Dual-mode action
118
-        if (WebRequest::wasPosted()) {
119
-            $this->validateCSRFToken();
120
-
121
-            $reason = WebRequest::postString('reason');
122
-            if ($reason === false || trim($reason) === '') {
123
-                throw new ApplicationLogicException('No reason specified for roles change');
124
-            }
125
-
126
-            /** @var UserRole[] $delete */
127
-            $delete = array();
128
-            /** @var string[] $delete */
129
-            $add = array();
130
-
131
-            foreach ($roleData as $name => $r) {
132
-                if ($r['allowEdit'] !== 1) {
133
-                    // not allowed, to touch this, so ignore it
134
-                    continue;
135
-                }
136
-
137
-                $newValue = WebRequest::postBoolean('role-' . $name) ? 1 : 0;
138
-                if ($newValue !== $r['active']) {
139
-                    if ($newValue === 0) {
140
-                        $delete[] = $r['object'];
141
-                    }
142
-
143
-                    if ($newValue === 1) {
144
-                        $add[] = $name;
145
-                    }
146
-                }
147
-            }
148
-
149
-            // Check there's something to do
150
-            if ((count($add) + count($delete)) === 0) {
151
-                $this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
152
-                SessionAlert::warning('No changes made to roles.');
153
-
154
-                return;
155
-            }
156
-
157
-            $removed = array();
158
-
159
-            /** @var UserRole $d */
160
-            foreach ($delete as $d) {
161
-                $removed[] = $d->getRole();
162
-                $d->delete();
163
-            }
164
-
165
-            foreach ($add as $x) {
166
-                $a = new UserRole();
167
-                $a->setUser($user->getId());
168
-                $a->setRole($x);
169
-                $a->setDatabase($database);
170
-                $a->save();
171
-            }
172
-
173
-            Logger::userRolesEdited($database, $user, $reason, $add, $removed);
174
-
175
-            // dummy save for optimistic locking. If this fails, the entire txn will roll back.
176
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
177
-            $user->save();
178
-
179
-            $this->getNotificationHelper()->userRolesEdited($user, $reason);
180
-            SessionAlert::quick('Roles changed for user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
181
-
182
-            $this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
183
-
184
-            return;
185
-        }
186
-        else {
187
-            $this->assignCSRFToken();
188
-            $this->setTemplate('usermanagement/roleedit.tpl');
189
-            $this->assign('user', $user);
190
-            $this->assign('roleData', $roleData);
191
-        }
192
-    }
193
-
194
-    /**
195
-     * Action target for suspending users
196
-     *
197
-     * @throws ApplicationLogicException
198
-     */
199
-    protected function suspend()
200
-    {
201
-        $this->setHtmlTitle('User Management');
202
-
203
-        $database = $this->getDatabase();
204
-
205
-        $userId = WebRequest::getInt('user');
206
-
207
-        /** @var User $user */
208
-        $user = User::getById($userId, $database);
209
-
210
-        if ($user === false) {
211
-            throw new ApplicationLogicException('Sorry, the user you are trying to suspend could not be found.');
212
-        }
213
-
214
-        if ($user->isSuspended()) {
215
-            throw new ApplicationLogicException('Sorry, the user you are trying to suspend is already suspended.');
216
-        }
217
-
218
-        // Dual-mode action
219
-        if (WebRequest::wasPosted()) {
220
-            $this->validateCSRFToken();
221
-            $reason = WebRequest::postString('reason');
222
-
223
-            if ($reason === null || trim($reason) === "") {
224
-                throw new ApplicationLogicException('No reason provided');
225
-            }
226
-
227
-            $user->setStatus(User::STATUS_SUSPENDED);
228
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
229
-            $user->save();
230
-            Logger::suspendedUser($database, $user, $reason);
231
-
232
-            $this->getNotificationHelper()->userSuspended($user, $reason);
233
-            SessionAlert::quick('Suspended user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
234
-
235
-            // send email
236
-            $this->sendStatusChangeEmail(
237
-                'Your WP:ACC account has been suspended',
238
-                'usermanagement/emails/suspended.tpl',
239
-                $reason,
240
-                $user,
241
-                User::getCurrent($database)->getUsername()
242
-            );
243
-
244
-            $this->redirect('userManagement');
245
-
246
-            return;
247
-        }
248
-        else {
249
-            $this->assignCSRFToken();
250
-            $this->setTemplate('usermanagement/changelevel-reason.tpl');
251
-            $this->assign('user', $user);
252
-            $this->assign('status', 'Suspended');
253
-            $this->assign("showReason", true);
254
-        }
255
-    }
256
-
257
-    /**
258
-     * Entry point for the decline action
259
-     *
260
-     * @throws ApplicationLogicException
261
-     */
262
-    protected function decline()
263
-    {
264
-        $this->setHtmlTitle('User Management');
265
-
266
-        $database = $this->getDatabase();
267
-
268
-        $userId = WebRequest::getInt('user');
269
-        $user = User::getById($userId, $database);
270
-
271
-        if ($user === false) {
272
-            throw new ApplicationLogicException('Sorry, the user you are trying to decline could not be found.');
273
-        }
274
-
275
-        if (!$user->isNewUser()) {
276
-            throw new ApplicationLogicException('Sorry, the user you are trying to decline is not new.');
277
-        }
278
-
279
-        // Dual-mode action
280
-        if (WebRequest::wasPosted()) {
281
-            $this->validateCSRFToken();
282
-            $reason = WebRequest::postString('reason');
283
-
284
-            if ($reason === null || trim($reason) === "") {
285
-                throw new ApplicationLogicException('No reason provided');
286
-            }
287
-
288
-            $user->setStatus(User::STATUS_DECLINED);
289
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
290
-            $user->save();
291
-            Logger::declinedUser($database, $user, $reason);
292
-
293
-            $this->getNotificationHelper()->userDeclined($user, $reason);
294
-            SessionAlert::quick('Declined user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
295
-
296
-            // send email
297
-            $this->sendStatusChangeEmail(
298
-                'Your WP:ACC account has been declined',
299
-                'usermanagement/emails/declined.tpl',
300
-                $reason,
301
-                $user,
302
-                User::getCurrent($database)->getUsername()
303
-            );
304
-
305
-            $this->redirect('userManagement');
306
-
307
-            return;
308
-        }
309
-        else {
310
-            $this->assignCSRFToken();
311
-            $this->setTemplate('usermanagement/changelevel-reason.tpl');
312
-            $this->assign('user', $user);
313
-            $this->assign('status', 'Declined');
314
-            $this->assign("showReason", true);
315
-        }
316
-    }
317
-
318
-    /**
319
-     * Entry point for the approve action
320
-     *
321
-     * @throws ApplicationLogicException
322
-     */
323
-    protected function approve()
324
-    {
325
-        $this->setHtmlTitle('User Management');
326
-
327
-        $database = $this->getDatabase();
328
-
329
-        $userId = WebRequest::getInt('user');
330
-        $user = User::getById($userId, $database);
331
-
332
-        if ($user === false) {
333
-            throw new ApplicationLogicException('Sorry, the user you are trying to approve could not be found.');
334
-        }
335
-
336
-        if ($user->isActive()) {
337
-            throw new ApplicationLogicException('Sorry, the user you are trying to approve is already an active user.');
338
-        }
339
-
340
-        // Dual-mode action
341
-        if (WebRequest::wasPosted()) {
342
-            $this->validateCSRFToken();
343
-            $user->setStatus(User::STATUS_ACTIVE);
344
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
345
-            $user->save();
346
-            Logger::approvedUser($database, $user);
347
-
348
-            $this->getNotificationHelper()->userApproved($user);
349
-            SessionAlert::quick('Approved user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
350
-
351
-            // send email
352
-            $this->sendStatusChangeEmail(
353
-                'Your WP:ACC account has been approved',
354
-                'usermanagement/emails/approved.tpl',
355
-                null,
356
-                $user,
357
-                User::getCurrent($database)->getUsername()
358
-            );
359
-
360
-            $this->redirect("userManagement");
361
-
362
-            return;
363
-        }
364
-        else {
365
-            $this->assignCSRFToken();
366
-            $this->setTemplate("usermanagement/changelevel-reason.tpl");
367
-            $this->assign("user", $user);
368
-            $this->assign("status", "Active");
369
-            $this->assign("showReason", false);
370
-        }
371
-    }
372
-
373
-    #endregion
374
-
375
-    #region Renaming / Editing
376
-
377
-    /**
378
-     * Entry point for the rename action
379
-     *
380
-     * @throws ApplicationLogicException
381
-     */
382
-    protected function rename()
383
-    {
384
-        $this->setHtmlTitle('User Management');
385
-
386
-        $database = $this->getDatabase();
387
-
388
-        $userId = WebRequest::getInt('user');
389
-        $user = User::getById($userId, $database);
390
-
391
-        if ($user === false) {
392
-            throw new ApplicationLogicException('Sorry, the user you are trying to rename could not be found.');
393
-        }
394
-
395
-        // Dual-mode action
396
-        if (WebRequest::wasPosted()) {
397
-            $this->validateCSRFToken();
398
-            $newUsername = WebRequest::postString('newname');
399
-
400
-            if ($newUsername === null || trim($newUsername) === "") {
401
-                throw new ApplicationLogicException('The new username cannot be empty');
402
-            }
403
-
404
-            if (User::getByUsername($newUsername, $database) != false) {
405
-                throw new ApplicationLogicException('The new username already exists');
406
-            }
407
-
408
-            $oldUsername = $user->getUsername();
409
-            $user->setUsername($newUsername);
410
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
411
-
412
-            $user->save();
413
-
414
-            $logEntryData = serialize(array(
415
-                'old' => $oldUsername,
416
-                'new' => $newUsername,
417
-            ));
418
-
419
-            Logger::renamedUser($database, $user, $logEntryData);
420
-
421
-            SessionAlert::quick("Changed User "
422
-                . htmlentities($oldUsername, ENT_COMPAT, 'UTF-8')
423
-                . " name to "
424
-                . htmlentities($newUsername, ENT_COMPAT, 'UTF-8'));
425
-
426
-            $this->getNotificationHelper()->userRenamed($user, $oldUsername);
427
-
428
-            // send an email to the user.
429
-            $this->assign('targetUsername', $user->getUsername());
430
-            $this->assign('toolAdmin', User::getCurrent($database)->getUsername());
431
-            $this->assign('oldUsername', $oldUsername);
432
-            $this->assign('mailingList', $this->adminMailingList);
433
-
434
-            $this->getEmailHelper()->sendMail(
435
-                $user->getEmail(),
436
-                'Your username on WP:ACC has been changed',
437
-                $this->fetchTemplate('usermanagement/emails/renamed.tpl'),
438
-                array('Reply-To' => $this->adminMailingList)
439
-            );
440
-
441
-            $this->redirect("userManagement");
442
-
443
-            return;
444
-        }
445
-        else {
446
-            $this->assignCSRFToken();
447
-            $this->setTemplate('usermanagement/renameuser.tpl');
448
-            $this->assign('user', $user);
449
-        }
450
-    }
451
-
452
-    /**
453
-     * Entry point for the edit action
454
-     *
455
-     * @throws ApplicationLogicException
456
-     */
457
-    protected function editUser()
458
-    {
459
-        $this->setHtmlTitle('User Management');
460
-
461
-        $database = $this->getDatabase();
462
-
463
-        $userId = WebRequest::getInt('user');
464
-        $user = User::getById($userId, $database);
465
-        $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
466
-
467
-        if ($user === false) {
468
-            throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
469
-        }
470
-
471
-        // Dual-mode action
472
-        if (WebRequest::wasPosted()) {
473
-            $this->validateCSRFToken();
474
-            $newEmail = WebRequest::postEmail('user_email');
475
-            $newOnWikiName = WebRequest::postString('user_onwikiname');
476
-
477
-            if ($newEmail === null) {
478
-                throw new ApplicationLogicException('Invalid email address');
479
-            }
480
-
481
-            if (!$oauth->isFullyLinked()) {
482
-                if (trim($newOnWikiName) == "") {
483
-                    throw new ApplicationLogicException('New on-wiki username cannot be blank');
484
-                }
485
-
486
-                $user->setOnWikiName($newOnWikiName);
487
-            }
488
-
489
-            $user->setEmail($newEmail);
490
-
491
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
492
-
493
-            $user->save();
494
-
495
-            Logger::userPreferencesChange($database, $user);
496
-            $this->getNotificationHelper()->userPrefChange($user);
497
-            SessionAlert::quick('Changes to user\'s preferences have been saved');
498
-
499
-            $this->redirect("userManagement");
500
-
501
-            return;
502
-        }
503
-        else {
504
-            $this->assignCSRFToken();
505
-            $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(),
506
-                $this->getSiteConfiguration());
507
-            $this->setTemplate('usermanagement/edituser.tpl');
508
-            $this->assign('user', $user);
509
-            $this->assign('oauth', $oauth);
510
-        }
511
-    }
512
-
513
-    #endregion
514
-
515
-    /**
516
-     * Sends a status change email to the user.
517
-     *
518
-     * @param string      $subject           The subject of the email
519
-     * @param string      $template          The smarty template to use
520
-     * @param string|null $reason            The reason for performing the status change
521
-     * @param User        $user              The user affected
522
-     * @param string      $toolAdminUsername The tool admin's username who is making the edit
523
-     */
524
-    private function sendStatusChangeEmail($subject, $template, $reason, $user, $toolAdminUsername)
525
-    {
526
-        $this->assign('targetUsername', $user->getUsername());
527
-        $this->assign('toolAdmin', $toolAdminUsername);
528
-        $this->assign('actionReason', $reason);
529
-        $this->assign('mailingList', $this->adminMailingList);
530
-
531
-        $this->getEmailHelper()->sendMail(
532
-            $user->getEmail(),
533
-            $subject,
534
-            $this->fetchTemplate($template),
535
-            array('Reply-To' => $this->adminMailingList)
536
-        );
537
-    }
538
-
539
-    /**
540
-     * @param UserRole[] $activeRoles
541
-     *
542
-     * @return array
543
-     */
544
-    private function getRoleData($activeRoles)
545
-    {
546
-        $availableRoles = $this->getSecurityManager()->getRoleConfiguration()->getAvailableRoles();
547
-
548
-        $currentUser = User::getCurrent($this->getDatabase());
549
-        $this->getSecurityManager()->getActiveRoles($currentUser, $userRoles, $inactiveRoles);
550
-
551
-        $initialValue = array('active' => 0, 'allowEdit' => 0, 'description' => '???', 'object' => null);
552
-
553
-        $roleData = array();
554
-        foreach ($availableRoles as $role => $data) {
555
-            $intersection = array_intersect($data['editableBy'], $userRoles);
556
-
557
-            $roleData[$role] = $initialValue;
558
-            $roleData[$role]['allowEdit'] = count($intersection) > 0 ? 1 : 0;
559
-            $roleData[$role]['description'] = $data['description'];
560
-        }
561
-
562
-        foreach ($activeRoles as $role) {
563
-            if (!isset($roleData[$role->getRole()])) {
564
-                // This value is no longer available in the configuration, allow changing (aka removing) it.
565
-                $roleData[$role->getRole()] = $initialValue;
566
-                $roleData[$role->getRole()]['allowEdit'] = 1;
567
-            }
568
-
569
-            $roleData[$role->getRole()]['object'] = $role;
570
-            $roleData[$role->getRole()]['active'] = 1;
571
-        }
572
-
573
-        return $roleData;
574
-    }
27
+	/** @var string */
28
+	private $adminMailingList = '[email protected]';
29
+
30
+	/**
31
+	 * Main function for this page, when no specific actions are called.
32
+	 */
33
+	protected function main()
34
+	{
35
+		$this->setHtmlTitle('User Management');
36
+
37
+		$database = $this->getDatabase();
38
+		$currentUser = User::getCurrent($database);
39
+
40
+		$userSearchRequest = WebRequest::getString('usersearch');
41
+		if ($userSearchRequest !== null) {
42
+			$searchedUser = User::getByUsername($userSearchRequest, $database);
43
+			if($searchedUser !== false) {
44
+				$this->redirect('statistics/users', 'detail', ['user' => $searchedUser->getId()]);
45
+				return;
46
+			}
47
+		}
48
+
49
+		// A bit hacky, but it's better than my last solution of creating an object for each user and passing that to
50
+		// the template. I still don't have a particularly good way of handling this.
51
+		OAuthUserHelper::prepareTokenCountStatement($database);
52
+
53
+		if (WebRequest::getBoolean("showAll")) {
54
+			$this->assign("showAll", true);
55
+
56
+			$suspendedUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_SUSPENDED)->fetch();
57
+			$this->assign("suspendedUsers", $suspendedUsers);
58
+
59
+			$declinedUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_DECLINED)->fetch();
60
+			$this->assign("declinedUsers", $declinedUsers);
61
+
62
+			UserSearchHelper::get($database)->getRoleMap($roleMap);
63
+		}
64
+		else {
65
+			$this->assign("showAll", false);
66
+			$this->assign("suspendedUsers", array());
67
+			$this->assign("declinedUsers", array());
68
+
69
+			UserSearchHelper::get($database)->statusIn(array('New', 'Active'))->getRoleMap($roleMap);
70
+		}
71
+
72
+		$newUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_NEW)->fetch();
73
+		$normalUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('user')->fetch();
74
+		$adminUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('admin')->fetch();
75
+		$checkUsers = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('checkuser')->fetch();
76
+		$toolRoots = UserSearchHelper::get($database)->byStatus(User::STATUS_ACTIVE)->byRole('toolRoot')->fetch();
77
+		$this->assign('newUsers', $newUsers);
78
+		$this->assign('normalUsers', $normalUsers);
79
+		$this->assign('adminUsers', $adminUsers);
80
+		$this->assign('checkUsers', $checkUsers);
81
+		$this->assign('toolRoots', $toolRoots);
82
+
83
+		$this->assign('roles', $roleMap);
84
+
85
+		$this->addJs("/api.php?action=users&all=true&targetVariable=typeaheaddata");
86
+
87
+		$this->assign('canApprove', $this->barrierTest('approve', $currentUser));
88
+		$this->assign('canDecline', $this->barrierTest('decline', $currentUser));
89
+		$this->assign('canRename', $this->barrierTest('rename', $currentUser));
90
+		$this->assign('canEditUser', $this->barrierTest('editUser', $currentUser));
91
+		$this->assign('canSuspend', $this->barrierTest('suspend', $currentUser));
92
+		$this->assign('canEditRoles', $this->barrierTest('editRoles', $currentUser));
93
+
94
+		$this->setTemplate("usermanagement/main.tpl");
95
+	}
96
+
97
+	#region Access control
98
+
99
+	/**
100
+	 * Action target for editing the roles assigned to a user
101
+	 */
102
+	protected function editRoles()
103
+	{
104
+		$this->setHtmlTitle('User Management');
105
+		$database = $this->getDatabase();
106
+		$userId = WebRequest::getInt('user');
107
+
108
+		/** @var User $user */
109
+		$user = User::getById($userId, $database);
110
+
111
+		if ($user === false) {
112
+			throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
113
+		}
114
+
115
+		$roleData = $this->getRoleData(UserRole::getForUser($user->getId(), $database));
116
+
117
+		// Dual-mode action
118
+		if (WebRequest::wasPosted()) {
119
+			$this->validateCSRFToken();
120
+
121
+			$reason = WebRequest::postString('reason');
122
+			if ($reason === false || trim($reason) === '') {
123
+				throw new ApplicationLogicException('No reason specified for roles change');
124
+			}
125
+
126
+			/** @var UserRole[] $delete */
127
+			$delete = array();
128
+			/** @var string[] $delete */
129
+			$add = array();
130
+
131
+			foreach ($roleData as $name => $r) {
132
+				if ($r['allowEdit'] !== 1) {
133
+					// not allowed, to touch this, so ignore it
134
+					continue;
135
+				}
136
+
137
+				$newValue = WebRequest::postBoolean('role-' . $name) ? 1 : 0;
138
+				if ($newValue !== $r['active']) {
139
+					if ($newValue === 0) {
140
+						$delete[] = $r['object'];
141
+					}
142
+
143
+					if ($newValue === 1) {
144
+						$add[] = $name;
145
+					}
146
+				}
147
+			}
148
+
149
+			// Check there's something to do
150
+			if ((count($add) + count($delete)) === 0) {
151
+				$this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
152
+				SessionAlert::warning('No changes made to roles.');
153
+
154
+				return;
155
+			}
156
+
157
+			$removed = array();
158
+
159
+			/** @var UserRole $d */
160
+			foreach ($delete as $d) {
161
+				$removed[] = $d->getRole();
162
+				$d->delete();
163
+			}
164
+
165
+			foreach ($add as $x) {
166
+				$a = new UserRole();
167
+				$a->setUser($user->getId());
168
+				$a->setRole($x);
169
+				$a->setDatabase($database);
170
+				$a->save();
171
+			}
172
+
173
+			Logger::userRolesEdited($database, $user, $reason, $add, $removed);
174
+
175
+			// dummy save for optimistic locking. If this fails, the entire txn will roll back.
176
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
177
+			$user->save();
178
+
179
+			$this->getNotificationHelper()->userRolesEdited($user, $reason);
180
+			SessionAlert::quick('Roles changed for user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
181
+
182
+			$this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
183
+
184
+			return;
185
+		}
186
+		else {
187
+			$this->assignCSRFToken();
188
+			$this->setTemplate('usermanagement/roleedit.tpl');
189
+			$this->assign('user', $user);
190
+			$this->assign('roleData', $roleData);
191
+		}
192
+	}
193
+
194
+	/**
195
+	 * Action target for suspending users
196
+	 *
197
+	 * @throws ApplicationLogicException
198
+	 */
199
+	protected function suspend()
200
+	{
201
+		$this->setHtmlTitle('User Management');
202
+
203
+		$database = $this->getDatabase();
204
+
205
+		$userId = WebRequest::getInt('user');
206
+
207
+		/** @var User $user */
208
+		$user = User::getById($userId, $database);
209
+
210
+		if ($user === false) {
211
+			throw new ApplicationLogicException('Sorry, the user you are trying to suspend could not be found.');
212
+		}
213
+
214
+		if ($user->isSuspended()) {
215
+			throw new ApplicationLogicException('Sorry, the user you are trying to suspend is already suspended.');
216
+		}
217
+
218
+		// Dual-mode action
219
+		if (WebRequest::wasPosted()) {
220
+			$this->validateCSRFToken();
221
+			$reason = WebRequest::postString('reason');
222
+
223
+			if ($reason === null || trim($reason) === "") {
224
+				throw new ApplicationLogicException('No reason provided');
225
+			}
226
+
227
+			$user->setStatus(User::STATUS_SUSPENDED);
228
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
229
+			$user->save();
230
+			Logger::suspendedUser($database, $user, $reason);
231
+
232
+			$this->getNotificationHelper()->userSuspended($user, $reason);
233
+			SessionAlert::quick('Suspended user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
234
+
235
+			// send email
236
+			$this->sendStatusChangeEmail(
237
+				'Your WP:ACC account has been suspended',
238
+				'usermanagement/emails/suspended.tpl',
239
+				$reason,
240
+				$user,
241
+				User::getCurrent($database)->getUsername()
242
+			);
243
+
244
+			$this->redirect('userManagement');
245
+
246
+			return;
247
+		}
248
+		else {
249
+			$this->assignCSRFToken();
250
+			$this->setTemplate('usermanagement/changelevel-reason.tpl');
251
+			$this->assign('user', $user);
252
+			$this->assign('status', 'Suspended');
253
+			$this->assign("showReason", true);
254
+		}
255
+	}
256
+
257
+	/**
258
+	 * Entry point for the decline action
259
+	 *
260
+	 * @throws ApplicationLogicException
261
+	 */
262
+	protected function decline()
263
+	{
264
+		$this->setHtmlTitle('User Management');
265
+
266
+		$database = $this->getDatabase();
267
+
268
+		$userId = WebRequest::getInt('user');
269
+		$user = User::getById($userId, $database);
270
+
271
+		if ($user === false) {
272
+			throw new ApplicationLogicException('Sorry, the user you are trying to decline could not be found.');
273
+		}
274
+
275
+		if (!$user->isNewUser()) {
276
+			throw new ApplicationLogicException('Sorry, the user you are trying to decline is not new.');
277
+		}
278
+
279
+		// Dual-mode action
280
+		if (WebRequest::wasPosted()) {
281
+			$this->validateCSRFToken();
282
+			$reason = WebRequest::postString('reason');
283
+
284
+			if ($reason === null || trim($reason) === "") {
285
+				throw new ApplicationLogicException('No reason provided');
286
+			}
287
+
288
+			$user->setStatus(User::STATUS_DECLINED);
289
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
290
+			$user->save();
291
+			Logger::declinedUser($database, $user, $reason);
292
+
293
+			$this->getNotificationHelper()->userDeclined($user, $reason);
294
+			SessionAlert::quick('Declined user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
295
+
296
+			// send email
297
+			$this->sendStatusChangeEmail(
298
+				'Your WP:ACC account has been declined',
299
+				'usermanagement/emails/declined.tpl',
300
+				$reason,
301
+				$user,
302
+				User::getCurrent($database)->getUsername()
303
+			);
304
+
305
+			$this->redirect('userManagement');
306
+
307
+			return;
308
+		}
309
+		else {
310
+			$this->assignCSRFToken();
311
+			$this->setTemplate('usermanagement/changelevel-reason.tpl');
312
+			$this->assign('user', $user);
313
+			$this->assign('status', 'Declined');
314
+			$this->assign("showReason", true);
315
+		}
316
+	}
317
+
318
+	/**
319
+	 * Entry point for the approve action
320
+	 *
321
+	 * @throws ApplicationLogicException
322
+	 */
323
+	protected function approve()
324
+	{
325
+		$this->setHtmlTitle('User Management');
326
+
327
+		$database = $this->getDatabase();
328
+
329
+		$userId = WebRequest::getInt('user');
330
+		$user = User::getById($userId, $database);
331
+
332
+		if ($user === false) {
333
+			throw new ApplicationLogicException('Sorry, the user you are trying to approve could not be found.');
334
+		}
335
+
336
+		if ($user->isActive()) {
337
+			throw new ApplicationLogicException('Sorry, the user you are trying to approve is already an active user.');
338
+		}
339
+
340
+		// Dual-mode action
341
+		if (WebRequest::wasPosted()) {
342
+			$this->validateCSRFToken();
343
+			$user->setStatus(User::STATUS_ACTIVE);
344
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
345
+			$user->save();
346
+			Logger::approvedUser($database, $user);
347
+
348
+			$this->getNotificationHelper()->userApproved($user);
349
+			SessionAlert::quick('Approved user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
350
+
351
+			// send email
352
+			$this->sendStatusChangeEmail(
353
+				'Your WP:ACC account has been approved',
354
+				'usermanagement/emails/approved.tpl',
355
+				null,
356
+				$user,
357
+				User::getCurrent($database)->getUsername()
358
+			);
359
+
360
+			$this->redirect("userManagement");
361
+
362
+			return;
363
+		}
364
+		else {
365
+			$this->assignCSRFToken();
366
+			$this->setTemplate("usermanagement/changelevel-reason.tpl");
367
+			$this->assign("user", $user);
368
+			$this->assign("status", "Active");
369
+			$this->assign("showReason", false);
370
+		}
371
+	}
372
+
373
+	#endregion
374
+
375
+	#region Renaming / Editing
376
+
377
+	/**
378
+	 * Entry point for the rename action
379
+	 *
380
+	 * @throws ApplicationLogicException
381
+	 */
382
+	protected function rename()
383
+	{
384
+		$this->setHtmlTitle('User Management');
385
+
386
+		$database = $this->getDatabase();
387
+
388
+		$userId = WebRequest::getInt('user');
389
+		$user = User::getById($userId, $database);
390
+
391
+		if ($user === false) {
392
+			throw new ApplicationLogicException('Sorry, the user you are trying to rename could not be found.');
393
+		}
394
+
395
+		// Dual-mode action
396
+		if (WebRequest::wasPosted()) {
397
+			$this->validateCSRFToken();
398
+			$newUsername = WebRequest::postString('newname');
399
+
400
+			if ($newUsername === null || trim($newUsername) === "") {
401
+				throw new ApplicationLogicException('The new username cannot be empty');
402
+			}
403
+
404
+			if (User::getByUsername($newUsername, $database) != false) {
405
+				throw new ApplicationLogicException('The new username already exists');
406
+			}
407
+
408
+			$oldUsername = $user->getUsername();
409
+			$user->setUsername($newUsername);
410
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
411
+
412
+			$user->save();
413
+
414
+			$logEntryData = serialize(array(
415
+				'old' => $oldUsername,
416
+				'new' => $newUsername,
417
+			));
418
+
419
+			Logger::renamedUser($database, $user, $logEntryData);
420
+
421
+			SessionAlert::quick("Changed User "
422
+				. htmlentities($oldUsername, ENT_COMPAT, 'UTF-8')
423
+				. " name to "
424
+				. htmlentities($newUsername, ENT_COMPAT, 'UTF-8'));
425
+
426
+			$this->getNotificationHelper()->userRenamed($user, $oldUsername);
427
+
428
+			// send an email to the user.
429
+			$this->assign('targetUsername', $user->getUsername());
430
+			$this->assign('toolAdmin', User::getCurrent($database)->getUsername());
431
+			$this->assign('oldUsername', $oldUsername);
432
+			$this->assign('mailingList', $this->adminMailingList);
433
+
434
+			$this->getEmailHelper()->sendMail(
435
+				$user->getEmail(),
436
+				'Your username on WP:ACC has been changed',
437
+				$this->fetchTemplate('usermanagement/emails/renamed.tpl'),
438
+				array('Reply-To' => $this->adminMailingList)
439
+			);
440
+
441
+			$this->redirect("userManagement");
442
+
443
+			return;
444
+		}
445
+		else {
446
+			$this->assignCSRFToken();
447
+			$this->setTemplate('usermanagement/renameuser.tpl');
448
+			$this->assign('user', $user);
449
+		}
450
+	}
451
+
452
+	/**
453
+	 * Entry point for the edit action
454
+	 *
455
+	 * @throws ApplicationLogicException
456
+	 */
457
+	protected function editUser()
458
+	{
459
+		$this->setHtmlTitle('User Management');
460
+
461
+		$database = $this->getDatabase();
462
+
463
+		$userId = WebRequest::getInt('user');
464
+		$user = User::getById($userId, $database);
465
+		$oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
466
+
467
+		if ($user === false) {
468
+			throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
469
+		}
470
+
471
+		// Dual-mode action
472
+		if (WebRequest::wasPosted()) {
473
+			$this->validateCSRFToken();
474
+			$newEmail = WebRequest::postEmail('user_email');
475
+			$newOnWikiName = WebRequest::postString('user_onwikiname');
476
+
477
+			if ($newEmail === null) {
478
+				throw new ApplicationLogicException('Invalid email address');
479
+			}
480
+
481
+			if (!$oauth->isFullyLinked()) {
482
+				if (trim($newOnWikiName) == "") {
483
+					throw new ApplicationLogicException('New on-wiki username cannot be blank');
484
+				}
485
+
486
+				$user->setOnWikiName($newOnWikiName);
487
+			}
488
+
489
+			$user->setEmail($newEmail);
490
+
491
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
492
+
493
+			$user->save();
494
+
495
+			Logger::userPreferencesChange($database, $user);
496
+			$this->getNotificationHelper()->userPrefChange($user);
497
+			SessionAlert::quick('Changes to user\'s preferences have been saved');
498
+
499
+			$this->redirect("userManagement");
500
+
501
+			return;
502
+		}
503
+		else {
504
+			$this->assignCSRFToken();
505
+			$oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(),
506
+				$this->getSiteConfiguration());
507
+			$this->setTemplate('usermanagement/edituser.tpl');
508
+			$this->assign('user', $user);
509
+			$this->assign('oauth', $oauth);
510
+		}
511
+	}
512
+
513
+	#endregion
514
+
515
+	/**
516
+	 * Sends a status change email to the user.
517
+	 *
518
+	 * @param string      $subject           The subject of the email
519
+	 * @param string      $template          The smarty template to use
520
+	 * @param string|null $reason            The reason for performing the status change
521
+	 * @param User        $user              The user affected
522
+	 * @param string      $toolAdminUsername The tool admin's username who is making the edit
523
+	 */
524
+	private function sendStatusChangeEmail($subject, $template, $reason, $user, $toolAdminUsername)
525
+	{
526
+		$this->assign('targetUsername', $user->getUsername());
527
+		$this->assign('toolAdmin', $toolAdminUsername);
528
+		$this->assign('actionReason', $reason);
529
+		$this->assign('mailingList', $this->adminMailingList);
530
+
531
+		$this->getEmailHelper()->sendMail(
532
+			$user->getEmail(),
533
+			$subject,
534
+			$this->fetchTemplate($template),
535
+			array('Reply-To' => $this->adminMailingList)
536
+		);
537
+	}
538
+
539
+	/**
540
+	 * @param UserRole[] $activeRoles
541
+	 *
542
+	 * @return array
543
+	 */
544
+	private function getRoleData($activeRoles)
545
+	{
546
+		$availableRoles = $this->getSecurityManager()->getRoleConfiguration()->getAvailableRoles();
547
+
548
+		$currentUser = User::getCurrent($this->getDatabase());
549
+		$this->getSecurityManager()->getActiveRoles($currentUser, $userRoles, $inactiveRoles);
550
+
551
+		$initialValue = array('active' => 0, 'allowEdit' => 0, 'description' => '???', 'object' => null);
552
+
553
+		$roleData = array();
554
+		foreach ($availableRoles as $role => $data) {
555
+			$intersection = array_intersect($data['editableBy'], $userRoles);
556
+
557
+			$roleData[$role] = $initialValue;
558
+			$roleData[$role]['allowEdit'] = count($intersection) > 0 ? 1 : 0;
559
+			$roleData[$role]['description'] = $data['description'];
560
+		}
561
+
562
+		foreach ($activeRoles as $role) {
563
+			if (!isset($roleData[$role->getRole()])) {
564
+				// This value is no longer available in the configuration, allow changing (aka removing) it.
565
+				$roleData[$role->getRole()] = $initialValue;
566
+				$roleData[$role->getRole()]['allowEdit'] = 1;
567
+			}
568
+
569
+			$roleData[$role->getRole()]['object'] = $role;
570
+			$roleData[$role->getRole()]['active'] = 1;
571
+		}
572
+
573
+		return $roleData;
574
+	}
575 575
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -40,7 +40,7 @@  discard block
 block discarded – undo
40 40
         $userSearchRequest = WebRequest::getString('usersearch');
41 41
         if ($userSearchRequest !== null) {
42 42
             $searchedUser = User::getByUsername($userSearchRequest, $database);
43
-            if($searchedUser !== false) {
43
+            if ($searchedUser !== false) {
44 44
                 $this->redirect('statistics/users', 'detail', ['user' => $searchedUser->getId()]);
45 45
                 return;
46 46
             }
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
                     continue;
135 135
                 }
136 136
 
137
-                $newValue = WebRequest::postBoolean('role-' . $name) ? 1 : 0;
137
+                $newValue = WebRequest::postBoolean('role-'.$name) ? 1 : 0;
138 138
                 if ($newValue !== $r['active']) {
139 139
                     if ($newValue === 0) {
140 140
                         $delete[] = $r['object'];
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
             $user->save();
178 178
 
179 179
             $this->getNotificationHelper()->userRolesEdited($user, $reason);
180
-            SessionAlert::quick('Roles changed for user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
180
+            SessionAlert::quick('Roles changed for user '.htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
181 181
 
182 182
             $this->redirect('statistics/users', 'detail', array('user' => $user->getId()));
183 183
 
@@ -230,7 +230,7 @@  discard block
 block discarded – undo
230 230
             Logger::suspendedUser($database, $user, $reason);
231 231
 
232 232
             $this->getNotificationHelper()->userSuspended($user, $reason);
233
-            SessionAlert::quick('Suspended user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
233
+            SessionAlert::quick('Suspended user '.htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
234 234
 
235 235
             // send email
236 236
             $this->sendStatusChangeEmail(
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
             Logger::declinedUser($database, $user, $reason);
292 292
 
293 293
             $this->getNotificationHelper()->userDeclined($user, $reason);
294
-            SessionAlert::quick('Declined user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
294
+            SessionAlert::quick('Declined user '.htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
295 295
 
296 296
             // send email
297 297
             $this->sendStatusChangeEmail(
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
             Logger::approvedUser($database, $user);
347 347
 
348 348
             $this->getNotificationHelper()->userApproved($user);
349
-            SessionAlert::quick('Approved user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
349
+            SessionAlert::quick('Approved user '.htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
350 350
 
351 351
             // send email
352 352
             $this->sendStatusChangeEmail(
Please login to merge, or discard this patch.
includes/Pages/PageXffDemo.php 2 patches
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -13,136 +13,136 @@
 block discarded – undo
13 13
 
14 14
 class PageXffDemo extends InternalPageBase
15 15
 {
16
-    use RequestData;
17
-
18
-    /**
19
-     * @inheritDoc
20
-     */
21
-    protected function main()
22
-    {
23
-        $this->setTemplate('xffdemo.tpl');
24
-
25
-        // requestHasForwardedIp == false
26
-        // requestProxyData
27
-        // requestRealIp == proxy
28
-        // requestForwardedIp == xff header
29
-        // forwardedOrigin  == top of the chain, assuming xff is trusted
30
-
31
-
32
-        $this->assign('demo2', [
33
-            [
34
-                'trust' => true,
35
-                'trustedlink' => true,
36
-                'ip' => '172.16.0.164',
37
-                'routable' => false,
38
-
39
-            ],[
40
-                'trust' => true,
41
-                'ip' => '198.51.100.123',
42
-                'routable' => true,
43
-                'rdns' => 'trustedproxy.example.com',
44
-
45
-            ],[
46
-                'trust' => true,
47
-                'ip' => '192.0.2.1',
48
-                'routable' => true,
49
-                'rdns' => 'client.users.example.org',
50
-                'location' => [
51
-                    'cityName' => 'San Francisco',
52
-                    'regionName' => 'California',
53
-                    'countryName' => 'United States'
54
-                ],
55
-                'showlinks' => true
56
-            ]
57
-        ]);
58
-
59
-        $this->assign('demo3', [
60
-            [
61
-                'trust' => true,
62
-                'trustedlink' => true,
63
-                'ip' => '172.16.0.164',
64
-                'routable' => false,
65
-
66
-            ],[
67
-                'trust' => false,
68
-                'ip' => '198.51.100.234',
69
-                'routable' => true,
70
-                'rdns' => 'sketchyproxy.example.com',
71
-                'showlinks' => true
72
-
73
-            ],[
74
-                'trust' => false,
75
-                'ip' => '192.0.2.1',
76
-                'routable' => true,
77
-                'rdns' => 'client.users.example.org',
78
-                'location' => [
79
-                    'cityName' => 'San Francisco',
80
-                    'regionName' => 'California',
81
-                    'countryName' => 'United States'
82
-                ],
83
-                'showlinks' => true
84
-            ]
85
-        ]);
86
-
87
-        $this->assign('demo4', [
88
-            [
89
-                'trust' => true,
90
-                'trustedlink' => true,
91
-                'ip' => '172.16.0.164',
92
-                'routable' => false,
93
-
94
-            ],[
95
-                'trust' => true,
96
-                'ip' => '198.51.100.123',
97
-                'routable' => true,
98
-                'rdns' => 'trustedproxy.example.com',
99
-            ],[
100
-                'trust' => false,
101
-                'ip' => '198.51.100.234',
102
-                'routable' => true,
103
-                'rdns' => 'sketchyproxy.example.com',
104
-                'showlinks' => true
105
-            ], [
106
-                'trust' => false,
107
-                'trustedlink' => true,
108
-                'ip' => '198.51.100.124',
109
-                'routable' => true,
110
-                'rdns' => 'trustedproxy2.example.com',
111
-                'showlinks' => true
112
-            ],[
113
-                'trust' => false,
114
-                'ip' => '192.0.2.1',
115
-                'routable' => true,
116
-                'rdns' => 'client.users.example.org',
117
-                'location' => [
118
-                    'cityName' => 'San Francisco',
119
-                    'regionName' => 'California',
120
-                    'countryName' => 'United States'
121
-                ],
122
-                'showlinks' => true
123
-            ]
124
-        ]);
125
-
126
-        $this->assign('demo1', [
127
-            [
128
-                'trust' => true,
129
-                'trustedlink' => true,
130
-                'ip' => '172.16.0.164',
131
-                'routable' => false,
132
-
133
-            ], [
134
-                'trust' => true,
135
-                'trustedlink' => true,
136
-                'ip' => '192.0.2.1',
137
-                'routable' => true,
138
-                'rdns' => 'client.users.example.org',
139
-                'location' => [
140
-                    'cityName' => 'San Francisco',
141
-                    'regionName' => 'California',
142
-                    'countryName' => 'United States'
143
-                ],
144
-                'showlinks' => true
145
-            ]
146
-        ]);
147
-    }
16
+	use RequestData;
17
+
18
+	/**
19
+	 * @inheritDoc
20
+	 */
21
+	protected function main()
22
+	{
23
+		$this->setTemplate('xffdemo.tpl');
24
+
25
+		// requestHasForwardedIp == false
26
+		// requestProxyData
27
+		// requestRealIp == proxy
28
+		// requestForwardedIp == xff header
29
+		// forwardedOrigin  == top of the chain, assuming xff is trusted
30
+
31
+
32
+		$this->assign('demo2', [
33
+			[
34
+				'trust' => true,
35
+				'trustedlink' => true,
36
+				'ip' => '172.16.0.164',
37
+				'routable' => false,
38
+
39
+			],[
40
+				'trust' => true,
41
+				'ip' => '198.51.100.123',
42
+				'routable' => true,
43
+				'rdns' => 'trustedproxy.example.com',
44
+
45
+			],[
46
+				'trust' => true,
47
+				'ip' => '192.0.2.1',
48
+				'routable' => true,
49
+				'rdns' => 'client.users.example.org',
50
+				'location' => [
51
+					'cityName' => 'San Francisco',
52
+					'regionName' => 'California',
53
+					'countryName' => 'United States'
54
+				],
55
+				'showlinks' => true
56
+			]
57
+		]);
58
+
59
+		$this->assign('demo3', [
60
+			[
61
+				'trust' => true,
62
+				'trustedlink' => true,
63
+				'ip' => '172.16.0.164',
64
+				'routable' => false,
65
+
66
+			],[
67
+				'trust' => false,
68
+				'ip' => '198.51.100.234',
69
+				'routable' => true,
70
+				'rdns' => 'sketchyproxy.example.com',
71
+				'showlinks' => true
72
+
73
+			],[
74
+				'trust' => false,
75
+				'ip' => '192.0.2.1',
76
+				'routable' => true,
77
+				'rdns' => 'client.users.example.org',
78
+				'location' => [
79
+					'cityName' => 'San Francisco',
80
+					'regionName' => 'California',
81
+					'countryName' => 'United States'
82
+				],
83
+				'showlinks' => true
84
+			]
85
+		]);
86
+
87
+		$this->assign('demo4', [
88
+			[
89
+				'trust' => true,
90
+				'trustedlink' => true,
91
+				'ip' => '172.16.0.164',
92
+				'routable' => false,
93
+
94
+			],[
95
+				'trust' => true,
96
+				'ip' => '198.51.100.123',
97
+				'routable' => true,
98
+				'rdns' => 'trustedproxy.example.com',
99
+			],[
100
+				'trust' => false,
101
+				'ip' => '198.51.100.234',
102
+				'routable' => true,
103
+				'rdns' => 'sketchyproxy.example.com',
104
+				'showlinks' => true
105
+			], [
106
+				'trust' => false,
107
+				'trustedlink' => true,
108
+				'ip' => '198.51.100.124',
109
+				'routable' => true,
110
+				'rdns' => 'trustedproxy2.example.com',
111
+				'showlinks' => true
112
+			],[
113
+				'trust' => false,
114
+				'ip' => '192.0.2.1',
115
+				'routable' => true,
116
+				'rdns' => 'client.users.example.org',
117
+				'location' => [
118
+					'cityName' => 'San Francisco',
119
+					'regionName' => 'California',
120
+					'countryName' => 'United States'
121
+				],
122
+				'showlinks' => true
123
+			]
124
+		]);
125
+
126
+		$this->assign('demo1', [
127
+			[
128
+				'trust' => true,
129
+				'trustedlink' => true,
130
+				'ip' => '172.16.0.164',
131
+				'routable' => false,
132
+
133
+			], [
134
+				'trust' => true,
135
+				'trustedlink' => true,
136
+				'ip' => '192.0.2.1',
137
+				'routable' => true,
138
+				'rdns' => 'client.users.example.org',
139
+				'location' => [
140
+					'cityName' => 'San Francisco',
141
+					'regionName' => 'California',
142
+					'countryName' => 'United States'
143
+				],
144
+				'showlinks' => true
145
+			]
146
+		]);
147
+	}
148 148
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -36,13 +36,13 @@  discard block
 block discarded – undo
36 36
                 'ip' => '172.16.0.164',
37 37
                 'routable' => false,
38 38
 
39
-            ],[
39
+            ], [
40 40
                 'trust' => true,
41 41
                 'ip' => '198.51.100.123',
42 42
                 'routable' => true,
43 43
                 'rdns' => 'trustedproxy.example.com',
44 44
 
45
-            ],[
45
+            ], [
46 46
                 'trust' => true,
47 47
                 'ip' => '192.0.2.1',
48 48
                 'routable' => true,
@@ -63,14 +63,14 @@  discard block
 block discarded – undo
63 63
                 'ip' => '172.16.0.164',
64 64
                 'routable' => false,
65 65
 
66
-            ],[
66
+            ], [
67 67
                 'trust' => false,
68 68
                 'ip' => '198.51.100.234',
69 69
                 'routable' => true,
70 70
                 'rdns' => 'sketchyproxy.example.com',
71 71
                 'showlinks' => true
72 72
 
73
-            ],[
73
+            ], [
74 74
                 'trust' => false,
75 75
                 'ip' => '192.0.2.1',
76 76
                 'routable' => true,
@@ -91,12 +91,12 @@  discard block
 block discarded – undo
91 91
                 'ip' => '172.16.0.164',
92 92
                 'routable' => false,
93 93
 
94
-            ],[
94
+            ], [
95 95
                 'trust' => true,
96 96
                 'ip' => '198.51.100.123',
97 97
                 'routable' => true,
98 98
                 'rdns' => 'trustedproxy.example.com',
99
-            ],[
99
+            ], [
100 100
                 'trust' => false,
101 101
                 'ip' => '198.51.100.234',
102 102
                 'routable' => true,
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
                 'routable' => true,
110 110
                 'rdns' => 'trustedproxy2.example.com',
111 111
                 'showlinks' => true
112
-            ],[
112
+            ], [
113 113
                 'trust' => false,
114 114
                 'ip' => '192.0.2.1',
115 115
                 'routable' => true,
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PageForgotPassword.php 2 patches
Indentation   +204 added lines, -204 removed lines patch added patch discarded remove patch
@@ -22,208 +22,208 @@
 block discarded – undo
22 22
 
23 23
 class PageForgotPassword extends InternalPageBase
24 24
 {
25
-    /**
26
-     * Main function for this page, when no specific actions are called.
27
-     *
28
-     * This is the forgotten password reset form
29
-     * @category Security-Critical
30
-     */
31
-    protected function main()
32
-    {
33
-        if (WebRequest::wasPosted()) {
34
-            $this->validateCSRFToken();
35
-            $username = WebRequest::postString('username');
36
-            $email = WebRequest::postEmail('email');
37
-            $database = $this->getDatabase();
38
-
39
-            if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
40
-                throw new ApplicationLogicException("Both username and email address must be specified!");
41
-            }
42
-
43
-            $user = User::getByUsername($username, $database);
44
-            $this->sendResetMail($user, $email);
45
-
46
-            SessionAlert::success('<strong>Your password reset request has been completed.</strong> Please check your e-mail.');
47
-
48
-            $this->redirect('login');
49
-        }
50
-        else {
51
-            $this->assignCSRFToken();
52
-            $this->setTemplate('forgot-password/forgotpw.tpl');
53
-        }
54
-    }
55
-
56
-    /**
57
-     * Sends a reset email if the user is authenticated
58
-     *
59
-     * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
60
-     *                            check anyway within this method and silently skip if we don't have a user.
61
-     * @param string       $email The provided email address
62
-     */
63
-    private function sendResetMail($user, $email)
64
-    {
65
-        // If the user isn't found, or the email address is wrong, skip sending the details silently.
66
-        if (!$user instanceof User) {
67
-            return;
68
-        }
69
-
70
-        if (strtolower($user->getEmail()) === strtolower($email)) {
71
-            $clientIp = $this->getXffTrustProvider()
72
-                ->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
73
-
74
-            $this->cleanExistingTokens($user);
75
-
76
-            $hash = Base32::encode(openssl_random_pseudo_bytes(30));
77
-
78
-            $encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
79
-
80
-            $cred = new Credential();
81
-            $cred->setDatabase($this->getDatabase());
82
-            $cred->setFactor(-1);
83
-            $cred->setUserId($user->getId());
84
-            $cred->setType('reset');
85
-            $cred->setData($encryptionHelper->encryptData($hash));
86
-            $cred->setVersion(0);
87
-            $cred->setDisabled(0);
88
-            $cred->setTimeout(new DateTimeImmutable('+ 1 hour'));
89
-            $cred->setPriority(9);
90
-            $cred->save();
91
-
92
-            $this->assign("user", $user);
93
-            $this->assign("hash", $hash);
94
-            $this->assign("remoteAddress", $clientIp);
95
-
96
-            $emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
97
-
98
-            $this->getEmailHelper()->sendMail($user->getEmail(), "WP:ACC password reset", $emailContent);
99
-        }
100
-    }
101
-
102
-    /**
103
-     * Entry point for the reset action
104
-     *
105
-     * This is the reset password part of the form.
106
-     * @category Security-Critical
107
-     */
108
-    protected function reset()
109
-    {
110
-        $si = WebRequest::getString('si');
111
-        $id = WebRequest::getString('id');
112
-
113
-        if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
114
-            throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
115
-        }
116
-
117
-        $database = $this->getDatabase();
118
-        $user = $this->getResettingUser($id, $database, $si);
119
-
120
-        // Dual mode
121
-        if (WebRequest::wasPosted()) {
122
-            $this->validateCSRFToken();
123
-            try {
124
-                $this->doReset($user);
125
-                $this->cleanExistingTokens($user);
126
-            }
127
-            catch (ApplicationLogicException $ex) {
128
-                SessionAlert::error($ex->getMessage());
129
-                $this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
130
-
131
-                return;
132
-            }
133
-        }
134
-        else {
135
-            $this->assignCSRFToken();
136
-            $this->assign('user', $user);
137
-            $this->setTemplate('forgot-password/forgotpwreset.tpl');
138
-        }
139
-    }
140
-
141
-    /**
142
-     * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
143
-     *
144
-     * @param integer     $id       The ID of the user to retrieve
145
-     * @param PdoDatabase $database The database object to use
146
-     * @param string      $si       The reset hash provided
147
-     *
148
-     * @return User
149
-     * @throws ApplicationLogicException
150
-     */
151
-    private function getResettingUser($id, $database, $si)
152
-    {
153
-        $user = User::getById($id, $database);
154
-
155
-        if ($user === false ||  $user->isCommunityUser()) {
156
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
157
-        }
158
-
159
-        $statement = $database->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
160
-        $statement->execute([':user' => $user->getId()]);
161
-
162
-        /** @var Credential $credential */
163
-        $credential = $statement->fetchObject(Credential::class);
164
-
165
-        $statement->closeCursor();
166
-
167
-        if ($credential === false) {
168
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
169
-        }
170
-
171
-        $credential->setDatabase($database);
172
-
173
-        $encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
174
-        if ($encryptionHelper->decryptData($credential->getData()) != $si) {
175
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
176
-        }
177
-
178
-        if ($credential->getTimeout() < new DateTimeImmutable()) {
179
-            $credential->delete();
180
-            throw new ApplicationLogicException("Password reset token expired. Please try again.");
181
-        }
182
-
183
-        return $user;
184
-    }
185
-
186
-    /**
187
-     * Performs the setting of the new password
188
-     *
189
-     * @param User $user The user to set the password for
190
-     *
191
-     * @throws ApplicationLogicException
192
-     */
193
-    private function doReset(User $user)
194
-    {
195
-        $pw = WebRequest::postString('pw');
196
-        $pw2 = WebRequest::postString('pw2');
197
-
198
-        if ($pw !== $pw2) {
199
-            throw new ApplicationLogicException('Passwords do not match!');
200
-        }
201
-
202
-        $passwordCredentialProvider = new PasswordCredentialProvider($user->getDatabase(), $this->getSiteConfiguration());
203
-        $passwordCredentialProvider->setCredential($user, 1, $pw);
204
-
205
-        SessionAlert::success('You may now log in!');
206
-        $this->redirect('login');
207
-    }
208
-
209
-    protected function isProtectedPage()
210
-    {
211
-        return false;
212
-    }
213
-
214
-    /**
215
-     * @param $user
216
-     */
217
-    private function cleanExistingTokens($user): void
218
-    {
219
-        // clean out existing reset tokens
220
-        $statement = $this->getDatabase()->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
221
-        $statement->execute([':user' => $user->getId()]);
222
-        $existing = $statement->fetchAll(PdoDatabase::FETCH_CLASS, Credential::class);
223
-
224
-        foreach ($existing as $c) {
225
-            $c->setDatabase($this->getDatabase());
226
-            $c->delete();
227
-        }
228
-    }
25
+	/**
26
+	 * Main function for this page, when no specific actions are called.
27
+	 *
28
+	 * This is the forgotten password reset form
29
+	 * @category Security-Critical
30
+	 */
31
+	protected function main()
32
+	{
33
+		if (WebRequest::wasPosted()) {
34
+			$this->validateCSRFToken();
35
+			$username = WebRequest::postString('username');
36
+			$email = WebRequest::postEmail('email');
37
+			$database = $this->getDatabase();
38
+
39
+			if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
40
+				throw new ApplicationLogicException("Both username and email address must be specified!");
41
+			}
42
+
43
+			$user = User::getByUsername($username, $database);
44
+			$this->sendResetMail($user, $email);
45
+
46
+			SessionAlert::success('<strong>Your password reset request has been completed.</strong> Please check your e-mail.');
47
+
48
+			$this->redirect('login');
49
+		}
50
+		else {
51
+			$this->assignCSRFToken();
52
+			$this->setTemplate('forgot-password/forgotpw.tpl');
53
+		}
54
+	}
55
+
56
+	/**
57
+	 * Sends a reset email if the user is authenticated
58
+	 *
59
+	 * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
60
+	 *                            check anyway within this method and silently skip if we don't have a user.
61
+	 * @param string       $email The provided email address
62
+	 */
63
+	private function sendResetMail($user, $email)
64
+	{
65
+		// If the user isn't found, or the email address is wrong, skip sending the details silently.
66
+		if (!$user instanceof User) {
67
+			return;
68
+		}
69
+
70
+		if (strtolower($user->getEmail()) === strtolower($email)) {
71
+			$clientIp = $this->getXffTrustProvider()
72
+				->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
73
+
74
+			$this->cleanExistingTokens($user);
75
+
76
+			$hash = Base32::encode(openssl_random_pseudo_bytes(30));
77
+
78
+			$encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
79
+
80
+			$cred = new Credential();
81
+			$cred->setDatabase($this->getDatabase());
82
+			$cred->setFactor(-1);
83
+			$cred->setUserId($user->getId());
84
+			$cred->setType('reset');
85
+			$cred->setData($encryptionHelper->encryptData($hash));
86
+			$cred->setVersion(0);
87
+			$cred->setDisabled(0);
88
+			$cred->setTimeout(new DateTimeImmutable('+ 1 hour'));
89
+			$cred->setPriority(9);
90
+			$cred->save();
91
+
92
+			$this->assign("user", $user);
93
+			$this->assign("hash", $hash);
94
+			$this->assign("remoteAddress", $clientIp);
95
+
96
+			$emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
97
+
98
+			$this->getEmailHelper()->sendMail($user->getEmail(), "WP:ACC password reset", $emailContent);
99
+		}
100
+	}
101
+
102
+	/**
103
+	 * Entry point for the reset action
104
+	 *
105
+	 * This is the reset password part of the form.
106
+	 * @category Security-Critical
107
+	 */
108
+	protected function reset()
109
+	{
110
+		$si = WebRequest::getString('si');
111
+		$id = WebRequest::getString('id');
112
+
113
+		if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
114
+			throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
115
+		}
116
+
117
+		$database = $this->getDatabase();
118
+		$user = $this->getResettingUser($id, $database, $si);
119
+
120
+		// Dual mode
121
+		if (WebRequest::wasPosted()) {
122
+			$this->validateCSRFToken();
123
+			try {
124
+				$this->doReset($user);
125
+				$this->cleanExistingTokens($user);
126
+			}
127
+			catch (ApplicationLogicException $ex) {
128
+				SessionAlert::error($ex->getMessage());
129
+				$this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
130
+
131
+				return;
132
+			}
133
+		}
134
+		else {
135
+			$this->assignCSRFToken();
136
+			$this->assign('user', $user);
137
+			$this->setTemplate('forgot-password/forgotpwreset.tpl');
138
+		}
139
+	}
140
+
141
+	/**
142
+	 * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
143
+	 *
144
+	 * @param integer     $id       The ID of the user to retrieve
145
+	 * @param PdoDatabase $database The database object to use
146
+	 * @param string      $si       The reset hash provided
147
+	 *
148
+	 * @return User
149
+	 * @throws ApplicationLogicException
150
+	 */
151
+	private function getResettingUser($id, $database, $si)
152
+	{
153
+		$user = User::getById($id, $database);
154
+
155
+		if ($user === false ||  $user->isCommunityUser()) {
156
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
157
+		}
158
+
159
+		$statement = $database->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
160
+		$statement->execute([':user' => $user->getId()]);
161
+
162
+		/** @var Credential $credential */
163
+		$credential = $statement->fetchObject(Credential::class);
164
+
165
+		$statement->closeCursor();
166
+
167
+		if ($credential === false) {
168
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
169
+		}
170
+
171
+		$credential->setDatabase($database);
172
+
173
+		$encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
174
+		if ($encryptionHelper->decryptData($credential->getData()) != $si) {
175
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
176
+		}
177
+
178
+		if ($credential->getTimeout() < new DateTimeImmutable()) {
179
+			$credential->delete();
180
+			throw new ApplicationLogicException("Password reset token expired. Please try again.");
181
+		}
182
+
183
+		return $user;
184
+	}
185
+
186
+	/**
187
+	 * Performs the setting of the new password
188
+	 *
189
+	 * @param User $user The user to set the password for
190
+	 *
191
+	 * @throws ApplicationLogicException
192
+	 */
193
+	private function doReset(User $user)
194
+	{
195
+		$pw = WebRequest::postString('pw');
196
+		$pw2 = WebRequest::postString('pw2');
197
+
198
+		if ($pw !== $pw2) {
199
+			throw new ApplicationLogicException('Passwords do not match!');
200
+		}
201
+
202
+		$passwordCredentialProvider = new PasswordCredentialProvider($user->getDatabase(), $this->getSiteConfiguration());
203
+		$passwordCredentialProvider->setCredential($user, 1, $pw);
204
+
205
+		SessionAlert::success('You may now log in!');
206
+		$this->redirect('login');
207
+	}
208
+
209
+	protected function isProtectedPage()
210
+	{
211
+		return false;
212
+	}
213
+
214
+	/**
215
+	 * @param $user
216
+	 */
217
+	private function cleanExistingTokens($user): void
218
+	{
219
+		// clean out existing reset tokens
220
+		$statement = $this->getDatabase()->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
221
+		$statement->execute([':user' => $user->getId()]);
222
+		$existing = $statement->fetchAll(PdoDatabase::FETCH_CLASS, Credential::class);
223
+
224
+		foreach ($existing as $c) {
225
+			$c->setDatabase($this->getDatabase());
226
+			$c->delete();
227
+		}
228
+	}
229 229
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -152,7 +152,7 @@
 block discarded – undo
152 152
     {
153 153
         $user = User::getById($id, $database);
154 154
 
155
-        if ($user === false ||  $user->isCommunityUser()) {
155
+        if ($user === false || $user->isCommunityUser()) {
156 156
             throw new ApplicationLogicException("Password reset failed. Please try again.");
157 157
         }
158 158
 
Please login to merge, or discard this patch.