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

Jacek Kowalski
2015-09-03 8891c7c5e6c5857eec83205815fa023939e1e126
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     
710793 70     public function getSessionName($sessionName) {
JK 71         return $this->sessionName;
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     
115     public function verifyTicket($ticket) {
116         $context = array(
117             'http' => array(
118                 'method' => 'GET',
119                 'user_agent' => 'uphpCAS/'.self::VERSION,
120                 'max_redirects' => 3,
121             ),
122             'ssl' => array(
123                 'verify_peer' => TRUE,
90ae5b 124                 'verify_peer_name' => TRUE,
64d82d 125                 'verify_depth' => 5,
90ae5b 126                 'allow_self_signed' => FALSE,
JK 127                 'disable_compression' => TRUE,
64d82d 128             ),
JK 129         );
130         
d35cf4 131         if(version_compare(PHP_VERSION, '5.6', '<')) {
JK 132             $cafiles = array(
133                 '/etc/ssl/certs/ca-certificates.crt',
134                 '/etc/ssl/certs/ca-bundle.crt',
135                 '/etc/pki/tls/certs/ca-bundle.crt',
136             );
137             $cafile = NULL;
138             foreach($cafiles as $file) {
139                 if(is_file($file)) {
140                     $cafile = $file;
141                     break;
142                 }
143             }
144             
145             $url = parse_url($this->serverUrl);
146             $context['ssl']['cafile'] = $cafile;
147             $context['ssl']['ciphers'] = 'ECDH:DH:AES:CAMELLIA:!SSLv2:!aNULL'
148                     .':!eNULL:!EXPORT:!DES:!3DES:!MD5:!RC4:!ADH:!PSK:!SRP';
149             $context['ssl']['CN_match'] = $url['host'];
150         }
151         
90ae5b 152         $data = file_get_contents($this->serverUrl
JK 153                     .'/serviceValidate?service='.urlencode($this->serviceUrl)
154                     .'&ticket='.urlencode($ticket),
64d82d 155                 FALSE, stream_context_create($context));
JK 156         if($data === FALSE) {
157             throw new JasigException('Authentication error: CAS server is unavailable');
158         }
159         
160         $xmlEntityLoader = libxml_disable_entity_loader(TRUE);
161         $xmlInternalErrors = libxml_use_internal_errors(TRUE);
162         try {
163             $xml = new DOMDocument();
164             $xml->loadXML($data);
165             
166             foreach(libxml_get_errors() as $error) {
90ae5b 167                 $e = new ErrorException($error->message, $error->code, 1,
JK 168                         $error->file, $error->line);
64d82d 169                 switch ($error->level) {
JK 170                     case LIBXML_ERR_ERROR:
171                     case LIBXML_ERR_FATAL:
90ae5b 172                         throw new Exception('Fatal error during XML parsing',
JK 173                                 0, $e);
64d82d 174                         break;
JK 175                 }
176             }
2f13b1 177         } catch(Exception $e) {
90ae5b 178             throw new JasigException('Authentication error: CAS server'
JK 179                     .' response invalid - parse error', 0, $e);
64d82d 180         } finally {
JK 181             libxml_clear_errors();
182             libxml_disable_entity_loader($xmlEntityLoader);
183             libxml_use_internal_errors($xmlInternalErrors);
184         }
185         
186         $failure = $xml->getElementsByTagName('authenticationFailure');
187         $success = $xml->getElementsByTagName('authenticationSuccess');
188         
189         if($failure->length > 0) {
190             $failure = $failure->item(0);
191             if(!($failure instanceof DOMElement)) {
90ae5b 192                 throw new JasigException('Authentication error: CAS server'
JK 193                         .' response invalid - authenticationFailure');
64d82d 194             }
90ae5b 195             throw new JasigAuthException('Authentication error: '
JK 196                     .$failure->textContent);
64d82d 197         } elseif($success->length > 0) {
JK 198             $success = $success->item(0);
199             if(!($success instanceof DOMElement)) {
90ae5b 200                 throw new JasigException('Authentication error: CAS server'
JK 201                         .' response invalid - authenticationSuccess');
64d82d 202             }
JK 203             
204             $user = $success->getElementsByTagName('user');
205             if($user->length == 0) {
90ae5b 206                 throw new JasigException('Authentication error: CAS server'
JK 207                         .' response invalid - user');
64d82d 208             }
JK 209             
210             $user = trim($user->item(0)->textContent);
2f13b1 211             if(strlen($user) < 1) {
90ae5b 212                 throw new JasigException('Authentication error: CAS server'
JK 213                         .' response invalid - user value');
64d82d 214             }
JK 215             
216             $jusr = new JasigUser();
217             $jusr->user = $user;
218             
219             $attrs = $success->getElementsByTagName('attributes');
220             if($attrs->length > 0) {
221                 $attrs = $attrs->item(0);
222                 foreach($attrs->childNodes as $node) {
223                     $jusr->attributes[$node->localName] = $node->textContent;
224                 }
225             }
226             
227             return $jusr;
2f13b1 228         } else {
90ae5b 229             throw new JasigException('Authentication error: CAS server'
JK 230                     .' response invalid - required tag not found');
64d82d 231         }
JK 232     }
233 }