mirror of https://github.com/jacekkow/uphpCAS

Jacek Kowalski
2015-09-03 501c90e5965ec99cd6de5191652707ae28fc3a75
commit | author | age
64d82d 1 <?php
JK 2 // Thrown when internal error occurs
3 class JasigException extends Exception {}
2f13b1 4 // Thrown when CAS server returns authentication error
64d82d 5 class JasigAuthException extends JasigException {}
JK 6
7 class JasigUser {
8     public $user;
9     public $attributes = array();
10 }
11
12 class uphpCAS {
13     const VERSION = '1.0';
14     protected $serverUrl = '';
15     protected $serviceUrl;
710793 16     protected $sessionName = 'uphpCAS-user';
64d82d 17     
710793 18     function __construct($serverUrl = NULL, $serviceUrl = NULL, $sessionName = NULL) {
64d82d 19         if($serverUrl != NULL) {
JK 20             $this->serverUrl = rtrim($serverUrl, '/');
21         }
22         
23         if($serviceUrl != NULL) {
24             $this->serviceUrl = $serviceUrl;
25         } else {
8891c7 26             $this->serviceUrl = $this->getCurrentUrl();
64d82d 27         }
710793 28         
JK 29         if($sessionName) {
30             $this->sessionName = $sessionName;
31         }
64d82d 32     }
JK 33     
8891c7 34     public function getCurrentUrl() {
JK 35         $url = 'http://';
36         $port = 0;
37         if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
38             $url = 'https://';
39             if(isset($_SERVER['SERVER_PORT'])
40                     && $_SERVER['SERVER_PORT'] != '443') {
41                 $port = $_SERVER['SERVER_PORT'];
42             }
43         } elseif(isset($_SERVER['SERVER_PORT'])
44                 && $_SERVER['SERVER_PORT'] != '80') {
45             $port = $_SERVER['SERVER_PORT'];
46         }
47         
48         $url .= $_SERVER['SERVER_NAME'];
49         
50         if($port != 0) {
51             $url .= ':'.$port;
52         }
53         $url .= $_SERVER['REQUEST_URI'];
54     }
55     
deadb1 56     public function getServerUrl() {
bae7f7 57         return $this->serverUrl;
JK 58     }
64d82d 59     public function setServerUrl($serverUrl) {
JK 60         $this->serverUrl = $serverUrl;
61     }
62     
bae7f7 63     public function getServiceUrl() {
JK 64         return $this->serviceUrl;
65     }
64d82d 66     public function setServiceUrl($serviceUrl) {
JK 67         $this->serviceUrl = $serviceUrl;
68     }
69     
165b17 70     public function getSessionName() {
710793 71         return $this->sessionName;
JK 72     }
73     public function setSessionName($sessionName) {
74         $this->sessionName = $sessionName;
75     }
76     
64d82d 77     public function loginUrl() {
2092d3 78         return $this->serverUrl.'/login?method=POST&service='.urlencode($this->serviceUrl);
64d82d 79     }
JK 80     
f124d4 81     public function logoutUrl($returnUrl = NULL) {
JK 82         return $this->serverUrl.'/logout'.($returnUrl ? '?service='.urlencode($returnUrl) : '');
64d82d 83     }
JK 84     
cc5e29 85     public function logout($returnUrl = NULL) {
64d82d 86         session_start();
cc5e29 87         if($this->isAuthenticated()) {
710793 88             unset($_SESSION[$this->sessionName]);
cc5e29 89             header('Location: '.$this->logoutUrl($returnUrl));
JK 90             die();
91         } elseif($returnUrl) {
92             header('Location: '.$returnUrl);
93             die();
64d82d 94         }
JK 95     }
96     
fc49b8 97     public function isAuthenticated() {
710793 98         return isset($_SESSION[$this->sessionName]);
fc49b8 99     }
JK 100     
64d82d 101     public function authenticate() {
JK 102         session_start();
fc49b8 103         if($this->isAuthenticated()) {
710793 104             return $_SESSION[$this->sessionName];
2092d3 105         } elseif(isset($_REQUEST['ticket'])) {
JK 106             $user = $this->verifyTicket($_REQUEST['ticket']);
710793 107             $_SESSION[$this->sessionName] = $user;
64d82d 108             return $user;
JK 109         } else {
110             header('Location: '.$this->loginUrl());
111             die();
112         }
113     }
114     
501c90 115     protected function findCaFile() {
JK 116         $cafiles = array(
117             '/etc/ssl/certs/ca-certificates.crt',
118             '/etc/ssl/certs/ca-bundle.crt',
119             '/etc/pki/tls/certs/ca-bundle.crt',
120         );
121         
122         $cafile = NULL;
123         foreach($cafiles as $file) {
124             if(is_file($file)) {
125                 $cafile = $file;
126                 break;
127             }
128         }
129         
130         return $cafile;
131     }
132     
64d82d 133     public function verifyTicket($ticket) {
JK 134         $context = array(
135             'http' => array(
136                 'method' => 'GET',
137                 'user_agent' => 'uphpCAS/'.self::VERSION,
138                 'max_redirects' => 3,
139             ),
140             'ssl' => array(
141                 'verify_peer' => TRUE,
90ae5b 142                 'verify_peer_name' => TRUE,
64d82d 143                 'verify_depth' => 5,
90ae5b 144                 'allow_self_signed' => FALSE,
JK 145                 'disable_compression' => TRUE,
64d82d 146             ),
JK 147         );
148         
d35cf4 149         if(version_compare(PHP_VERSION, '5.6', '<')) {
JK 150             $url = parse_url($this->serverUrl);
501c90 151             $context['ssl']['cafile'] = $this->findCaFile();
d35cf4 152             $context['ssl']['ciphers'] = 'ECDH:DH:AES:CAMELLIA:!SSLv2:!aNULL'
JK 153                     .':!eNULL:!EXPORT:!DES:!3DES:!MD5:!RC4:!ADH:!PSK:!SRP';
154             $context['ssl']['CN_match'] = $url['host'];
155         }
156         
90ae5b 157         $data = file_get_contents($this->serverUrl
JK 158                     .'/serviceValidate?service='.urlencode($this->serviceUrl)
159                     .'&ticket='.urlencode($ticket),
64d82d 160                 FALSE, stream_context_create($context));
JK 161         if($data === FALSE) {
162             throw new JasigException('Authentication error: CAS server is unavailable');
163         }
164         
165         $xmlEntityLoader = libxml_disable_entity_loader(TRUE);
166         $xmlInternalErrors = libxml_use_internal_errors(TRUE);
167         try {
168             $xml = new DOMDocument();
169             $xml->loadXML($data);
170             
171             foreach(libxml_get_errors() as $error) {
90ae5b 172                 $e = new ErrorException($error->message, $error->code, 1,
JK 173                         $error->file, $error->line);
64d82d 174                 switch ($error->level) {
JK 175                     case LIBXML_ERR_ERROR:
176                     case LIBXML_ERR_FATAL:
90ae5b 177                         throw new Exception('Fatal error during XML parsing',
JK 178                                 0, $e);
64d82d 179                         break;
JK 180                 }
181             }
2f13b1 182         } catch(Exception $e) {
90ae5b 183             throw new JasigException('Authentication error: CAS server'
JK 184                     .' response invalid - parse error', 0, $e);
64d82d 185         } finally {
JK 186             libxml_clear_errors();
187             libxml_disable_entity_loader($xmlEntityLoader);
188             libxml_use_internal_errors($xmlInternalErrors);
189         }
190         
191         $failure = $xml->getElementsByTagName('authenticationFailure');
192         $success = $xml->getElementsByTagName('authenticationSuccess');
193         
194         if($failure->length > 0) {
195             $failure = $failure->item(0);
196             if(!($failure instanceof DOMElement)) {
90ae5b 197                 throw new JasigException('Authentication error: CAS server'
JK 198                         .' response invalid - authenticationFailure');
64d82d 199             }
90ae5b 200             throw new JasigAuthException('Authentication error: '
JK 201                     .$failure->textContent);
64d82d 202         } elseif($success->length > 0) {
JK 203             $success = $success->item(0);
204             if(!($success instanceof DOMElement)) {
90ae5b 205                 throw new JasigException('Authentication error: CAS server'
JK 206                         .' response invalid - authenticationSuccess');
64d82d 207             }
JK 208             
209             $user = $success->getElementsByTagName('user');
210             if($user->length == 0) {
90ae5b 211                 throw new JasigException('Authentication error: CAS server'
JK 212                         .' response invalid - user');
64d82d 213             }
JK 214             
215             $user = trim($user->item(0)->textContent);
2f13b1 216             if(strlen($user) < 1) {
90ae5b 217                 throw new JasigException('Authentication error: CAS server'
JK 218                         .' response invalid - user value');
64d82d 219             }
JK 220             
221             $jusr = new JasigUser();
222             $jusr->user = $user;
223             
224             $attrs = $success->getElementsByTagName('attributes');
225             if($attrs->length > 0) {
226                 $attrs = $attrs->item(0);
227                 foreach($attrs->childNodes as $node) {
228                     $jusr->attributes[$node->localName] = $node->textContent;
229                 }
230             }
231             
232             return $jusr;
2f13b1 233         } else {
90ae5b 234             throw new JasigException('Authentication error: CAS server'
JK 235                     .' response invalid - required tag not found');
64d82d 236         }
JK 237     }
238 }