Version compatible with Piwik 2.1
[piwik-CASLogin.git] / Auth.php
1 <?php
2 /**
3  * Piwik - Open source web analytics
4  * 
5  * @link http://piwik.org
6  * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later
7  * @version $Id:$
8  * 
9  * @category Piwik_Plugins
10  * @package CASLogin
11  */
12
13 namespace Piwik\Plugins\CASLogin;
14
15 use Piwik\AuthResult;
16 use Piwik\Common;
17 use Piwik\Config;
18 use Piwik\Db;
19 use Piwik\Piwik;
20 use Piwik\Plugins\UsersManager\API;
21
22 /**
23  * Class that implements an authentication mechanism via CAS (Central Authentication Services)
24  *
25  * @package Piwik_CASLogin
26  */
27 class Auth implements \Piwik\Auth
28 {
29         protected $login = null;
30         protected $token_auth = null;
31
32         public function getName()
33         {
34                 return 'CASLogin';
35         }
36
37         public function initSession($login, $md5Password, $rememberMe) {}
38
39         public function authenticate()
40         {
41                 $user = '';
42
43                 require_once PIWIK_INCLUDE_PATH . '/plugins/CASLogin/CAS/CAS.php';
44
45                 // initialize phpCAS
46
47                 // What happens here: in some piwik functionality, some additional API-style calls are
48                 // made from a controller action, where the authenticate() method will be called *again*.
49                 // This happens for instance when an admin changes some permissions in Settings->Users.
50                 // The first authenticate() is from the page, and the second is due to an API call.
51                 // This checks if there was already a phpcas instance already initialized, otherwize
52                 // phpCAS::client() would fail.
53                 global $PHPCAS_CLIENT;
54                 if(!is_object($PHPCAS_CLIENT)) {
55                         \phpCAS::client(
56                                 constant( Config::getInstance()->caslogin['protocol'] ),
57                                 Config::getInstance()->caslogin['host'],
58                                 (integer) Config::getInstance()->caslogin['port'],
59                 '',
60                 false
61                         );
62                 }
63
64                 // no SSL validation for the CAS server
65                 \phpCAS::setNoCasServerValidation();
66
67                 // Handle single signout requests from CAS server
68                 \phpCAS::handleLogoutRequests();
69
70                 // force CAS authentication only if it has been requested by action argument
71                 $action = Piwik::getAction();
72                 
73                 $auth = \phpCAS::checkAuthentication();
74                 if(!$auth) {
75                         if($action == 'redirectToCAS') {
76                                 phpCAS::forceAuthentication();
77                         }
78
79                         if($action != 'login' && Piwik::getModule() != 'CoreUpdater') {
80                                 Piwik::redirectToModule('CASLogin', 'login');
81                                 return;
82                         } elseif($action == 'redirectToCAS') {
83                                 phpCAS::forceAuthentication();
84                         } else {
85                                 return new AuthResult( AuthResult::FAILURE, $user, NULL );
86                         }
87                 }
88
89                 // Additional Attributes
90                 // For future retrieval of attributes; they _might_ be of some use, but are highly
91                 // dependable on a specific installation. CAS|piwik hackers can do some magic
92                 // here with SAML attributes etc.
93                 /*
94                 foreach (\phpCAS::getAttributes() as $key => $value) {
95                         // syslog(LOG_DEBUG, "attribute: $key - ". print_r($value, true));
96                 }
97                  */
98
99                 if (isset($_SESSION['phpCAS']) && isset($_SESSION['phpCAS']['user'])) {
100                         $user = $_SESSION['phpCAS']['user'];
101                 }
102
103                 if($user) {
104                         $db_user = Db::fetchRow('SELECT login, superuser_access FROM '.Common::prefixTable('user').' WHERE login = ?',
105                                         array($user)
106                         );
107                         if($db_user === null) {
108                                 // ***User Autocreate***
109                                 // We can either add the authenticated but not-yet-authorized user to the piwik users
110                                 // database, or ignore that.
111                                 // TODO: make this a config option
112                                 $this->_populateDb($user);
113                                 $login = $user;
114                                 $superuser = false;
115                         }
116                         else {
117                                 $login = $db_user['login'];
118                                 $superuser = $db_user['superuser_access'];
119                         }
120                         if($login == $user)
121                         {
122                                 if ($superuser)
123                                         $code = AuthResult::SUCCESS_SUPERUSER_AUTH_CODE;
124                                 else $code = AuthResult::SUCCESS; 
125                                 return new AuthResult($code, $login, NULL );
126                         }
127                 }
128
129                 return new AuthResult( AuthResult::FAILURE, $user, NULL );
130         }
131
132         public function setLogin($login)
133         {
134                 $this->login = $login;
135         }
136         
137     public function setTokenAuth($token_auth)
138         {
139                 $this->token_auth = $token_auth;
140         }
141
142         /**
143          * This method is used to inject user into Piwik's tables.
144          * @todo Alias could be the 'cn' returned from CAS attributes.
145          */
146         private function _populateDb($user)
147         {
148                 $result = null;
149                 $dummy = md5('abcd1234');
150                 if ($this->_helper_userExists($user)) {
151                         $this->_helper_updateUser($user, $dummy, '', $user);
152                 } else {
153                         $this->_helper_addUser($user, $dummy, '', $user);
154                 }
155         }
156
157
158         ///// The following methods are taken from Piwik's UserManager, but in order to inject data into piwik's user and access tables, we need
159         ///// to make sure we don't wreck things. The UserManager API uses authenticate() to check if we're eligable to look this up,
160         ///// soi we can't use it - we need superuser permissions anyway.
161         //
162         ///// Warning - these methods are of course under Piwik's license.
163         private function _helper_userExists($name)
164         {
165                 $count = Db::fetchOne("SELECT count(*)
166                                                                         FROM ".Common::prefixTable("user"). "
167                                                                         WHERE login = ?", $name);
168                 return $count > 0;
169         }
170
171         private function _helper_updateUser( $userLogin, $password = false, $email = false, $alias = false ) 
172         {
173                 $token_auth = API::getTokenAuth($userLogin, $password);
174
175                 Db::get()->update( Common::prefixTable("user"),
176                                         array(
177                                                 'password' => $password,
178                                                 'alias' => $alias,
179                                                 'email' => $email,
180                                                 'token_auth' => $token_auth,
181                                                 ),
182                                         "login = '$userLogin'"
183                         );
184         }
185
186         private function _helper_addUser( $userLogin, $password, $email, $alias = false )
187         {               
188                 $token_auth = API::getTokenAuth($userLogin, $password);
189
190                 Db::get()->insert( Common::prefixTable("user"), array(
191                                                                         'login' => $userLogin,
192                                                                         'password' => $password,
193                                                                         'alias' => $alias,
194                                                                         'email' => $email,
195                                                                         'token_auth' => $token_auth,
196                                                                         )
197                 );
198         }
199     
200 }
201