361 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  * Licensed to Jasig under one or more contributor license
 | |
|  * agreements. See the NOTICE file distributed with this work for
 | |
|  * additional information regarding copyright ownership.
 | |
|  *
 | |
|  * Jasig licenses this file to you under the Apache License,
 | |
|  * Version 2.0 (the "License"); you may not use this file except in
 | |
|  * compliance with the License. You may obtain a copy of the License at:
 | |
|  *
 | |
|  * http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  *
 | |
|  * PHP Version 5
 | |
|  *
 | |
|  * @file     CAS/ProxiedService/Http/Abstract.php
 | |
|  * @category Authentication
 | |
|  * @package  PhpCAS
 | |
|  * @author   Adam Franco <afranco@middlebury.edu>
 | |
|  * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 | |
|  * @link     https://wiki.jasig.org/display/CASC/phpCAS
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * This class implements common methods for ProxiedService implementations included
 | |
|  * with phpCAS.
 | |
|  *
 | |
|  * @class    CAS_ProxiedService_Http_Abstract
 | |
|  * @category Authentication
 | |
|  * @package  PhpCAS
 | |
|  * @author   Adam Franco <afranco@middlebury.edu>
 | |
|  * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 | |
|  * @link     https://wiki.jasig.org/display/CASC/phpCAS
 | |
|  */
 | |
| abstract class CAS_ProxiedService_Http_Abstract extends
 | |
| CAS_ProxiedService_Abstract implements CAS_ProxiedService_Http
 | |
