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