1<?php 2 /* libraries/security.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 9 /* Pre-defined validation strings for valid_input() 10 */ 11 define("VALIDATE_CAPITALS", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 12 define("VALIDATE_NONCAPITALS", "abcdefghijklmnopqrstuvwxyz"); 13 define("VALIDATE_LETTERS", VALIDATE_CAPITALS.VALIDATE_NONCAPITALS); 14 define("VALIDATE_PHRASE", VALIDATE_LETTERS." ,.?!:;-'"); 15 define("VALIDATE_NUMBERS", "0123456789"); 16 define("VALIDATE_EMAIL", VALIDATE_LETTERS.VALIDATE_NUMBERS."_-@."); 17 define("VALIDATE_SYMBOLS", "!@#$%^&*()_-+={}[]|\:;\"'`~<>,./?"); 18 define("VALIDATE_URL", VALIDATE_LETTERS.VALIDATE_NUMBERS."-_/.="); 19 20 define("VALIDATE_NONEMPTY", 0); 21 22 /* Secure password with PBKDF2 23 * 24 * INPUT: string password, string salt 25 * OUTPUT: string hashed password 26 * ERROR: - 27 */ 28 function hash_password($password, $salt) { 29 return hash_pbkdf2("sha256", $password, hash("sha256", $salt), PASSWORD_ITERATIONS, 0); 30 } 31 32 /* Validate input 33 * 34 * INPUT: string input, string valid characters[, int length] 35 * OUTPUT: boolean input oke 36 * ERROR: - 37 */ 38 function valid_input($data, $allowed, $length = null) { 39 if (is_array($data) == false) { 40 $data_len = strlen($data); 41 42 if ($length !== null) { 43 if ($length == VALIDATE_NONEMPTY) { 44 if ($data_len == 0) { 45 return false; 46 } 47 } else if ($data_len !== $length) { 48 return false; 49 } 50 } else if ($data_len == 0) { 51 return true; 52 } 53 54 $data = str_split($data); 55 $allowed = str_split($allowed); 56 $diff = array_diff($data, $allowed); 57 58 return count($diff) == 0; 59 } else foreach ($data as $item) { 60 if (valid_input($item, $allowed, $length) == false) { 61 return false; 62 } 63 } 64 65 return true; 66 } 67 68 /* Validate an e-mail address 69 * 70 * INPUT: string e-mail address 71 * OUTPUT: boolean e-mail address oke 72 * ERROR: - 73 */ 74 function valid_email($email) { 75 return email::valid_address($email); 76 } 77 78 /* Validate a date string 79 * 80 * INPUT: string date 81 * OUTPUT: boolean date oke 82 * ERROR: - 83 */ 84 function valid_date($date) { 85 if ($date == "0000-00-00") { 86 return false; 87 } 88 89 return preg_match("/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/", $date) === 1; 90 } 91 92 /* Validate a time string 93 * 94 * INPUT: string time 95 * OUTPUT: boolean time oke 96 * ERROR: - 97 */ 98 function valid_time($time) { 99 return preg_match("/^(([01]?[0-9])|(2[0-3])):[0-5][0-9](:[0-5][0-9])?$/", $time) === 1; 100 } 101 102 /* Validate a timestamp 103 * 104 * INPUT: string timestamp 105 * OUTPUT: boolean timestamp oke 106 * ERROR: - 107 */ 108 function valid_timestamp($timestamp) { 109 list($date, $time) = explode(" ", $timestamp, 2); 110 return valid_date($date) && valid_time($time); 111 } 112 113 /* Validate a telephone number 114 * 115 * INPUT: string telephone number 116 * OUTPUT: boolean telephone number oke 117 * ERROR: - 118 */ 119 function valid_phonenumber($phonenr) { 120 $phonenr = str_replace(" ", "", $phonenr); 121 return preg_match("/^(\+31|0)([0-9]{9}|6-?[0-9]{8}|[0-9]{2}-?[0-9]{7}|[0-9]{3}-?[0-9]{6})$/", $phonenr) === 1; 122 } 123 124 /* Get users with a certain role 125 * 126 * INPUT: object database, string role name[, string role name, ...] 127 * OUTPUT: array user information 128 * ERROR: false 129 */ 130 function users_with_role() { 131 $roles = func_get_args(); 132 if (count($roles) < 2) { 133 return false; 134 } 135 136 $db = array_shift($roles); 137 138 $query = "select distinct u.* from users u, user_role m, roles r ". 139 "where r.id=m.role_id and m.user_id=u.id and (". 140 implode(" or ", array_fill(0, count($roles), "r.name=%s")). 141 ")"; 142 143 return $db->execute($query, $roles); 144 } 145 146 /* Return a per-page overview of the access levels 147 * 148 * INPUT: object database 149 * OUTPUT: array( string page => int access level[, ....] ) 150 * ERROR: false 151 */ 152 function page_access_list($db, $user) { 153 $access_rights = array(); 154 155 /* Public pages on disk 156 */ 157 $public = page_to_module(config_file("public_pages")); 158 foreach ($public as $page) { 159 $access_rights[$page] = 1; 160 } 161 162 /* Private pages on disk 163 */ 164 $private_pages = page_to_module(config_file("private_pages")); 165 foreach ($private_pages as $page) { 166 $access_rights[$page] = $user->is_admin ? YES : NO; 167 } 168 169 if ($user->logged_in && ($user->is_admin == false)) { 170 $query = "select * from roles where id in ". 171 "(select role_id from user_role where user_id=%d)"; 172 if (($roles = $db->execute($query, $user->id)) === false) { 173 return false; 174 } 175 foreach ($roles as $role) { 176 $role = array_slice($role, 2); 177 foreach ($role as $page => $level) { 178 $level = (int)$level; 179 if ($user->is_admin && ($level == NO)) { 180 $level = YES; 181 } 182 if (isset($access_rights[$page]) == false) { 183 $access_rights[$page] = $level; 184 } else if ($access_rights[$page] < $level) { 185 $access_rights[$page] = $level; 186 } 187 } 188 } 189 } 190 191 /* Pages in database 192 */ 193 if (($pages = $db->execute("select * from pages")) === false) { 194 return false; 195 } 196 foreach ($pages as $page) { 197 $access_rights[ltrim($page["url"], "/")] = is_false($page["private"]) || $user->is_admin ? YES : NO; 198 } 199 200 if ($user->logged_in && ($user->is_admin == false)) { 201 $conditions = $rids = array(); 202 foreach ($user->role_ids as $rid) { 203 array_push($conditions, "role_id=%d"); 204 array_push($rids, $rid); 205 } 206 207 $query = "select p.url,a.level from pages p, page_access a ". 208 "where p.id=a.page_id and (".implode(" or ", $conditions).")"; 209 if (($pages = $db->execute($query, $rids)) === false) { 210 return false; 211 } 212 213 foreach ($pages as $page) { 214 $url = ltrim($page["url"], "/"); 215 if ($access_rights[$url] < $page["level"]) { 216 $access_rights[$url] = $page["level"]; 217 } 218 } 219 } 220 221 return $access_rights; 222 } 223 224 /* Generate random string 225 * 226 * INPUT: [int length] 227 * OUTPUT: string random string 228 * ERROR: - 229 */ 230 function random_string($length = 32) { 231 $characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890"; 232 $max_chars = strlen($characters) - 1; 233 234 $bytes = openssl_random_pseudo_bytes($length); 235 236 $result = ""; 237 for ($i = 0; $i < $length; $i++) { 238 $result .= $characters[floor(ord($bytes[$i]) * $max_chars / 256)]; 239 } 240 241 return $result; 242 } 243 244 /* Get user's one time key 245 * 246 * INPUT: object database, int user identifier 247 * OUTPUT: string one time key 248 * ERROR: false 249 */ 250 function one_time_key($db, $user_id) { 251 if (($user = $db->entry("users", $user_id)) == false) { 252 return false; 253 } 254 255 if ($user["one_time_key"] != null) { 256 return $user["one_time_key"]; 257 } 258 259 $attempts = 3; 260 $query = "select id from users where one_time_key=%s"; 261 262 do { 263 if ($attempts-- == 0) { 264 return false; 265 } 266 267 $key = random_string(); 268 269 if (($result = $db->execute($query, $key)) === false) { 270 return false; 271 } 272 } while ($result != false); 273 274 if ($db->update("users", $user_id, array("one_time_key" => $key)) == false) { 275 return false; 276 } 277 278 return $key; 279 } 280?> 281