| {
 | |
|     /**
 | |
|      * The HTTP request mechanism talking to the target service.
 | |
|      *
 | |
|      * @var CAS_Request_RequestInterface $requestHandler
 | |
|      */
 | |
|     protected $requestHandler;
 | |
| 
 | |
|     /**
 | |
|      * The storage mechanism for cookies set by the target service.
 | |
|      *
 | |
|      * @var CAS_CookieJar $_cookieJar
 | |
|      */
 | |
|     private $_cookieJar;
 | |
| 
 | |
|     /**
 | |
|      * Constructor.
 | |
|      *
 | |
|      * @param CAS_Request_RequestInterface $requestHandler request handler object
 | |
|      * @param CAS_CookieJar                $cookieJar      cookieJar object
 | |
|      *
 | |
|      * @return void
 | |
|      */
 | |
|     public function __construct(CAS_Request_RequestInterface $requestHandler,
 | |
|         CAS_CookieJar $cookieJar
 | |
|     ) {
 | |
|         $this->requestHandler = $requestHandler;
 | |
|         $this->_cookieJar = $cookieJar;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * The target service url.
 | |
|      * @var string $_url;
 | |
|      */
 | |
|     private $_url;
 | |
| 
 | |
|     /**
 | |
|      * Answer a service identifier (URL) for whom we should fetch a proxy ticket.
 | |
|      *
 | |
|      * @return string
 | |
|      * @throws Exception If no service url is available.
 | |
|      */
 | |
|     public function getServiceUrl()
 | |
|     {
 | |
|         if (empty($this->_url)) {
 | |
|             throw new CAS_ProxiedService_Exception(
 | |
|                 'No URL set via ' . get_class($this) . '->setUrl($url).'
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return $this->_url;
 | |
|     }
 | |
| 
 | |
|     /*********************************************************
 | |
|      * Configure the Request
 | |
|      *********************************************************/
 | |
| 
 | |
|     /**
 | |
|      * Set the URL of the Request
 | |
|      *
 | |
|      * @param string $url url to set
 | |
|      *
 | |
|      * @return void
 | |
|      * @throws CAS_OutOfSequenceException If called after the Request has been sent.
 | |
|      */
 | |
|     public function setUrl($url)
 | |
|     {
 | |
|         if ($this->hasBeenSent()) {
 | |
|             throw new CAS_OutOfSequenceException(
 | |
|                 'Cannot set the URL, request already sent.'
 | |
|             );
 | |
|         }
 | |
|         if (!is_string($url)) {
 | |
|             throw new CAS_InvalidArgumentException('$url must be a string.');
 | |
|         }
 | |
| 
 | |
|         $this->_url = $url;
 | |
|     }
 | |
| 
 | |
|     /*********************************************************
 | |
|      * 2. Send the Request
 | |
|      *********************************************************/
 | |
| 
 | |
|     /**
 | |
|      * Perform the request.
 | |
|      *
 | |
|      * @return void
 | |
|      * @throws CAS_OutOfSequenceException If called multiple times.
 | |
|      * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
 | |
|      *		The code of the Exception will be one of:
 | |
|      *			PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
 | |
|      *			PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
 | |
|      *			PHPCAS_SERVICE_PT_FAILURE
 | |
|      * @throws CAS_ProxiedService_Exception If there is a failure sending the
 | |
|      * request to the target service.
 | |
|      */
 | |
|     public function send()
 | |
|     {
 | |
|         if ($this->hasBeenSent()) {
 | |
|             throw new CAS_OutOfSequenceException(
 | |
|                 'Cannot send, request already sent.'
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         phpCAS::traceBegin();
 | |
| 
 | |
|         // Get our proxy ticket and append it to our URL.
 | |
|         $this->initializeProxyTicket();
 | |
|         $url = $this->getServiceUrl();
 | |
|         if (strstr($url, '?') === false) {
 | |
|             $url = $url . '?ticket=' . $this->getProxyTicket();
 | |
|         } else {
 | |
|             $url = $url . '&ticket=' . $this->getProxyTicket();
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|             $this->makeRequest($url);
 | |
|         } catch (Exception $e) {
 | |
|             phpCAS::traceEnd();
 | |
|             throw $e;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Indicator of the number of requests (including redirects performed.
 | |
|      *
 | |
|      * @var int $_numRequests;
 | |
|      */
 | |
|     private $_numRequests = 0;
 | |
| 
 | |
|     /**
 | |
|      * The response headers.
 | |
|      *
 | |
|      * @var array $_responseHeaders;
 | |
|      */
 | |
|     private $_responseHeaders = array();
 | |
| 
 | |
|     /**
 | |
|      * The response status code.
 | |
|      *
 | |
|      * @var int $_responseStatusCode;
 | |
|      */
 | |
|     private $_responseStatusCode = '';
 | |
| 
 | |
|     /**
 | |
|      * The response headers.
 | |
|      *
 | |
|      * @var string $_responseBody;
 | |
|      */
 | |
|     private $_responseBody = '';
 | |
| 
 | |
|     /**
 | |
|      * Build and perform a request, following redirects
 | |
|      *
 | |
|      * @param string $url url for the request
 | |
|      *
 | |
|      * @return void
 | |
|      * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
 | |
|      *		The code of the Exception will be one of:
 | |
|      *			PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
 | |
|      *			PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
 | |
|      *			PHPCAS_SERVICE_PT_FAILURE
 | |
|      * @throws CAS_ProxiedService_Exception If there is a failure sending the
 | |
|      * request to the target service.
 | |
|      */
 | |
|     protected function makeRequest($url)
 | |
|     {
 | |
|         // Verify that we are not in a redirect loop
 | |
|         $this->_numRequests++;
 | |
|         if ($this->_numRequests > 4) {
 | |
|             $message = 'Exceeded the maximum number of redirects (3) in proxied service request.';
 | |
|             phpCAS::trace($message);
 | |
|             throw new CAS_ProxiedService_Exception($message);
 | |
|         }
 | |
| 
 | |
|         // Create a new request.
 | |
|         $request = clone $this->requestHandler;
 | |
|         $request->setUrl($url);
 | |
| 
 | |
|         // Add any cookies to the request.
 | |
|         $request->addCookies($this->_cookieJar->getCookies($url));
 | |
| 
 | |
|         // Add any other parts of the request needed by concrete classes
 | |
|         $this->populateRequest($request);
 | |
| 
 | |
|         // Perform the request.
 | |
|         phpCAS::trace('Performing proxied service request to \'' . $url . '\'');
 | |
|         if (!$request->send()) {
 | |
|             $message = 'Could not perform proxied service request to URL`'
 | |
|             . $url . '\'. ' . $request->getErrorMessage();
 | |
|             phpCAS::trace($message);
 | |
|             throw new CAS_ProxiedService_Exception($message);
 | |
|         }
 | |
| 
 | |
|         // Store any cookies from the response;
 | |
|         $this->_cookieJar->storeCookies($url, $request->getResponseHeaders());
 | |
| 
 | |
|         // Follow any redirects
 | |
|         if ($redirectUrl = $this->getRedirectUrl($request->getResponseHeaders())
 | |
|         ) {
 | |
|             phpCAS::trace('Found redirect:' . $redirectUrl);
 | |
|             $this->makeRequest($redirectUrl);
 | |
|         } else {
 | |
| 
 | |
|             $this->_responseHeaders = $request->getResponseHeaders();
 | |
|             $this->_responseBody = $request->getResponseBody();
 | |
|             $this->_responseStatusCode = $request->getResponseStatusCode();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Add any other parts of the request needed by concrete classes
 | |
|      *
 | |
|      * @param CAS_Request_RequestInterface $request request interface object
 | |
|      *
 | |
|      * @return void
 | |
|      */
 | |
|     abstract protected function populateRequest(
 | |
|         CAS_Request_RequestInterface $request
 | |
|     );
 | |
| 
 | |
|     /**
 | |
|      * Answer a redirect URL if a redirect header is found, otherwise null.
 | |
|      *
 | |
|      * @param array $responseHeaders response header to extract a redirect from
 | |
|      *
 | |
|      * @return string|null
 | |
|      */
 | |
|     protected function getRedirectUrl(array $responseHeaders)
 | |
|     {
 | |
|         // Check for the redirect after authentication
 | |
|         foreach ($responseHeaders as $header) {
 | |
|             if ( preg_match('/^(Location:|URI:)\s*([^\s]+.*)$/', $header, $matches)
 | |
|             ) {
 | |
|                 return trim(array_pop($matches));
 | |
|             }
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|     /*********************************************************
 | |
|      * 3. Access the response
 | |
|      *********************************************************/
 | |
| 
 | |
|     /**
 | |
|      * Answer true if our request has been sent yet.
 | |
|      *
 | |
|      * @return bool
 | |
|      */
 | |
|     protected function hasBeenSent()
 | |
|     {
 | |
|         return ($this->_numRequests > 0);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Answer the headers of the response.
 | |
|      *
 | |
|      * @return array An array of header strings.
 | |
|      * @throws CAS_OutOfSequenceException If called before the Request has been sent.
 | |
|      */
 | |
|     public function getResponseHeaders()
 | |
|     {
 | |
|         if (!$this->hasBeenSent()) {
 | |
|             throw new CAS_OutOfSequenceException(
 | |
|                 'Cannot access response, request not sent yet.'
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return $this->_responseHeaders;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Answer HTTP status code of the response
 | |
|      *
 | |
|      * @return int
 | |
|      * @throws CAS_OutOfSequenceException If called before the Request has been sent.
 | |
|      */
 | |
|     public function getResponseStatusCode()
 | |
|     {
 | |
|         if (!$this->hasBeenSent()) {
 | |
|             throw new CAS_OutOfSequenceException(
 | |
|                 'Cannot access response, request not sent yet.'
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return $this->_responseStatusCode;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Answer the body of response.
 | |
|      *
 | |
|      * @return string
 | |
|      * @throws CAS_OutOfSequenceException If called before the Request has been sent.
 | |
|      */
 | |
|     public function getResponseBody()
 | |
|     {
 | |
|         if (!$this->hasBeenSent()) {
 | |
|             throw new CAS_OutOfSequenceException(
 | |
|                 'Cannot access response, request not sent yet.'
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return $this->_responseBody;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Answer the cookies from the response. This may include cookies set during
 | |
|      * redirect responses.
 | |
|      *
 | |
|      * @return array An array containing cookies. E.g. array('name' => 'val');
 | |
|      */
 | |
|     public function getCookies()
 | |
|     {
 | |
|         return $this->_cookieJar->getCookies($this->getServiceUrl());
 | |
|     }
 | |
| 
 | |
| }
 | |
| ?>
 |