1 <?php 2 /* Copyright (c) 2013, Geert Bergman (geert@scrivo.nl) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. Neither the name of "Scrivo" nor the names of its contributors may be 14 * used to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $Id: LoginKey.php 866 2013-08-25 16:22:35Z geert $ 30 */ 31 32 /** 33 * Implementation of the \Scrivo\LoginKey class. 34 */ 35 36 namespace Scrivo; 37 38 /** 39 * Add a login_events record with a temporary key that can 40 * be used to log in. The following fields should be populated: 41 * 42 * instance_id Instance id 43 * date_login Time of the login event (for timeout) 44 * user_id User id 45 * return_code 0: OK, 1: invalid pwd, 2: invalid ucode 46 * remote_address Client IP-address 47 * access_key Random key 48 * 49 * Login return codes: 50 * 0: valid login credentials 51 * 0: login failed, invalid key 52 * 53 * An alternative function can defined in the .htscrivo file 54 * (LOGIN_PROVIDER refers to a php script that implements the 55 * function 'login_key()'. This is usefull if you want to use 56 * another user database to log in (LDAP). You still need to 57 * match roles on both systems, but you can write a function 58 * that does the authentication on the external system and if 59 * authenticated simply insert a row in the login_events table. 60 * Note that you still need a user in the Scrivo database, 61 * but that is only functional: a user id is used to retreive 62 * the roles. Simply insert a new user on login and link it 63 * with the corresponding roles. 64 */ 65 66 class LoginKey { 67 68 /** 69 * Value to indicate that login was unsuccesfull due to an invalid password. 70 */ 71 const INVALID_PASSWORD = 1; 72 73 /** 74 * Value to indicate that login was unsuccesfull due to an invalid usercode. 75 */ 76 const INVALID_USERCODE = 2; 77 78 /** 79 * Value to indicate that login was succesfull. 80 */ 81 const LOGIN_SUCCESSFULL = 3; 82 83 /** 84 * Value to indicate that login was unsuccesfull because login key 85 * generation and verification action were initiated using a different ip 86 * addresses. 87 */ 88 const INVALID_IP = 4; 89 90 /** 91 * Value to indicate that login was unsuccesfull because of a time out 92 * when verifying the key. 93 */ 94 const TIMEOUT = 5; 95 96 /** 97 * A Scrivo context. 98 * @var \Scrivo\Context 99 */ 100 protected $context = null; 101 102 /** 103 * Create a login object to either generate or verify a login key. 104 * 105 * @param \Scrivo\Context $context A valid Scrivo context. 106 */ 107 public function __construct(\Scrivo\Context $context) { 108 $this->context = $context; 109 } 110 111 /** 112 * Generate a login key for a given usercode and password. Override this 113 * function if you want to verify the user against another autentication 114 * database. 115 * 116 * @param \Scrivo\String $usercode A user code. 117 * @param \Scrivo\String $password A password. 118 * @return \Scrivo\String The login key if usercode and password 119 * verification was successfull, or NULL if not. 120 */ 121 public function generate( 122 \Scrivo\String $usercode, \Scrivo\String $password) { 123 124 $pwd = false; 125 $usr = false; 126 127 $sth = $this->context->connection->prepare( 128 "SELECT * FROM user where instance_id = :instId and user_code = :uCode"); 129 130 $this->context->connection->bindInstance($sth); 131 $sth->bindValue(":uCode", $usercode, \PDO::PARAM_STR); 132 133 $sth->execute(); 134 135 if ($sth->rowCount() == 1) { 136 $usr = true; 137 $user = $sth->fetch(\PDO::FETCH_ASSOC); 138 $pwd = $user["password"] == crypt($password, $user["password"]); 139 } 140 141 $lres = 0; 142 if (!$usr) { 143 $lres = self::INVALID_USERCODE; 144 } else if (!$pwd) { 145 $lres = self::INVALID_PASSWORD; 146 } 147 148 $k = new \Scrivo\String(md5(microtime())); 149 150 $this->insert( 151 $lres == self::INVALID_USERCODE ? -1 : intval($user["user_id"]), 152 $lres, $k); 153 154 return $lres == 0 ? $k : null; 155 } 156 157 /** 158 * Insert an entry in the login_events table, this entry will be checked 159 * by LoginKey::verify(). 160 * 161 * @param int $userId A user id if password verification was succesfull, 162 * -1 if not. 163 * @param int $loginStatus The result of the password verification: either 164 * \Scrivo\LoginKey::INVALID_USERCODE, \Scrivo\LoginKey::INVALID_PASSWORD 165 * or 0 (no error). 166 * @param \Scrivo\String $key A generated random key. 167 */ 168 public function insert($userId, $loginStatus, \Scrivo\String $key) { 169 170 $sth = $this->context->connection->prepare( 171 "INSERT INTO login_events ( 172 instance_id, date_login, user_id, user_satus, 173 return_code, remote_address, access_key 174 ) VALUES ( 175 :instId, NOW(), :userId, 0, 176 :returnCode, :remoteAddr, :key 177 )"); 178 179 $this->context->connection->bindInstance($sth); 180 $sth->bindValue(":userId", $userId, \PDO::PARAM_INT); 181 $sth->bindValue(":returnCode", $loginStatus, \PDO::PARAM_INT); 182 $sth->bindValue( 183 ":remoteAddr", $_SERVER["REMOTE_ADDR"], \PDO::PARAM_STR); 184 $sth->bindValue(":key", $key, \PDO::PARAM_STR); 185 186 $sth->execute(); 187 } 188 189 /** 190 * Verify the login key, return the user if succesfull, NULL if not. Also 191 * update the login_events table with the result of the verification. 192 * 193 * @param \Scrivo\String $key A key that was previously generated by 194 * LoginKey::generate() 195 * @return \Scrivo\User|NULL A Scrivo user if verification was succesfull, 196 * NULL if not. 197 */ 198 public function verify(\Scrivo\String $key) { 199 200 $addr = $_SERVER["REMOTE_ADDR"]; 201 202 $sth = $this->context->connection->prepare( 203 "SELECT 204 UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(date_login) TP, 205 user_id, user_satus, return_code, remote_address 206 FROM login_events 207 WHERE instance_id = :instId AND access_key = :key" 208 ); 209 210 $this->context->connection->bindInstance($sth); 211 $sth->bindValue(":key", $key, \PDO::PARAM_STR); 212 213 $sth->execute(); 214 215 $status = 0; 216 217 if ($sth->rowCount() == 1) { 218 219 $le = $sth->fetch(\PDO::FETCH_ASSOC); 220 221 $status = self::LOGIN_SUCCESSFULL; 222 $r = intval($le["return_code"]); 223 224 if ($r == self::INVALID_USERCODE || $r == self::INVALID_PASSWORD) { 225 $status = $r; 226 } else if ($le["remote_address"] != $addr) { 227 $status = self::INVALID_IP; 228 } else if ($le["TP"] > 10) { 229 $status = self::TIMEOUT; 230 } 231 232 if ($status == self::LOGIN_SUCCESSFULL || 233 $status == self::INVALID_IP || $status == self::TIMEOUT) { 234 235 $sth = $this->context->connection->prepare( 236 "UPDATE login_events SET return_code = :ret 237 WHERE instance_id = :instId AND access_key = :key"); 238 239 $this->context->connection->bindInstance($sth); 240 $sth->bindValue(":ret", $status, \PDO::PARAM_INT); 241 $sth->bindValue(":key", $key, \PDO::PARAM_STR); 242 243 $sth->execute(); 244 245 } 246 247 if ($status == self::LOGIN_SUCCESSFULL) { 248 249 return \Scrivo\User::fetch($this->context, intval($le["user_id"])); 250 251 } 252 253 } 254 255 return null; 256 } 257 258 } 259 260 ?>
Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013