/lib/user/blacklistlib.php
PHP | 360 lines | 283 code | 22 blank | 55 comment | 8 complexity | a600c9b3bcdc51ed80c1ef1d91921589 MD5 | raw file
- <?php
- // (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
- //
- // All Rights Reserved. See copyright.txt for details and a complete list of authors.
- // Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
- // $Id$
- /**
- *
- * A Group of functions related to Password Blacklist & Password Index Handling
- *
- * Class blacklist
- */
- class blacklistLib extends TikiLib
- {
- /**
- * @var int the number of passwords to generate (limit) or actual number, after the fact.
- */
- public $limit;
- /**
- * @var int the actual number of passwords generated.
- */
- public $actual;
- /**
- * Set default values
- *
- * blacklist constructor.
- */
- public function __construct()
- {
- $this->limit = 1000; // the number of passwords to generate (limit)
- }
- /**
- * removes the password index databse, if it exists.
- */
- public function deletePassIndex()
- {
- $query = 'DROP TABLE IF EXISTS tiki_password_index;';
- $this->query($query, []);
- }
- /**
- * Creates the word index database table.
- */
- public function createPassIndex()
- {
- $query = 'CREATE TABLE `tiki_password_index` (
- `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
- `password` VARCHAR(30) NOT NULL , UNIQUE (`password`) ,
- `length` TINYINT(30) NULL DEFAULT NULL ,
- `numchar` BOOLEAN NULL DEFAULT NULL ,
- `special` BOOLEAN NULL , PRIMARY KEY (`id`), UNIQUE (`password`)) ENGINE = InnoDB;';
- $this->query($query, []);
- }
- /**
- *
- * Given a filename, it will load the pass index database with its contents.
- * Files should be word lists separated by new lines.
- *
- * @param $filename string
- * @param $load bool Specifies if LOAD DATA INFILE is used. One needs to
- * be running mysql locally and have permission to use it
- * however it can handle much larger sets of data.
- */
- public function loadPassIndex($filename, $load = false)
- {
- if ($load) {
- $query = "LOAD DATA INFILE '" . $filename . "' IGNORE INTO TABLE `tiki_password_index` LINES TERMINATED BY '\n' (`password`);";
- $this->query($query, []);
- } else {
- $passwords = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- $passwords = array_map('strtolower', $passwords);
- $passwords = array_map('trim', $passwords);
- $passwords = array_unique($passwords);
- $passwords = array_map('addslashes', $passwords);
- $passwords = "('" . implode("'),('", $passwords) . "')";
- $query = "INSERT INTO tiki_password_index (password) VALUES $passwords";
- $this->query($query);
- }
- unlink($filename); // delete used temp file.
- $query = 'UPDATE tiki_password_index SET password = LOWER(password), length = CHAR_LENGTH(password), numchar = IF(password REGEXP \'[a-z]\' && password REGEXP \'[0-9]\',1,0), special = password REGEXP \'[!@#$%^&*()=+?><\\,.`;:{}~\\\'/"]\'';
- // the above indexes the password list with length, if the pasword contains both a letter and number, and if it contains special charactes (except for [], casue i couldnt figure it out!
- $this->query($query, []);
- }
- /**
- * Find the number of indexed passwords currently stored
- *@$result TikiDb_Pdo_Result
- *
- * @return int
- */
- public function passIndexNum()
- {
- // first check if table exists, and return 0 if it does not.
- $query = 'SELECT 1 FROM information_schema.COLUMNS
- WHERE table_name = \'tiki_password_index\'
- LIMIT 1;';
- $result = $this->query($query, []);
- $tableExists = $result->fetchRow();
- if ($tableExists[1] != 1) {
- return 0;
- }
- // if table does exits, find number of results and return.
- $query = 'SELECT MAX(id) FROM tiki_password_index';
- $result = $this->query($query, []);
- $num_rows = $result->fetchRow();
- return $num_rows['MAX(id)'];
- }
- /**
- *
- * Generates a formatted list of passwords, with new lines separating each password
- *
- * @param $toDisk bool if the file is written to disk or to screen.
- *
- * @return bool true on success and false on failure.
- */
- public function generatePassList($toDisk)
- {
- global $prefs;
- $query = 'SELECT password FROM tiki_password_index WHERE length >= ?';
- if ($prefs['pass_chr_special']) {
- $query .= ' && special';
- }
- if ($prefs['pass_chr_num']) {
- $query .= ' && numchar';
- }
- $query .= ' ORDER BY id ASC LIMIT ' . $this->limit;
- $result = $this->query($query, [$prefs['min_pass_length']]);
- $this->actual = $result->NumRows();
- if ($this->actual == 0) {
- Feedback::error(tr('There is no passwords that fit your criteria. You will need a more extensive word list to generate a password list.'));
- } elseif ($this->actual < $this->limit) {
- Feedback::warning("There wasn't enough words to meet your password limit. There was $this->actual passwords blacklisted.");
- }
- if ($toDisk) {
- $filename = $this->generateBlacklistName();
- if (! is_dir(dirname($filename))) {
- if (! mkdir(dirname($filename))) { // if the directory isnt there create it.
- Feedback::error(tr('Could not create /storage/pass_blacklists directory.'));
- return false;
- }
- }
- if (file_exists($filename)) {
- if (unlink($filename)) { // if the file already exists, then delete.
- Feedback::warning(tr('Existing password blacklist file was overwritten.'));
- } else {
- Feedback::error(tr('Existing password blacklist file could not be overwritten.'));
- return false;
- }
- }
- $pointer = @fopen($filename, 'x');
- if (! $pointer) {
- Feedback::error(tr('File Error. Password file not created.'));
- return false;
- };
- while ($foo = $result->fetchrow()) {
- if (! fwrite($pointer, $foo['password'] . PHP_EOL)) {
- Feedback::error(tr('File Error. Password file generation interrupted.'));
- return false;
- }
- }
- fclose($pointer);
- } else {
- while ($foo = $result->fetchrow()) {
- echo $foo['password'] . PHP_EOL;
- }
- }
- Feedback::success(tr('Password blacklist file generated.'));
- return true;
- }
- /**
- * Generates the name for a password file
- *
- * @param bool $asFile should the directory be returned as a file name with directory, if false only the name without extension
- *
- * @return string
- *
- */
- public function generateBlacklistName($asFile = true)
- {
- global $prefs;
- $filename = '';
- if ($asFile) {
- $filename = 'storage/pass_blacklists/'; // directory
- }
- $filename .= $prefs['pass_chr_num'];
- $filename .= '-' . $prefs['pass_chr_special'];
- $filename .= '-' . $prefs['min_pass_length'];
- $filename .= '-1-'; // indicates user created file
- $filename .= $this->actual;
- if (! $asFile) {
- return $filename;
- }
- $filename .= '.txt';
- return $filename;
- }
- public function whatFileUsing()
- {
- if ($GLOBALS['prefs']['pass_blacklist'] == 'n' || ! isset($GLOBALS['prefs']['pass_blacklist'])) {
- return 'Disabled';
- } elseif ($GLOBALS['prefs']['pass_blacklist_file'] == 'auto') {
- return $this->readableBlackName(explode('-', $GLOBALS['prefs']['pass_auto_blacklist'])) . ' - Auto Selected';
- } else {
- return $this->readableBlackName(explode('-', $GLOBALS['prefs']['pass_blacklist_file']));
- }
- }
- /**
- * Formatts a blacklist file name into a human readable description.
- *
- * @param $NameArray array of blacklist file specifycations
- *
- * @return string
- *
- */
- private function readableBlackName($NameArray)
- {
- $readable = 'Num & Let: ' . $NameArray['0'];
- $readable .= ', Special: ' . $NameArray['1'];
- $readable .= ', Min Len: ' . $NameArray['2'];
- $readable .= ', Custom: ' . $NameArray['3'];
- $readable .= ', Word Count: ' . $NameArray['4'];
- return $readable;
- }
- /**
- *
- * populates the password blacklist database table with the contents of a file of passwords.
- *
- * @param $filename string the name & path of the saved password
- */
- public function loadBlacklist($filename)
- {
- if (is_readable($filename)) {
- $passwords = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- $passwords = array_map('strtolower', $passwords);
- $passwords = array_map('trim', $passwords);
- $passwords = array_unique($passwords);
- $passwords = array_map('addslashes', $passwords);
- $passwords = "('" . implode("'),('", $passwords) . "')";
- $tikiDb = new TikiDb_Bridge();
- $query = "TRUNCATE TABLE tiki_password_blacklist";
- $tikiDb->query($query, []);
- $query = "INSERT INTO tiki_password_blacklist (password) VALUES $passwords";
- $tikiDb->query($query);
- } else {
- Feedback::error(tr('Unable to Populate Blacklist: File "%0" does not exist or is not readable.', $filename));
- }
- }
- /**
- * Obtains blacklists available, and returns one according to which one is best suited to current settings.
- * This function may only be called when values being updated, as it relies on the $_POST vars differing from
- * saved settings
- *
- * @var $file[0] bool chracter & number
- * @var $file[1] bool special character
- * @var $file[2] int minimum number of characters
- * @var $file[3] bool is user generated
- * @var $file[4] int number of passwords (limit)
- *
- * @param $pass_chr_num string the post var being updated
- * @param $pass_chr_num string the post var beign updated
- * @param $length string length value being updated
- *
- *
- * @return array|bool the file name (without extension) that is best suited to govern the blacklist, or false on no suitable files.
- */
- public function selectBestBlacklist($pass_chr_num, $pass_chr_special, $length)
- {
- $fileIndex = $this->genIndexedBlacks(false);
- $bestFile = false;
- $chrnum = false;
- $special = false;
- if ($pass_chr_num == 'on') {
- $chrnum = true;
- }
- if ($pass_chr_special == 'on') {
- $special = true;
- }
- foreach ($fileIndex as $file) {
- if (
- $file[0] == $chrnum && // first qualify the options
- $file[1] == $special &&
- $file[2] <= $length
- ) {
- $count = 2;
- while ($count < 5) { // then pick the best option
- if ($file[$count] >= $bestFile[$count]) {
- if ($file[$count] > $bestFile[$count]) {
- $bestFile = $file;
- }
- $count++;
- } else {
- $count = 5;
- }
- }
- }
- }
- return $bestFile;
- }
- /**
- * reads available password list files from disk and returns a sorted array of files
- *
- * @param $returnFormatted bool if false, will return a human readable array, if false, will return the same array with only numbers.
- *
- * @return array
- */
- public function genIndexedBlacks($returnFormatted = true)
- {
- $blacklist_options = array_diff(scandir(__DIR__ . '/../pass_blacklists'), ['..', '.', 'index.php', '.htaccess', '.svn', '.DS_Store', 'readme.txt']);
- if (is_dir('storage/pass_blacklists')) {
- $blacklist_options = array_merge($blacklist_options, array_diff(scandir(__DIR__ . '/../../storage/pass_blacklists'), ['..', '.', 'index.php', '.htaccess', '.svn', '.DS_Store', 'readme.txt']));
- }
- sort($blacklist_options);
- $fileindex = [];
- foreach ($blacklist_options as $blacklist_file) {
- $blacklist_file = substr($blacklist_file, 0, -4);
- $fileindex[$blacklist_file] = explode('-', $blacklist_file);
- if ($returnFormatted) {
- $fileindex[$blacklist_file] = $this->readableBlackName($fileindex[$blacklist_file]);
- }
- }
- return $fileindex;
- }
- }