Server.php
8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
<?php
/**
* Implements the CAS server class.
*
* @version 1.2.0
* @since 1.0.0
*/
namespace Cassava\CAS;
use Cassava\Exception\GeneralException;
use Cassava\Exception\RequestException;
use Cassava\Options;
use Cassava\Plugin;
/**
* Class providing all public CAS methods.
*
* @since 1.0.0
*/
class Server {
/**
* RFC 1123 Date-Time Format
*/
const RFC1123_DATE_FORMAT = 'D, d M Y H:i:s T';
//
// CAS Server Methods
//
/**
* Get the list of routes supported by this CAS server and the callbacks each will invoke.
*
* - `/login`
* - `/logout`
* - `/proxy`
* - `/proxyValidate`
* - `/serviceValidate`
* - `/validate`
*
* @return array Array containing supported routes as keys and their callbacks as values.
*
* @uses \apply_filters()
*/
public function routes() {
$namespace = '\\Cassava\\CAS\\Controller\\';
$routes = array(
'login' => $namespace . 'LoginController',
'logout' => $namespace . 'LogoutController',
'validate' => $namespace . 'ValidateController',
'proxy' => $namespace . 'ProxyController',
'proxyValidate' => $namespace . 'ProxyValidateController',
'serviceValidate' => $namespace . 'ServiceValidateController',
'p3/proxyValidate' => $namespace . 'ProxyValidateController',
'p3/serviceValidate' => $namespace . 'ServiceValidateController',
);
/**
* Allows developers to override the default controller
* mapping, define additional endpoints and provide
* alternative implementations to the provided methods.
*
* @param array $cas_routes CAS endpoint to controller mapping.
*/
return \apply_filters( 'cas_server_routes', $routes );
}
/**
* Perform an HTTP redirect.
*
* If the 'allowed_services' contains at least one host, it will always perform a safe
* redirect.
*
* Calling Server::redirect() will _always_ end the request.
*
* @param string $location URI to redirect to.
* @param integer $status HTTP status code (default 302).
*
* @uses \wp_redirect()
* @uses \wp_safe_redirect()
*/
public function redirect( $location, $status = 302 ) {
$allowedServices = Options::get( 'allowed_services' );
if ( is_array( $allowedServices ) && count( $allowedServices ) > 0 ) {
\wp_safe_redirect( $location, $status );
}
\wp_redirect( $location, $status );
exit;
}
/**
* Handle a CAS server request for a specific URI.
*
* This method will attempt to set the following HTTP headers to prevent browser caching:
*
* - `Pragma: no-cache`
* - `Cache-Control: no-store`
* - `Expires: <time of request>`
*
* @param string $path CAS request URI.
*
* @return string Request response.
*
* @throws \Cassava\Exception\GeneralException
*
* @global $_SERVER
*
* @uses \apply_filters()
* @uses \do_action()
*/
public function handleRequest( $path ) {
if ( ! defined( 'CAS_REQUEST' ) ) {
define( 'CAS_REQUEST', true );
}
$this->setResponseHeader( 'Pragma' , 'no-cache' );
$this->setResponseHeader( 'Cache-Control', 'no-store' );
$this->setResponseHeader( 'Expires' , gmdate( static::RFC1123_DATE_FORMAT ) );
/**
* Fires before processing the CAS request.
*
* @param string $path Requested URI path.
* @return string Filtered requested URI path.
*/
\do_action( 'cas_server_before_request', $path );
if (empty( $path )) {
$path = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : '/';
}
try {
$output = $this->dispatch( $path );
}
catch ( GeneralException $exception ) {
$this->setResponseContentType( 'text/xml' );
$response = new Response\BaseResponse();
$response->setError( $exception->getErrorInstance() );
$output = $response->prepare();
}
/**
* Fires after the CAS request is processed.
*
* @param string $path Requested URI path.
*/
\do_action( 'cas_server_after_request', $path );
/**
* Lets developers change the CAS server response string.
*
* @param string $output Response output string.
* @param string $path Requested URI path.
*/
$output = \apply_filters( 'cas_server_response', $output, $path );
return $output;
}
/**
* Dispatch the request for processing by the relevant callback as determined by the routes
* list returned by `Server::routes()`.
*
* @param string $path Requested URI path.
* @return mixed Service response string or WordPress error.
*
* @throws \Cassava\Exception\GeneralException
* @throws \Cassava\Exception\RequestException
*
* @global $_GET
*
* @uses \apply_filters()
* @uses \is_ssl()
* @uses \is_wp_error()
*/
protected function dispatch( $path ) {
if ( ! \is_ssl() ) {
//throw new GeneralException(
// __( 'The CAS server requires SSL.', 'wp-cas-server' ) );
}
/**
* Allows developers to disable CAS.
*
* @param boolean $cas_enabled Whether the server should respond to single sign-on requests.
*/
$enabled = apply_filters( 'cas_enabled', true );
if ( ! $enabled ) {
throw new GeneralException(
__( 'The CAS server is disabled.', 'wp-cas-server' ) );
}
$routes = $this->routes();
foreach ( $routes as $route => $controller ) {
$match = preg_match( '@^' . preg_quote( $route ) . '/?$@', $path );
if ( ! $match ) {
continue;
}
if ( ! class_exists( $controller ) ) {
throw new GeneralException(
__( 'The controller for the route is invalid.', 'wp-cas-server' ) );
}
$args = $_GET;
/**
* Filters the controller arguments to be dispatched for the request.
*
* Plugin developers may return a WP_Error object via the
* `cas_server_dispatch_args` filter to abort the request. Avoid throwing
* a `\Cassava\Exception\GeneralException` exception here because that
* would interrupt the filter controller chain.
*
* @param array $args Arguments to pass the controller.
* @param mixed $controller Controller class.
* @param string $path Requested URI path.
*
* @return mixed Arguments to pass the controller, or `WP_Error`.
*/
$args = \apply_filters( 'cas_server_dispatch_args', $args, $controller, $path );
if ( \is_wp_error( $args ) ) {
throw GeneralException::fromError( $args );
}
$controllerInstance = new $controller( $this );
return $controllerInstance->handleRequest( $args );
}
throw new RequestException(
__( 'The server does not support the method requested.', 'wp-cas-server' ) );
}
/**
* Wraps calls to session_start() to prevent 'headers already sent' errors.
*/
public function sessionStart() {
$sessionExists = function_exists( 'session_status' ) && session_status() === PHP_SESSION_NONE;
if ( headers_sent() || $sessionExists || strlen( session_id() ) ) {
return;
}
session_start();
}
/**
* Wraps calls to session destruction functions.
*/
public function sessionDestroy() {
wp_logout();
wp_set_current_user( false );
$sessionExists = function_exists( 'session_status' ) && session_status() === PHP_SESSION_NONE;
if ( headers_sent() || ! $sessionExists || ! strlen( session_id() ) ) {
return;
}
session_unset();
session_destroy();
}
/**
* Sets an HTTP response header.
*
* @param string $key Header key.
* @param string $value Header value.
*/
protected function setResponseHeader( $key, $value ) {
if (headers_sent()) return;
header( sprintf( '%s: %s', $key, $value ) );
}
/**
* Set response headers for a CAS version response.
*/
public function setResponseContentType( $type ) {
$this->setResponseHeader( 'Content-Type', $type . '; charset=' . get_bloginfo( 'charset' ) );
}
/**
* Redirects the user to either the standard WordPress authentication page or a custom one
* at a URI returned by the `cas_server_custom_auth_uri` filter.
*
* @param array $args HTTP request parameters received by `/login`.
*
* @uses apply_filters()
* @uses auth_redirect()
*/
public function authRedirect ( $args = array() ) {
/**
* Allows developers to redirect the user to a custom login form.
*
* @param string $custom_login_url URI for the custom login page.
* @param array $args Login request parameters.
*/
$custom_login_url = apply_filters( 'cas_server_custom_auth_uri', false, $args );
if ( $custom_login_url ) {
$this->redirect( $custom_login_url );
}
auth_redirect();
exit;
}
}