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

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