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

Jacek Kowalski
2015-09-03 fa3e92385333620ad1d7c2c74b140fdf2a1ae0a5
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     
fa3e92 133     protected function createStreamContext($hostname) {
JK 134         $context = stream_context_create(array(
64d82d 135             'http' => array(
JK 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             ),
fa3e92 147         ));
64d82d 148         
d35cf4 149         if(version_compare(PHP_VERSION, '5.6', '<')) {
fa3e92 150             stream_context_set_option($context, array(
JK 151                 'ssl' => array(
152                     'cafile' => $this->findCaFile(),
153                     'ciphers' => 'ECDH:DH:AES:CAMELLIA:!SSLv2:!aNULL:!eNULL'
154                         .':!EXPORT:!DES:!3DES:!MD5:!RC4:!ADH:!PSK:!SRP',
155                     'CN_match' => $hostname,
156                 ),
157             ));
d35cf4 158         }
fa3e92 159         
JK 160         return $context;
161     }
162     
163     public function verifyTicket($ticket) {
164         $url = parse_url($this->serverUrl);
165         $context = $this->createStreamContext($url['host']);
d35cf4 166         
90ae5b 167         $data = file_get_contents($this->serverUrl
JK 168                     .'/serviceValidate?service='.urlencode($this->serviceUrl)
fa3e92 169                     .'&ticket='.urlencode($ticket), FALSE, $context);
64d82d 170         if($data === FALSE) {
JK 171             throw new JasigException('Authentication error: CAS server is unavailable');
172         }
173         
174         $xmlEntityLoader = libxml_disable_entity_loader(TRUE);
175         $xmlInternalErrors = libxml_use_internal_errors(TRUE);
176         try {
177             $xml = new DOMDocument();
178             $xml->loadXML($data);
179             
180             foreach(libxml_get_errors() as $error) {
90ae5b 181                 $e = new ErrorException($error->message, $error->code, 1,
JK 182                         $error->file, $error->line);
64d82d 183                 switch ($error->level) {
JK 184                     case LIBXML_ERR_ERROR:
185                     case LIBXML_ERR_FATAL:
90ae5b 186                         throw new Exception('Fatal error during XML parsing',
JK 187                                 0, $e);
64d82d 188                         break;
JK 189                 }
190             }
2f13b1 191         } catch(Exception $e) {
90ae5b 192             throw new JasigException('Authentication error: CAS server'
JK 193                     .' response invalid - parse error', 0, $e);
64d82d 194         } finally {
JK 195             libxml_clear_errors();
196             libxml_disable_entity_loader($xmlEntityLoader);
197             libxml_use_internal_errors($xmlInternalErrors);
198         }
199         
200         $failure = $xml->getElementsByTagName('authenticationFailure');
201         $success = $xml->getElementsByTagName('authenticationSuccess');
202         
203         if($failure->length > 0) {
204             $failure = $failure->item(0);
205             if(!($failure instanceof DOMElement)) {
90ae5b 206                 throw new JasigException('Authentication error: CAS server'
JK 207                         .' response invalid - authenticationFailure');
64d82d 208             }
90ae5b 209             throw new JasigAuthException('Authentication error: '
JK 210                     .$failure->textContent);
64d82d 211         } elseif($success->length > 0) {
JK 212             $success = $success->item(0);
213             if(!($success instanceof DOMElement)) {
90ae5b 214                 throw new JasigException('Authentication error: CAS server'
JK 215                         .' response invalid - authenticationSuccess');
64d82d 216             }
JK 217             
218             $user = $success->getElementsByTagName('user');
219             if($user->length == 0) {
90ae5b 220                 throw new JasigException('Authentication error: CAS server'
JK 221                         .' response invalid - user');
64d82d 222             }
JK 223             
224             $user = trim($user->item(0)->textContent);
2f13b1 225             if(strlen($user) < 1) {
90ae5b 226                 throw new JasigException('Authentication error: CAS server'
JK 227                         .' response invalid - user value');
64d82d 228             }
JK 229             
230             $jusr = new JasigUser();
231             $jusr->user = $user;
232             
233             $attrs = $success->getElementsByTagName('attributes');
234             if($attrs->length > 0) {
235                 $attrs = $attrs->item(0);
236                 foreach($attrs->childNodes as $node) {
237                     $jusr->attributes[$node->localName] = $node->textContent;
238                 }
239             }
240             
241             return $jusr;
2f13b1 242         } else {
90ae5b 243             throw new JasigException('Authentication error: CAS server'
JK 244                     .' response invalid - required tag not found');
64d82d 245         }
JK 246     }
247 }