Passed
Push — master ( f999f4...f378ea )
by
unknown
02:33
created
src/Extension/CwpControllerExtension.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
         // First, see if we can get a member to act on, either from a changepassword token or the session
122 122
         if (isset($_REQUEST['m']) && isset($_REQUEST['t'])) {
123 123
             /** @var Member $member */
124
-            $member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first();
124
+            $member = Member::get()->filter('ID', (int)$_REQUEST['m'])->first();
125 125
 
126 126
             if (!$member->validateAutoLoginToken($_REQUEST['t'])) {
127 127
                 $member = null;
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
         }
136 136
 
137 137
         // Then, if they have the right permissions, check the allowed URLs
138
-        $existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function () use ($member) {
138
+        $existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function() use ($member) {
139 139
             return Permission::checkMember($member, 'ACCESS_UAT_SERVER');
140 140
         });
141 141
 
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 
155 155
         // Finally if they weren't allowed to bypass Basic Auth, trigger it
156 156
         if (!$allowWithoutAuth) {
157
-            $this->callWithSubsitesDisabled(function () use ($request) {
157
+            $this->callWithSubsitesDisabled(function() use ($request) {
158 158
                 BasicAuth::requireLogin(
159 159
                     $request,
160 160
                     _t(__CLASS__ . '.LoginPrompt', "Please log in with your CMS credentials"),
Please login to merge, or discard this patch.
Indentation   +207 added lines, -207 removed lines patch added patch discarded remove patch
@@ -19,211 +19,211 @@
 block discarded – undo
19 19
 class CwpControllerExtension extends Extension implements PermissionProvider
20 20
 {
21 21
 
22
-    /**
23
-     * Enables SSL redirections - disabling not recommended as it will prevent forcing SSL on admin panel.
24
-     *
25
-     * @config
26
-     * @var bool
27
-     */
28
-    private static $ssl_redirection_enabled = true;
29
-
30
-    /**
31
-     * Specify a domain to redirect the vulnerable areas to.
32
-     *
33
-     * If left as null, live instance will set this to <instance-id>.cwp.govt.nz via CWP_SECURE_DOMAIN in _config.php.
34
-     * This allows us to automatically protect vulnerable areas on live even if the frontend cert is not installed.
35
-     *
36
-     * Set to false to redirect to https protocol on current domain (e.g. if you have frontend cert).
37
-     *
38
-     * Set to a domain string (e.g. 'example.com') to force that domain.
39
-     *
40
-     * @config
41
-     * @var string
42
-     */
43
-    private static $ssl_redirection_force_domain = null;
44
-
45
-    /**
46
-     * Enables the BasicAuth protection on all test environments. Disable with caution - it will open up
47
-     * all your UAT and test environments to the world.
48
-     *
49
-     * @config
50
-     * @var bool
51
-     */
52
-    private static $test_basicauth_enabled = true;
53
-
54
-    /**
55
-     * Enables the BasicAuth protection on all live environments.
56
-     * Useful for securing sites prior to public launch.
57
-     *
58
-     * @config
59
-     * @var bool
60
-     */
61
-    private static $live_basicauth_enabled = false;
62
-
63
-    /**
64
-     * This executes the passed callback with subsite filter disabled,
65
-     * then enabled the filter again before returning the callback result
66
-     * (or throwing the exception the callback raised)
67
-     *
68
-     * @param  callback  $callback - The callback to execute
69
-     * @return mixed     The result of the callback
70
-     * @throws Exception Any exception the callback raised
71
-     */
72
-    protected function callWithSubsitesDisabled($callback)
73
-    {
74
-        $rv = null;
75
-
76
-        try {
77
-            if (class_exists(Subsite::class)) {
78
-                Subsite::disable_subsite_filter(true);
79
-            }
80
-
81
-            $rv = call_user_func($callback);
82
-        } catch (Exception $e) {
83
-            if (class_exists(Subsite::class)) {
84
-                Subsite::disable_subsite_filter(false);
85
-            }
86
-
87
-            throw $e;
88
-        }
89
-
90
-        if (class_exists(Subsite::class)) {
91
-            Subsite::disable_subsite_filter(false);
92
-        }
93
-
94
-        return $rv;
95
-    }
96
-
97
-    /**
98
-     * Trigger Basic Auth protection, except when there's a reason to bypass it
99
-     *  - The source IP address is in the comma-seperated string in the constant CWP_IP_BYPASS_BASICAUTH
100
-     *    (so Pingdom, etc, can access the site)
101
-     *  - There is an identifiable member, that member has the ACCESS_UAT_SERVER permission, and they're trying
102
-     *    to access a white-list of URLs (so people following a reset password link can reset their password)
103
-     */
104
-    protected function triggerBasicAuthProtection()
105
-    {
106
-        $allowWithoutAuth = false;
107
-
108
-        // Allow whitelisting IPs for bypassing the basic auth.
109
-        if (Environment::getEnv('CWP_IP_BYPASS_BASICAUTH')) {
110
-            $remote = $_SERVER['REMOTE_ADDR'];
111
-            $bypass = explode(',', Environment::getEnv('CWP_IP_BYPASS_BASICAUTH'));
112
-
113
-            if (in_array($remote, $bypass)) {
114
-                $allowWithoutAuth = true;
115
-            }
116
-        }
117
-
118
-        /** @var HTTPRequest|null $request */
119
-        $request = $this->getRequest();
120
-
121
-        // First, see if we can get a member to act on, either from a changepassword token or the session
122
-        if (isset($_REQUEST['m']) && isset($_REQUEST['t'])) {
123
-            /** @var Member $member */
124
-            $member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first();
125
-
126
-            if (!$member->validateAutoLoginToken($_REQUEST['t'])) {
127
-                $member = null;
128
-            }
129
-        } elseif ($request && $request->getSession()->get('AutoLoginHash')) {
130
-            $member = Member::member_from_autologinhash(
131
-                $request->getSession()->get('AutoLoginHash')
132
-            );
133
-        } else {
134
-            $member = Security::getCurrentUser();
135
-        }
136
-
137
-        // Then, if they have the right permissions, check the allowed URLs
138
-        $existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function () use ($member) {
139
-            return Permission::checkMember($member, 'ACCESS_UAT_SERVER');
140
-        });
141
-
142
-        if ($existingMemberCanAccessUAT) {
143
-            $allowed = array(
144
-                '/^Security\/changepassword/',
145
-                '/^Security\/ChangePasswordForm/'
146
-            );
147
-
148
-            $relativeURL = Director::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
149
-
150
-            foreach ($allowed as $pattern) {
151
-                $allowWithoutAuth = $allowWithoutAuth || preg_match($pattern, $relativeURL);
152
-            }
153
-        }
154
-
155
-        // Finally if they weren't allowed to bypass Basic Auth, trigger it
156
-        if (!$allowWithoutAuth) {
157
-            $this->callWithSubsitesDisabled(function () use ($request) {
158
-                BasicAuth::requireLogin(
159
-                    $request,
160
-                    _t(__CLASS__ . '.LoginPrompt', "Please log in with your CMS credentials"),
161
-                    'ACCESS_UAT_SERVER',
162
-                    true
163
-                );
164
-            });
165
-        }
166
-    }
167
-
168
-    /**
169
-     * Get the current request, either from an Injector service or from the current controller
170
-     *
171
-     * @return HTTPRequest|null
172
-     */
173
-    protected function getRequest()
174
-    {
175
-        if (Injector::inst()->has(HTTPRequest::class)) {
176
-            return Injector::inst()->get(HTTPRequest::class);
177
-        }
178
-        return $this->owner->getRequest();
179
-    }
180
-
181
-    /**
182
-     * @return void
183
-     */
184
-    public function onBeforeInit()
185
-    {
186
-        // Grab global injectable service to allow testing.
187
-        $director = Injector::inst()->get(Director::class);
188
-
189
-        if (Config::inst()->get(__CLASS__, 'ssl_redirection_enabled')) {
190
-            // redirect some vulnerable areas to the secure domain
191
-            if (!$director::is_https()) {
192
-                $forceDomain = Config::inst()->get(__CLASS__, 'ssl_redirection_force_domain');
193
-
194
-                if ($forceDomain) {
195
-                    $director::forceSSL(['/^Security/', '/^api/'], $forceDomain);
196
-                } else {
197
-                    $director::forceSSL(['/^Security/', '/^api/']);
198
-                }
199
-            }
200
-        }
201
-
202
-        if (Config::inst()->get(__CLASS__, 'test_basicauth_enabled')) {
203
-            // Turn on Basic Auth in testing mode
204
-            if ($director::isTest()) {
205
-                $this->triggerBasicAuthProtection();
206
-            }
207
-        }
208
-
209
-        if (Config::inst()->get(__CLASS__, 'live_basicauth_enabled')) {
210
-            // Turn on Basic Auth in live mode
211
-            if ($director::isLive()) {
212
-                $this->triggerBasicAuthProtection();
213
-            }
214
-        }
215
-    }
216
-
217
-    /**
218
-     * @return array
219
-     */
220
-    public function providePermissions()
221
-    {
222
-        return [
223
-            'ACCESS_UAT_SERVER' => _t(
224
-                __CLASS__ . '.UatServerPermission',
225
-                'Allow users to use their accounts to access the UAT server'
226
-            )
227
-        ];
228
-    }
22
+	/**
23
+	 * Enables SSL redirections - disabling not recommended as it will prevent forcing SSL on admin panel.
24
+	 *
25
+	 * @config
26
+	 * @var bool
27
+	 */
28
+	private static $ssl_redirection_enabled = true;
29
+
30
+	/**
31
+	 * Specify a domain to redirect the vulnerable areas to.
32
+	 *
33
+	 * If left as null, live instance will set this to <instance-id>.cwp.govt.nz via CWP_SECURE_DOMAIN in _config.php.
34
+	 * This allows us to automatically protect vulnerable areas on live even if the frontend cert is not installed.
35
+	 *
36
+	 * Set to false to redirect to https protocol on current domain (e.g. if you have frontend cert).
37
+	 *
38
+	 * Set to a domain string (e.g. 'example.com') to force that domain.
39
+	 *
40
+	 * @config
41
+	 * @var string
42
+	 */
43
+	private static $ssl_redirection_force_domain = null;
44
+
45
+	/**
46
+	 * Enables the BasicAuth protection on all test environments. Disable with caution - it will open up
47
+	 * all your UAT and test environments to the world.
48
+	 *
49
+	 * @config
50
+	 * @var bool
51
+	 */
52
+	private static $test_basicauth_enabled = true;
53
+
54
+	/**
55
+	 * Enables the BasicAuth protection on all live environments.
56
+	 * Useful for securing sites prior to public launch.
57
+	 *
58
+	 * @config
59
+	 * @var bool
60
+	 */
61
+	private static $live_basicauth_enabled = false;
62
+
63
+	/**
64
+	 * This executes the passed callback with subsite filter disabled,
65
+	 * then enabled the filter again before returning the callback result
66
+	 * (or throwing the exception the callback raised)
67
+	 *
68
+	 * @param  callback  $callback - The callback to execute
69
+	 * @return mixed     The result of the callback
70
+	 * @throws Exception Any exception the callback raised
71
+	 */
72
+	protected function callWithSubsitesDisabled($callback)
73
+	{
74
+		$rv = null;
75
+
76
+		try {
77
+			if (class_exists(Subsite::class)) {
78
+				Subsite::disable_subsite_filter(true);
79
+			}
80
+
81
+			$rv = call_user_func($callback);
82
+		} catch (Exception $e) {
83
+			if (class_exists(Subsite::class)) {
84
+				Subsite::disable_subsite_filter(false);
85
+			}
86
+
87
+			throw $e;
88
+		}
89
+
90
+		if (class_exists(Subsite::class)) {
91
+			Subsite::disable_subsite_filter(false);
92
+		}
93
+
94
+		return $rv;
95
+	}
96
+
97
+	/**
98
+	 * Trigger Basic Auth protection, except when there's a reason to bypass it
99
+	 *  - The source IP address is in the comma-seperated string in the constant CWP_IP_BYPASS_BASICAUTH
100
+	 *    (so Pingdom, etc, can access the site)
101
+	 *  - There is an identifiable member, that member has the ACCESS_UAT_SERVER permission, and they're trying
102
+	 *    to access a white-list of URLs (so people following a reset password link can reset their password)
103
+	 */
104
+	protected function triggerBasicAuthProtection()
105
+	{
106
+		$allowWithoutAuth = false;
107
+
108
+		// Allow whitelisting IPs for bypassing the basic auth.
109
+		if (Environment::getEnv('CWP_IP_BYPASS_BASICAUTH')) {
110
+			$remote = $_SERVER['REMOTE_ADDR'];
111
+			$bypass = explode(',', Environment::getEnv('CWP_IP_BYPASS_BASICAUTH'));
112
+
113
+			if (in_array($remote, $bypass)) {
114
+				$allowWithoutAuth = true;
115
+			}
116
+		}
117
+
118
+		/** @var HTTPRequest|null $request */
119
+		$request = $this->getRequest();
120
+
121
+		// First, see if we can get a member to act on, either from a changepassword token or the session
122
+		if (isset($_REQUEST['m']) && isset($_REQUEST['t'])) {
123
+			/** @var Member $member */
124
+			$member = Member::get()->filter('ID', (int) $_REQUEST['m'])->first();
125
+
126
+			if (!$member->validateAutoLoginToken($_REQUEST['t'])) {
127
+				$member = null;
128
+			}
129
+		} elseif ($request && $request->getSession()->get('AutoLoginHash')) {
130
+			$member = Member::member_from_autologinhash(
131
+				$request->getSession()->get('AutoLoginHash')
132
+			);
133
+		} else {
134
+			$member = Security::getCurrentUser();
135
+		}
136
+
137
+		// Then, if they have the right permissions, check the allowed URLs
138
+		$existingMemberCanAccessUAT = $member && $this->callWithSubsitesDisabled(function () use ($member) {
139
+			return Permission::checkMember($member, 'ACCESS_UAT_SERVER');
140
+		});
141
+
142
+		if ($existingMemberCanAccessUAT) {
143
+			$allowed = array(
144
+				'/^Security\/changepassword/',
145
+				'/^Security\/ChangePasswordForm/'
146
+			);
147
+
148
+			$relativeURL = Director::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
149
+
150
+			foreach ($allowed as $pattern) {
151
+				$allowWithoutAuth = $allowWithoutAuth || preg_match($pattern, $relativeURL);
152
+			}
153
+		}
154
+
155
+		// Finally if they weren't allowed to bypass Basic Auth, trigger it
156
+		if (!$allowWithoutAuth) {
157
+			$this->callWithSubsitesDisabled(function () use ($request) {
158
+				BasicAuth::requireLogin(
159
+					$request,
160
+					_t(__CLASS__ . '.LoginPrompt', "Please log in with your CMS credentials"),
161
+					'ACCESS_UAT_SERVER',
162
+					true
163
+				);
164
+			});
165
+		}
166
+	}
167
+
168
+	/**
169
+	 * Get the current request, either from an Injector service or from the current controller
170
+	 *
171
+	 * @return HTTPRequest|null
172
+	 */
173
+	protected function getRequest()
174
+	{
175
+		if (Injector::inst()->has(HTTPRequest::class)) {
176
+			return Injector::inst()->get(HTTPRequest::class);
177
+		}
178
+		return $this->owner->getRequest();
179
+	}
180
+
181
+	/**
182
+	 * @return void
183
+	 */
184
+	public function onBeforeInit()
185
+	{
186
+		// Grab global injectable service to allow testing.
187
+		$director = Injector::inst()->get(Director::class);
188
+
189
+		if (Config::inst()->get(__CLASS__, 'ssl_redirection_enabled')) {
190
+			// redirect some vulnerable areas to the secure domain
191
+			if (!$director::is_https()) {
192
+				$forceDomain = Config::inst()->get(__CLASS__, 'ssl_redirection_force_domain');
193
+
194
+				if ($forceDomain) {
195
+					$director::forceSSL(['/^Security/', '/^api/'], $forceDomain);
196
+				} else {
197
+					$director::forceSSL(['/^Security/', '/^api/']);
198
+				}
199
+			}
200
+		}
201
+
202
+		if (Config::inst()->get(__CLASS__, 'test_basicauth_enabled')) {
203
+			// Turn on Basic Auth in testing mode
204
+			if ($director::isTest()) {
205
+				$this->triggerBasicAuthProtection();
206
+			}
207
+		}
208
+
209
+		if (Config::inst()->get(__CLASS__, 'live_basicauth_enabled')) {
210
+			// Turn on Basic Auth in live mode
211
+			if ($director::isLive()) {
212
+				$this->triggerBasicAuthProtection();
213
+			}
214
+		}
215
+	}
216
+
217
+	/**
218
+	 * @return array
219
+	 */
220
+	public function providePermissions()
221
+	{
222
+		return [
223
+			'ACCESS_UAT_SERVER' => _t(
224
+				__CLASS__ . '.UatServerPermission',
225
+				'Allow users to use their accounts to access the UAT server'
226
+			)
227
+		];
228
+	}
229 229
 }
Please login to merge, or discard this patch.