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

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