1<?php 2 3/** 4 * Licensed to Jasig under one or more contributor license 5 * agreements. See the NOTICE file distributed with this work for 6 * additional information regarding copyright ownership. 7 * 8 * Jasig licenses this file to you under the Apache License, 9 * Version 2.0 (the "License"); you may not use this file except in 10 * compliance with the License. You may obtain a copy of the License at: 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 * PHP Version 7 21 * 22 * @file CAS/ServiceBaseUrl/AllowedListDiscovery.php 23 * @category Authentication 24 * @package PhpCAS 25 * @author Henry Pan <git@phy25.com> 26 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 27 * @link https://wiki.jasig.org/display/CASC/phpCAS 28 */ 29 30 31/** 32 * Class that gets the service base URL of the PHP server by HTTP header 33 * discovery and allowlist check. This is used to generate service URL 34 * and PGT callback URL. 35 * 36 * @class CAS_ServiceBaseUrl_AllowedListDiscovery 37 * @category Authentication 38 * @package PhpCAS 39 * @author Henry Pan <git@phy25.com> 40 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 41 * @link https://wiki.jasig.org/display/CASC/phpCAS 42 */ 43 44class CAS_ServiceBaseUrl_AllowedListDiscovery 45extends CAS_ServiceBaseUrl_Base 46{ 47 private $_list = array(); 48 49 public function __construct($list) { 50 if (is_array($list)) { 51 if (count($list) === 0) { 52 throw new CAS_InvalidArgumentException('$list should not be empty'); 53 } 54 foreach ($list as $value) { 55 $this->allow($value); 56 } 57 } else { 58 throw new CAS_TypeMismatchException($list, '$list', 'array'); 59 } 60 } 61 62 /** 63 * Add a base URL to the allowed list. 64 * 65 * @param $url protocol, host name and port to add to the allowed list 66 * 67 * @return void 68 */ 69 public function allow($url) 70 { 71 $this->_list[] = $this->removeStandardPort($url); 72 } 73 74 /** 75 * Check if the server name is allowed by configuration. 76 * 77 * @param $name server name to check 78 * 79 * @return bool whether the allowed list contains the server name 80 */ 81 protected function isAllowed($name) 82 { 83 return in_array($name, $this->_list); 84 } 85 86 /** 87 * Discover the server name through HTTP headers. 88 * 89 * We read: 90 * - HTTP header X-Forwarded-Host 91 * - HTTP header X-Forwarded-Server and X-Forwarded-Port 92 * - HTTP header Host and SERVER_PORT 93 * - PHP SERVER_NAME (which can change based on the HTTP server used) 94 * 95 * The standard port will be omitted (80 for HTTP, 443 for HTTPS). 96 * 97 * @return string the discovered, unsanitized server protocol, hostname and port 98 */ 99 protected function discover() 100 { 101 $isHttps = $this->isHttps(); 102 $protocol = $isHttps ? 'https' : 'http'; 103 $protocol .= '://'; 104 if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) { 105 // explode the host list separated by comma and use the first host 106 $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); 107 // see rfc7239#5.3 and rfc7230#2.7.1: port is in HTTP_X_FORWARDED_HOST if non default 108 return $protocol . $hosts[0]; 109 } else if (!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])) { 110 $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER']; 111 } else { 112 if (empty($_SERVER['SERVER_NAME'])) { 113 $server_url = $_SERVER['HTTP_HOST']; 114 } else { 115 $server_url = $_SERVER['SERVER_NAME']; 116 } 117 } 118 if (!strpos($server_url, ':')) { 119 if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) { 120 $server_port = $_SERVER['SERVER_PORT']; 121 } else { 122 $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']); 123 $server_port = $ports[0]; 124 } 125 126 $server_url .= ':'; 127 $server_url .= $server_port; 128 } 129 return $protocol . $server_url; 130 } 131 132 /** 133 * Get PHP server base URL. 134 * 135 * @return string the server protocol, hostname and port 136 */ 137 public function get() 138 { 139 phpCAS::traceBegin(); 140 $result = $this->removeStandardPort($this->discover()); 141 phpCAS::trace("Discovered server base URL: " . $result); 142 if ($this->isAllowed($result)) { 143 phpCAS::trace("Server base URL is allowed"); 144 phpCAS::traceEnd(true); 145 } else { 146 $result = $this->_list[0]; 147 phpCAS::trace("Server base URL is not allowed, using default: " . $result); 148 phpCAS::traceEnd(false); 149 } 150 return $result; 151 } 152} 153