Completed
Pull Request — master (#22)
by Robbie
02:15
created
src/Extension/CwpControllerExtension.php 2 patches
Indentation   +208 added lines, -208 removed lines patch added patch discarded remove patch
@@ -19,212 +19,212 @@
 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 = [
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
-        } elseif ($this->owner->getRequest() instanceof HTTPRequest) {
178
-            return $this->owner->getRequest();
179
-        }
180
-    }
181
-
182
-    /**
183
-     * @return void
184
-     */
185
-    public function onBeforeInit()
186
-    {
187
-        // Grab global injectable service to allow testing.
188
-        $director = Injector::inst()->get(Director::class);
189
-
190
-        if (Config::inst()->get(__CLASS__, 'ssl_redirection_enabled')) {
191
-            // redirect some vulnerable areas to the secure domain
192
-            if (!$director::is_https()) {
193
-                $forceDomain = Config::inst()->get(__CLASS__, 'ssl_redirection_force_domain');
194
-
195
-                if ($forceDomain) {
196
-                    $director::forceSSL(['/^Security/', '/^api/'], $forceDomain);
197
-                } else {
198
-                    $director::forceSSL(['/^Security/', '/^api/']);
199
-                }
200
-            }
201
-        }
202
-
203
-        if (Config::inst()->get(__CLASS__, 'test_basicauth_enabled')) {
204
-            // Turn on Basic Auth in testing mode
205
-            if ($director::isTest()) {
206
-                $this->triggerBasicAuthProtection();
207
-            }
208
-        }
209
-
210
-        if (Config::inst()->get(__CLASS__, 'live_basicauth_enabled')) {
211
-            // Turn on Basic Auth in live mode
212
-            if ($director::isLive()) {
213
-                $this->triggerBasicAuthProtection();
214
-            }
215
-        }
216
-    }
217
-
218
-    /**
219
-     * @return array
220
-     */
221
-    public function providePermissions()
222
-    {
223
-        return [
224
-            'ACCESS_UAT_SERVER' => _t(
225
-                __CLASS__ . '.UatServerPermission',
226
-                'Allow users to use their accounts to access the UAT server'
227
-            )
228
-        ];
229
-    }
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 = [
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
+		} elseif ($this->owner->getRequest() instanceof HTTPRequest) {
178
+			return $this->owner->getRequest();
179
+		}
180
+	}
181
+
182
+	/**
183
+	 * @return void
184
+	 */
185
+	public function onBeforeInit()
186
+	{
187
+		// Grab global injectable service to allow testing.
188
+		$director = Injector::inst()->get(Director::class);
189
+
190
+		if (Config::inst()->get(__CLASS__, 'ssl_redirection_enabled')) {
191
+			// redirect some vulnerable areas to the secure domain
192
+			if (!$director::is_https()) {
193
+				$forceDomain = Config::inst()->get(__CLASS__, 'ssl_redirection_force_domain');
194
+
195
+				if ($forceDomain) {
196
+					$director::forceSSL(['/^Security/', '/^api/'], $forceDomain);
197
+				} else {
198
+					$director::forceSSL(['/^Security/', '/^api/']);
199
+				}
200
+			}
201
+		}
202
+
203
+		if (Config::inst()->get(__CLASS__, 'test_basicauth_enabled')) {
204
+			// Turn on Basic Auth in testing mode
205
+			if ($director::isTest()) {
206
+				$this->triggerBasicAuthProtection();
207
+			}
208
+		}
209
+
210
+		if (Config::inst()->get(__CLASS__, 'live_basicauth_enabled')) {
211
+			// Turn on Basic Auth in live mode
212
+			if ($director::isLive()) {
213
+				$this->triggerBasicAuthProtection();
214
+			}
215
+		}
216
+	}
217
+
218
+	/**
219
+	 * @return array
220
+	 */
221
+	public function providePermissions()
222
+	{
223
+		return [
224
+			'ACCESS_UAT_SERVER' => _t(
225
+				__CLASS__ . '.UatServerPermission',
226
+				'Allow users to use their accounts to access the UAT server'
227
+			)
228
+		];
229
+	}
230 230
 }
Please login to merge, or discard this patch.
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.