1<?php 2/** 3 * DokuWiki SSO CAS Plugin 4 * 5 * @licence GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Iain Hallam <iain@nineworlds.net> 7 */ 8 9/** 10 * Copyright (C) 2012 Iain Hallam, Andreas Gohr 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 25 */ 26 27// must be run within DokuWiki 28if(!defined('DOKU_INC')) die(); 29 30if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 31require_once(DOKU_PLUGIN.'action.php'); 32 33class action_plugin_ssocas extends DokuWiki_Action_Plugin { 34 function getInfo() { 35 return array ( 36 'author' => 'Iain Hallam', 37 'email' => 'iain@nineworlds.net', 38 'date' => '2012-06-16', 39 'name' => 'SSO CAS Plugin', 40 'desc' => 'Authenticate DokuWiki users via CAS', 41 'url' => 'http://www.dokuwiki.org/plugin:ssocas', 42 ); 43 } 44 45 function register (&$controller) { 46 if ($this->getConf('server') != '') { 47 $controller->register_hook ('HTML_LOGINFORM_OUTPUT', 'BEFORE', $this, 'handle_login_form'); 48 $controller->register_hook ('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_action'); 49 $controller->register_hook ('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handle_template'); 50 } 51 } 52 53 function _self () { 54 global $ID; 55 return wl($ID, '', true, ''); 56 } 57 58 function _selfdo ($do) { 59 global $ID; 60 return wl($ID, 'do=' . $do, true, '&'); 61 } 62 63 function _redirect ($url) { 64 header ('Location: ' . $url); 65 exit; 66 } 67 68 // Log function copied from Andreas Gohr's loglog plugin 69 function _log ($msg) { 70 global $conf; 71 $t = time(); 72 $log = $t."\t".strftime($conf['dformat'],$t)."\t".$_SERVER['REMOTE_ADDR']."\t".$_SERVER['REMOTE_USER']."\t".$msg; 73 io_saveFile($conf['cachedir'].'/ssocas.log',"$log\n",true); 74 } 75 76 function handle_login_form (&$event, $param) { 77 global $auth; 78 global $conf; 79 global $lang; 80 global $ID; 81 82 // Remove the register and resendpwd links, if they exist. 83 for ($formPosition = 0; $formPosition < count($event->data->_content); $formPosition++) { 84 $formElement = $event->data->getElementAt($formPosition); 85 if ((! is_array($formElement)) and (substr($formElement, 0, 2) == '<p')) { 86 $event->data->replaceElement ($formPosition, NULL); 87 } 88 } 89 90 $insertElement = 5; 91 92 if($auth && $auth->canDo('addUser') && actionOK('register')){ 93 $event->data->insertElement($insertElement,'<p>'.$lang['reghere'].': <a href="'.wl($ID,'do=register').'" rel="nofollow" class="wikilink1">'.$lang['register'].'</a></p>'); 94 $insertElement = 6; 95 } 96 if ($auth && $auth->canDo('modPass') && actionOK('resendpwd')) { 97 $event->data->insertElement($insertElement,'<p>'.$lang['pwdforget'].': <a href="'.wl($ID,'do=resendpwd').'" rel="nofollow" class="wikilink1">'.$lang['btn_resendpwd'].'</a></p>'); 98 } 99 100 if ($this->getConf('logourl') != '') { 101 $caslogo = '<img src="'.$this->getConf('logourl').'" alt="" style="vertical-align: middle;" /> '; 102 } else { 103 $caslogo = ''; 104 } 105 106 $event->data->insertElement(0,'<fieldset><legend>'.$this->getConf('name').'</legend>'); 107 $event->data->insertElement(1,'<p style="text-align: center;">'.$caslogo.'<a href="'.$this->_selfdo('caslogin').'">Login</a></p>'); 108 $event->data->insertElement(2,'</fieldset>'); 109 if ($this->getConf('jshidelocal')) { 110 $event->data->insertElement(3,'<p id="normalLoginToggle" style="display: none; text-align: center;"><a href="#" onClick="javascript:document.getElementById(\'normalLogin\').style.display = \'block\'; document.getElementById(\'normalLoginToggle\').style.display = \'none\'; return false;">Show '.$this->getConf('localname').'</a></p><p style="text-align: center;">Only use this if you cannot use the '.$this->getConf('name').' above.</p>'); 111 $event->data->replaceElement(4,'<fieldset id="normalLogin" style="display: block;"><legend>'.$this->getConf('localname').'</legend><script type="text/javascript">document.getElementById(\'normalLoginToggle\').style.display = \'block\'; document.getElementById(\'normalLogin\').style.display = \'none\';</script>'); 112 } else { 113 $event->data->replaceElement(3,'<fieldset><legend>'.$this->getConf('localname').'</legend>'); 114 } 115 } 116 117 function handle_caslogin () { 118 global $ACT, $auth, $conf, $INFO, $USERINFO; 119 $cas_auth_client = phpCAS::forceAuthentication(); 120 if ($cas_auth_client) { 121 // Successful 122 $casuser = phpCAS::getUser(); 123 if ($this->getConf('logging')) {$this->_log('user '.$casuser.' successfully authenticated');} 124 $USERINFO = $auth->getUserData($casuser); 125 126 if (empty($USERINFO)) { 127 // Logged in, but no valid account 128 if ($this->getConf('logging')) {$this->_log('user '.$casuser.' has no local account');} 129 $ACT = act_permcheck('show'); 130 msg ('Sorry; your login to '.$this->getConf('name').' succeeded but you don\'t have a valid account here.',-1); 131 } else { 132 // Populate the session variables 133 $_SERVER['REMOTE_USER'] = $casuser; 134 if ($this->getConf('stickysession')) { 135 $stickysession = true; 136 } else { 137 $stickysession = false; 138 } 139 auth_setCookie($casuser,'CAS',$stickysession); 140 141 // Authentication info has changed: reset the page info 142 $INFO = pageinfo(); 143 144 // Now logged in, so show the page 145 $ACT = act_permcheck('show'); 146 147 // Log action 148 if ($this->getConf('logging')) { 149 if ($stickysession) { 150 $this->_log('logged in permanently'); 151 }else{ 152 $this->_log('logged in temporarily'); 153 } 154 } 155 } 156 } else { 157 // Failed, but this should never be reached, because the CAS server shouldn't redirect back to us without a successful login 158 if ($this->getConf('logging')) {$this->_log('forceAuthentication failed');} 159 $ACT = act_permcheck('show'); 160 msg ('Sorry; your login to '.$this->getConf('name').' failed.',-1); 161 } 162 } 163 164 function handle_caslogout () { 165 // Check CAS authentication and whether to log out of CAS completely, and do a phpCAS::logout if so. 166 if ((isset($_SERVER['REMOTE_USER'])) && ($_SESSION[DOKU_COOKIE]['auth']['pass'] == 'CAS')) { 167 if (($this->getConf('caslogout')) && (phpCAS::checkAuthentication())) { 168 phpCAS::logoutWithRedirectServiceAndUrl($this->_self(), $this->_self()); 169 170 // Log action 171 if ($this->getConf('logging')) { 172 $this->_log('logged off'); 173 } 174 } 175 } 176 auth_logoff(); 177 } 178 179 function handle_action (&$event, $param) { 180 global $ACT; 181 require_once ('CAS.php'); 182 phpCAS::client($this->getConf('version').'.0',$this->getConf('server'),(integer) $this->getConf('port'),$this->getConf('uri')); 183 if (! ($this->getConf('servercert') or $this->getConf('servercacert'))) { 184 // Neither Server or CA certificate is set 185 phpCAS::setNoCasServerValidation(); 186 } else { 187 if ($this->getConf('servercert')) { 188 // Server certificate is set 189 phpCAS::setCasServerCert($this->getConf('servercert')); 190 } 191 if ($this->getConf('servercacert')) { 192 // CA certificate is set 193 phpCAS::setCasServerCACert($this->getConf('servercacert')); 194 } 195 } 196 197 // Handle the case where the CAS session is finished but the user is still logged in to DokuWiki 198 if (! $this->getConf('stickysession')) { 199 if ((isset($_SERVER['REMOTE_USER'])) && ($_SESSION[DOKU_COOKIE]['auth']['pass'] == 'CAS')) { 200 if (! phpCAS::checkAuthentication()) { 201 // Authentication failed 202 $event->preventDefault(); 203 $this->handle_caslogout(); 204 $this->_redirect($this->_self()); 205 } 206 } 207 } 208 209 if ($event->data == 'caslogin') { 210 $event->preventDefault(); 211 $this->handle_caslogin(); 212 } 213 if ($event->data == 'logout') { 214 $this->handle_caslogout(); 215 } 216 } 217 218 function handle_template (&$event, $param) { 219 if ($event->data == 'caslogin') { 220 $event->preventDefault(); 221 } 222 } 223} 224