1<?php 2 /* libraries/user.php 3 * 4 * Copyright (C) by Hugo Leisink <hugo@leisink.net> 5 * This file is part of the Banshee PHP framework 6 * http://www.banshee-php.org/ 7 * 8 * Don't change this file, unless you know what you are doing. 9 */ 10 11 final class user { 12 private $db = null; 13 private $settings = null; 14 private $session = null; 15 private $logged_in = false; 16 private $record = array(); 17 private $is_admin = false; 18 19 /* Constructor 20 * 21 * INPUT: object database, object settings, object session 22 * OUTPUT: - 23 * ERROR: - 24 */ 25 public function __construct($db, $settings, $session) { 26 $this->db = $db; 27 $this->settings = $settings; 28 $this->session = $session; 29 30 /* Basic HTTP Authentication for web services 31 */ 32 if (isset($_SERVER["HTTP_AUTHORIZATION"])) { 33 list($method, $auth) = explode(" ", $_SERVER["HTTP_AUTHORIZATION"], 2); 34 if (($method == "Basic") && (($auth = base64_decode($auth)) !== false)) { 35 list($username, $password) = explode(":", $auth, 2); 36 if ($this->login_password($username, $password, false) == false) { 37 header("Status: 401"); 38 } else { 39 $this->bind_to_ip(); 40 } 41 } 42 } 43 44 if (isset($_SESSION["user_id"])) { 45 if (time() - $_SESSION["last_private_visit"] >= $this->settings->session_timeout) { 46 $this->logout(); 47 } else if (($_SESSION["binded_ip"] === NO) || ($_SESSION["binded_ip"] === $_SERVER["REMOTE_ADDR"])) { 48 $this->load_user_record($_SESSION["user_id"]); 49 } 50 } 51 } 52 53 /* Magic method get 54 * 55 * INPUT: string key 56 * OUTPUT: mixed value 57 * ERROR: null 58 */ 59 public function __get($key) { 60 switch ($key) { 61 case "logged_in": return $this->logged_in; 62 case "is_admin": return $this->is_admin; 63 case "do_not_track": return $_SERVER["HTTP_DNT"] == 1; 64 case "session_via_database": return $this->session->using_database; 65 default: 66 if (isset($this->record[$key])) { 67 return $this->record[$key]; 68 } 69 } 70 71 return null; 72 } 73 74 /* Store user information from database in $this->record 75 * 76 * INPUT: int user identifier 77 * OUTPUT: - 78 * ERROR: - 79 */ 80 private function load_user_record($user_id) { 81 if (($this->record = $this->db->entry("users", $user_id)) == false) { 82 $this->logout(); 83 } else if ($this->record["status"] == USER_STATUS_DISABLED) { 84 $this->logout(); 85 } else { 86 $this->logged_in = true; 87 88 $this->record["role_ids"] = array(); 89 $query = "select role_id from user_role where user_id=%d"; 90 if (($roles = $this->db->execute($query, $this->record["id"])) != false) { 91 foreach ($roles as $role) { 92 array_push($this->record["role_ids"], $role["role_id"]); 93 if ((int)$role["role_id"] === (int)ADMIN_ROLE_ID) { 94 $this->is_admin = true; 95 } 96 } 97 } 98 } 99 } 100 101 /* Login user 102 * 103 * INPUT: int user id 104 * OUTPUT: - 105 * ERROR: - 106 */ 107 private function login($user_id) { 108 $this->load_user_record($user_id); 109 $this->log_action("user logged-in"); 110 111 $_SESSION["user_id"] = $user_id; 112 $_SESSION["binded_ip"] = NO; 113 $_SESSION["last_private_visit"] = time(); 114 115 $this->session->set_user_id($user_id); 116 } 117 118 /* Verify user credentials 119 * 120 * INPUT: string username, string password 121 * OUTPUT: boolean login correct 122 * ERROR: - 123 */ 124 public function login_password($username, $password) { 125 $query = "select * from users where username=%s and status!=%d limit 1"; 126 if (($data = $this->db->execute($query, $username, USER_STATUS_DISABLED)) == false) { 127 header("X-Hiawatha-Monitor: failed_login"); 128 sleep(1); 129 return false; 130 } 131 $user = $data[0]; 132 133 usleep(rand(0, 10000)); 134 135 if ($user["password"] === hash_password($password, $username)) { 136 $this->login((int)$user["id"]); 137 } 138 139 if ($this->logged_in == false) { 140 header("X-Hiawatha-Monitor: failed_login"); 141 sleep(1); 142 } 143 144 return $this->logged_in; 145 } 146 147 /* Verify one time key 148 * 149 * INPUT: string one time key 150 * OUTPUT: boolean key valid 151 * ERROR: - 152 */ 153 public function login_one_time_key($key) { 154 if ($key == "") { 155 return false; 156 } 157 158 usleep(rand(0, 100000)); 159 160 $query = "select * from users where one_time_key=%s and status!=%d limit 1"; 161 if (($data = $this->db->execute($query, $key, USER_STATUS_DISABLED)) == false) { 162 header("X-Hiawatha-Monitor: failed_login"); 163 sleep(1); 164 return false; 165 } 166 $user = $data[0]; 167 168 $query = "update users set one_time_key=null where id=%d"; 169 $this->db->query($query, $user["id"]); 170 171 $this->login((int)$user["id"]); 172 $this->bind_to_ip(); 173 174 return true; 175 } 176 177 /* Login via SSL client authentication 178 * 179 * INPUT: int certificate serial number 180 * OUTPUT: boolean serial number valid 181 * ERROR: - 182 */ 183 public function login_ssl_auth($cert_serial) { 184 $query = "select * from users where cert_serial=%d and status!=%d limit 1"; 185 if (($data = $this->db->execute($query, $cert_serial, USER_STATUS_DISABLED)) == false) { 186 return false; 187 } 188 $user = $data[0]; 189 190 $this->login((int)$user["id"]); 191 192 return true; 193 } 194 195 /* Logout current user 196 * 197 * INPUT: - 198 * OUTPUT: - 199 * ERROR: - 200 */ 201 public function logout() { 202 $this->log_action("user logged-out"); 203 204 $this->session->reset(); 205 206 $this->logged_in = false; 207 $this->record = array(); 208 $this->is_admin = false; 209 } 210 211 /* Checks if user has access to page 212 * 213 * INPUT: string page identifier 214 * OUTPUT: boolean user has access to page 215 * ERROR: - 216 */ 217 public function access_allowed($page) { 218 static $access = array(); 219 220 /* Always access 221 */ 222 $allowed = array(LOGOUT_MODULE); 223 if ($this->is_admin || in_array($page, $allowed)) { 224 return true; 225 } 226 227 /* Public module 228 */ 229 if (in_array($page, page_to_module(config_file("public_pages")))) { 230 return true; 231 } 232 233 /* Public page in database 234 */ 235 $query = "select count(*) as count from pages where url=%s and private=%d"; 236 if (($result = $this->db->execute($query, "/".$page, NO)) == false) { 237 return false; 238 } else if ($result[0]["count"] > 0) { 239 return true; 240 } 241 242 /* No roles, no access 243 */ 244 if (is_array($this->record["role_ids"]) == false) { 245 return false; 246 } else if (count($this->record["role_ids"]) == 0) { 247 return false; 248 } 249 250 /* Cached? 251 */ 252 if (isset($access[$page])) { 253 return $access[$page]; 254 } 255 256 /* Check access 257 */ 258 $conditions = $rids = array(); 259 foreach ($this->record["role_ids"] as $rid) { 260 array_push($conditions, "%d"); 261 array_push($rids, $rid); 262 } 263 264 if (in_array($page, page_to_module(config_file("private_pages")))) { 265 /* Pages on disk (modules) 266 */ 267 $query = "select %S from roles where id in (".implode(", ", $conditions).")"; 268 if (($access = $this->db->execute($query, $page, $rids)) == false) { 269 return false; 270 } 271 } else { 272 /* Pages in database 273 */ 274 $query = "select a.level from page_access a, pages p ". 275 "where a.page_id=p.id and p.url=%s and a.level>0 ". 276 "and a.role_id in (".implode(", ", $conditions).")"; 277 if (($access = $this->db->execute($query, "/".$page, $rids)) == false) { 278 return false; 279 } 280 } 281 282 $access[$page] = max(array_flatten($access)) > 0; 283 284 return $access[$page]; 285 } 286 287 /* Bind current session to IP address 288 * 289 * INPUT: - 290 * OUTPUT: - 291 * ERROR: - 292 */ 293 public function bind_to_ip() { 294 $_SESSION["binded_ip"] = $_SERVER["REMOTE_ADDR"]; 295 } 296 297 /* Verify if user has a certain role 298 * 299 * INPUT: int role identifier / string role name 300 * OUTPUT: boolean user has role 301 * ERROR: - 302 */ 303 public function has_role($role) { 304 if (is_int($role)) { 305 return in_array($role, $this->record["role_ids"]); 306 } else if (is_string($role)) { 307 if (($entry = $this->db->entry("roles", $role, "name")) != false) { 308 return $this->has_role((int)$entry["id"]); 309 } 310 } else if (is_array($role)) { 311 foreach ($role as $item) { 312 if ($this->has_role($item)) { 313 return true; 314 } 315 } 316 } 317 318 return false; 319 } 320 321 /* Log user action 322 * 323 * INPUT: string action 324 * OUTPUT: true 325 * ERROR: false 326 */ 327 public function log_action($action) { 328 if (func_num_args() > 1) { 329 $args = func_get_args(); 330 array_shift($args); 331 $action = vsprintf($action, $args); 332 } 333 334 $mesg = $_SERVER["REMOTE_ADDR"]."|".date("D d M Y H:i:s")."|"; 335 if ($this->logged_in == false) { 336 $mesg .= "-"; 337 } else if (isset($_SESSION["user_switch"]) == false) { 338 $mesg .= $this->id; 339 } else { 340 $mesg .= $_SESSION["user_switch"].":".$this->id; 341 } 342 $mesg .= "|".$action."\n"; 343 344 if (($fp = fopen("../logfiles/actions.log", "a")) == false) { 345 return false; 346 } 347 348 fputs($fp, $mesg); 349 fclose($fp); 350 351 return true; 352 } 353 } 354?> 355