From fa6856eb2abf6290ad71a01c0f07f6d83703232e Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 8 Dec 2014 13:18:33 +0900 Subject: [PATCH] Update to core login: password set/lock, start add PDO::Pgsql The login class and edit interface has added lock/strict login and a basic layout add for forced change password in X days (not yet implemented) Also start adding pdo interface wrapper class for pgsql --- 4dev/database/table/edit_user.sql | 7 + www/admin/edit_base.inc | 1 + www/admin/table_arrays/array_edit_users.inc | 20 +- www/configs/config.inc | 9 + www/libs/Class.Basic.inc | 4 +- www/libs/Class.Form.Generate.inc | 11 +- www/libs/db_pgsql.inc | 14 +- www/libs/db_pgsql_pdo.inc | 358 ++++++++++++++++++++ 8 files changed, 411 insertions(+), 13 deletions(-) create mode 100644 www/libs/db_pgsql_pdo.inc diff --git a/4dev/database/table/edit_user.sql b/4dev/database/table/edit_user.sql index d79c0434..6a31a89c 100644 --- a/4dev/database/table/edit_user.sql +++ b/4dev/database/table/edit_user.sql @@ -21,6 +21,13 @@ CREATE TABLE edit_user ( edit_group_id INT NOT NULL, edit_scheme_id INT, edit_access_right_id INT NOT NULL, + login_error_count INT, + login_error_date_last TIMESTAMP WTIHOUT TIME ZONE, + login_error_date_first TIMESTAMP WTIHOUT TIME ZONE, + strict SMALLINT DEFAULT 0, + locked SMALLINT DEFAULT 0, + password_change_date TIMESTAMP WITHOUT TIME ZONE, -- only when password is first set or changed + password_change_interval INTERVAL, -- null if no change is needed, or d/m/y time interval FOREIGN KEY (edit_language_id) REFERENCES edit_language (edit_language_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE, diff --git a/www/admin/edit_base.inc b/www/admin/edit_base.inc index 78b84876..a339f01e 100644 --- a/www/admin/edit_base.inc +++ b/www/admin/edit_base.inc @@ -228,6 +228,7 @@ $elements[] = $form->form_create_element("enabled"); $elements[] = $form->form_create_element("username"); $elements[] = $form->form_create_element("password"); + $elements[] = $form->form_create_element("password_change_interval"); $elements[] = $form->form_create_element("email"); $elements[] = $form->form_create_element("edit_group_id"); $elements[] = $form->form_create_element("edit_access_right_id"); diff --git a/www/admin/table_arrays/array_edit_users.inc b/www/admin/table_arrays/array_edit_users.inc index a581257b..c9b86a9a 100644 --- a/www/admin/table_arrays/array_edit_users.inc +++ b/www/admin/table_arrays/array_edit_users.inc @@ -11,7 +11,7 @@ "value" => $GLOBALS["username"], "output_name" => "Username", "mandatory" => 1, - "error_check" => "unique|alphanumeric", + "error_check" => "unique|alphanumericextended", "type" => "text" ), "password" => array ( @@ -20,8 +20,24 @@ "CONFIRM_value" => $GLOBALS["CONFIRM_password"], "output_name" => "Password", "mandatory" => 1, - "type" => "password" // later has to be password for encryption in database + "type" => "password", // later has to be password for encryption in database + 'update' => array ( // connected field updates, and update data + 'password_change_date' => array ( // db row to update + 'type' => 'date', // type of field (int/text/date/etc) + 'value' => 'NOW()' // value [todo: complex reference + ) + ) ), + // password date when first insert and password is set, needs special field with connection to password + 'password_change_interval' => array ( + 'value' => $GLOBALS['password_change_interval'], + 'output_name' => 'Password change interval', + 'error_check' => 'intervalshort', // can be any date length format. n Y/M/D [not H/M/S], only one set, no combination + 'type' => 'text', + 'size' => 5, // make it 5 chars long + 'length' => 5 + ), + // password reset force interval, if set, user needs to reset password after X time period "enabled" => array ( "value" => $GLOBALS["enabled"], "output_name" => "Enabled", diff --git a/www/configs/config.inc b/www/configs/config.inc index 0b6f9233..ca16d322 100644 --- a/www/configs/config.inc +++ b/www/configs/config.inc @@ -224,6 +224,15 @@ } } + // turn off debug if debug flag is OFF + if (DEBUG == false) + { + $ECHO_ALL = 0; + $DEBUG_ALL = 0; + $PRINT_ALL = 0; + $DB_DEBUG = 0; + } + // any other global definitons here // DEFINE('SOME_ID', ); diff --git a/www/libs/Class.Basic.inc b/www/libs/Class.Basic.inc index 90393eb5..6e99e453 100644 --- a/www/libs/Class.Basic.inc +++ b/www/libs/Class.Basic.inc @@ -805,10 +805,10 @@ // RETURN host name // DESCRIPTION // get the host name without the port as given by the SELF var - public static function get_host_name() + public function get_host_name() { list($host_name, $port) = explode(":", $_SERVER['HTTP_HOST']); - $self->host_port = $port; + $this->host_port = $port; return $host_name; } diff --git a/www/libs/Class.Form.Generate.inc b/www/libs/Class.Form.Generate.inc index 1a509d87..1cdb335a 100644 --- a/www/libs/Class.Form.Generate.inc +++ b/www/libs/Class.Form.Generate.inc @@ -894,6 +894,9 @@ break; case "datetime": // YYYY-MM-DD HH:MM[:SS] break; + case "intervalshort": // ony interval n [Y/M/D] only + if (preg_match("/^\d{1,3}\ ?[YMDymd]{1}$/", $this->table_array[$key]['value'])) + $this->msg .= sprintf($this->l->__('Please enter a valid time interval in the format Y|M|D for the %s Field!
'), $this->table[$key]['output_name']); case "email": if (!preg_match("/$this->email_regex/", $this->table_array[$key]["value"])) $this->msg .= sprintf($this->l->__("Please enter a valid E-Mail Address for the %s Field!
"), $this->table_array[$key]["output_name"]); @@ -914,7 +917,13 @@ case "alphanumeric": //$this->debug('edit', 'IN Alphanumeric'); if (!preg_match("/^[0-9A-Za-z_-]+$/", $this->table_array[$key]["value"])) - $this->msg .= sprintf($this->l->__("Please enter a valid alphanumeric (Numbers and Letters only, no spaces) value for the %s Field!
"), $this->table_array[$key]["output_name"]); + $this->msg .= sprintf($this->l->__("Please enter a valid alphanumeric (Numbers and Letters only also - and _, no spaces) value for the %s Field!
"), $this->table_array[$key]["output_name"]); + break; + // this one also allows @ and . + case "alphanumericextended": +//$this->debug('edit', 'IN Alphanumeric'); + if (!preg_match("/^[0-9A-Za-z_-@\.]+$/", $this->table_array[$key]["value"])) + $this->msg .= sprintf($this->l->__("Please enter a valid alphanumeric extended (Numbers, Letters, -, _, @ and . only, no spaces) value for the %s Field!
"), $this->table_array[$key]["output_name"]); break; case "password": // password can only be alphanumeric + special chars diff --git a/www/libs/db_pgsql.inc b/www/libs/db_pgsql.inc index 61813724..3b5ba3c7 100644 --- a/www/libs/db_pgsql.inc +++ b/www/libs/db_pgsql.inc @@ -3,7 +3,7 @@ * AUTHOR: Clemens "Gullevek" Schwaighofer (www.gullevek.org) * CREATED: 2003/04/09 * SHORT DESCRIPTION: - * pgsq; wrapper calls + * pgsql wrapper calls * HISTORY: * 2008/04/16 (cs) wrapper for pg escape string * 2007/01/11 (cs) add prepare/execute for postgres @@ -96,16 +96,14 @@ } // METHOD: _db_close - // PARAMS: optional database handler + // PARAMS: none // RETURN: none // DESC : wrapper for pg_close - public function _db_close($dbh = '') + public function _db_close() { - if (!$dbh) - $dbh = $this->dbh; - if (is_resource($dbh)) - if (@pg_connection_status($dbh) === PGSQL_CONNECTION_OK) - @pg_close($dbh); + if (is_resource($this->dbh)) + if (@pg_connection_status($this->dbh) === PGSQL_CONNECTION_OK) + @pg_close($this->dbh); } // METHOD: _db_prepare diff --git a/www/libs/db_pgsql_pdo.inc b/www/libs/db_pgsql_pdo.inc new file mode 100644 index 00000000..4ac014ad --- /dev/null +++ b/www/libs/db_pgsql_pdo.inc @@ -0,0 +1,358 @@ +last_error_query) + return true; + else + return false; + } + + // METHOD: _db_query + // PARAMS: query + // RETURN: query result + // DESC : wrapper for gp_query, catches error and stores it in class var + public function _db_query($query) + { + $this->last_error_query = ''; +/* // read out the query status and save the query if needed + $result = @pg_query($this->dbh, $query); + if (!$result) + $this->last_error_query = $query; */ + return $result; + } + + // METHOD: _db_send_query + // PARAMS: query + // RETURN: true/false if query was sent successful + // DESC : sends an async query to the server + public function _db_send_query($query) + { +// return @pg_send_query($this->dbh, $query); + } + + // METHOD: _db_get_result + // PARAMS: none + // RETURN: resource handler + // DESC : wrapper for pg_get_result + public function _db_get_result() + { + $this->last_error_query = ''; +/* $result = pg_get_result($this->dbh); + if ($error = pg_result_error($result)) + $this->last_error_query = $error; */ + return $result; + } + + // METHOD: _db_close + // PARAMS: none + // RETURN: none + // DESC : wrapper for pg_close + public function _db_close() + { + $this->cursor->closeCursor; + $this->cursor = null; + $this->dbh = null; + } + + // METHOD: _db_prepare + // PARAMS: prepare name, query + // RETURN: prepared statement handler + // DESC : wrapper for pg_prepare + public function _db_prepare($name, $query) + { +// return @pg_prepare($this->dbh, $name, $query); + } + + // METHOD: _db_execute + // PARAMS: prepare name, data for query + // RETURN: returns status + // DESC : wrapper for pg_execute for running a prepared statement + public function _db_execute($name, $data) + { +// return @pg_execute($this->dbh, $name, $data); + } + + // METHOD: _db_num_rows + // PARAMS: cursor + // RETURN: rows + // DESC : wrapper for pg_num_rows + public function _db_num_rows($cursor) + { +// return pg_num_rows($cursor); + } + + // METHOD: _db_num_fields + // PARAMS: cursor + // RETURN: number for fields in query + // DESC : wrapper for pg_num_fields + public function _db_num_fields($cursor) + { +// return pg_num_fields($cursor); + } + + // METHOD: _db_field_name + // PARAMS: cursor, field position + // RETURN: name of field + // DESC : wrapper for pg_field_name + public function _db_field_name($cursor, $i) + { +// return pg_field_name($cursor, $i); + } + + // METHOD: _db_fetch_array + // PARAMS: cursor + // RETURN: row + // DESC : wrapper for pg_fetch_array + public function _db_fetch_array($cursor) + { +// return pg_fetch_array($cursor); + } + + // METHOD: _db_affected_ros + // PARAMS: cursor + // RETURN: number for rows + // DESC : wrapper for pg_affected_rows + public function _db_affected_rows($cursor) + { +// return pg_affected_rows($cursor); + } + + // METHOD: _db_insert_id + // PARAMS: query, primary key name + // RETURN: last insert primary key + // DESC : reads the last inserted primary key for the query + // if ther is no pk_name tries to auto built it from the table name + // this only works if db schema is after "no plural names. and pk name is table name + _id + // detects schema prefix in table name + public function _db_insert_id($query, $pk_name) + { + // only if an insert has been done + if (preg_match("/^insert /i", $query)) + { + $schema = ''; + // get table name from insert + $array = explode(' ', $query); + $_table = $array[2]; + // if there is a dot inside, we need to split + if (strstr($_table, '.')) + list($schema, $table) = explode('.', $_table); + else + $table = $_table; + // no PK name given at all + if (!$pk_name) + { + // if name is plurar, make it singular +// if (preg_match("/.*s$/i", $table)) +// $table = substr($table, 0, -1); + // set pk_name to "id" + $pk_name = $table."_id"; + } + $seq = (($schema) ? $schema.'.' : '').$table."_".$pk_name."_seq"; + $q = "SELECT CURRVAL('$seq') AS insert_id"; +// $this->currval_query = $q; + // I have to do manually or I overwrite the original insert internal vars ... + if ($q = $this->_db_query($q)) + { + list($id) = pg_fetch_array($q); + } + else + { + $id = array(-1, $q); + } + return $id; + } + } + + // METHOD: _db_primary_key + // PARAMS: table and optional schema + // RETURN: primary key name OR false if not possible + // DESC : queries database for the primary key name to this table in the selected schema + public function _db_primary_key($table, $schema = '') + { + if ($table) + { + // check if schema set is different from schema given, only needed if schema is not empty + $table_prefix = ''; + if ($schema) + { + $q = "SHOW search_path"; + $cursor = $this->_db_query($q); + $search_path = $this->_db_fetch_array($cursor)['search_path']; + if ($search_path != $schema) + { + $table_prefix = $schema.'.'; + } + } + // read from table the PK name + // faster primary key get + $q = "SELECT pg_attribute.attname AS column_name, format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type "; + $q .= "FROM pg_index, pg_class, pg_attribute "; + if ($schema) + $q .= ", pg_namespace "; + $q .= "WHERE "; + // regclass translates the OID to the name + $q .= "pg_class.oid = '".$table_prefix.$table."'::regclass AND "; + $q .= "indrelid = pg_class.oid AND "; + if ($schema) + { + $q .= "nspname = '".$schema."' AND "; + $q .= "pg_class.relnamespace = pg_namespace.oid AND "; + } + $q .= "pg_attribute.attrelid = pg_class.oid AND "; + $q .= "pg_attribute.attnum = any(pg_index.indkey) "; + $q .= "AND indisprimary"; + $cursor = $this->_db_query($q); + if ($cursor) + return $this->_db_fetch_array($cursor)['column_name']; + else + return false; + } + else + { + return false; + } + } + + // METHOD: _db_connect + // PARAMS: host name, user name, password, database name, optional port (defaults to default postgres port), optional ssl (default allow) + // RETURN: database handler + // DESC : wrapper for pg_connect, writes out failure to screen if error occurs (hidden var) + public function _db_connect($db_host, $db_user, $db_pass, $db_name, $db_port = 5432, $db_ssl = 'allow') + { + // to avoid empty db_port + if (!$db_port) + { + $db_port = 5432; + } +/* $this->dbh = @pg_connect("host=".$db_host." port=".$db_port." user=".$db_user." password=".$db_pass." dbname=".$db_name." sslmode=".$db_ssl); + if (!$this->dbh) + { + die(""); + } */ + return $this->dbh; + } + + // METHOD: _db_print_error + // PARAMS: database handler, cursor + // RETURN: error string (HTML) + // DESC : reads the last error for this cursor + public function _db_print_error($cursor = '') + { +/* // run the query again for the error result here + if (!$cursor && $this->last_error_query) + { + pg_send_query($this->dbh, $this->last_error_query); + $this->last_error_query = ''; + $cursor = pg_get_result($this->dbh); + } + if (pg_result_error($cursor)) + return "-PostgreSQL-Error-> ".pg_result_error($cursor)."
"; */ + } + + // METHOD: _db_meta_data + // PARAMS: table name + // RETURN: array with table data + // DESC : wrapper for pg_emta_data + public function _db_meta_data($table) + { +// return @pg_meta_data($this->dbh, $table); + } + + // METHOD: _db_escape_string + // PARAMS: string + // RETURN: escaped string for postgres + // DESC : wrapper for pg_escape_string + public function _db_escape_string($string) + { +// return pg_escape_string($this->dbh, $string); + } + + // METHOD: _db_escape_bytea + // PARAMS: string + // RETURN: escape bytes for postgres + // DESC : wrapper for pg_escape_bytea + public function _db_escape_bytea($bytea) + { +// return pg_escape_bytea($this->dbh, $bytea); + } + + // METHOD: _db_connection_busy + // PARAMS: none + // RETURN: true/false for busy connection + // DESC : wrapper for pg_connection_busy + public function _db_connection_busy() + { +// return pg_connection_busy($this->dbh); + } + + // METHOD: _db_version + // PARAMS: none + // RETURN: databse version + // DESC : wrapper for pg_version + public function _db_version() + { + // array has client, protocol, server + // we just need the server + $v = pg_version($this->dbh); + return $v['server']; + } + + // METHOD: _db_array_parse + // PARAMS: input text, output array [needed] + // [internal] limit: are we at the end of the parse + // [internal] offset: shift for {} + // RETURN: array with the elements + // DESC : postgresql array to php array + public function _db_array_parse($text, &$output, $limit = false, $offset = 1) + { + if (false === $limit) + { + $limit = strlen($text) - 1; + $output = array(); + } + if ('{}' != $text) + do + { + if ('{' != $text{$offset}) + { + preg_match("/(\\{?\"([^\"\\\\]|\\\\.)*\"|[^,{}]+)+([,}]+)/", $text, $match, 0, $offset); + $offset += strlen($match[0]); + $output[] = ('"' != $match[1]{0} ? $match[1] : stripcslashes(substr($match[1], 1, -1))); + if ('},' == $match[3]) + return $offset; + } + else + $offset = pg_array_parse($text, $output[], $limit, $offset + 1); + } + while ($limit > $offset); + return $output; + } + } +?>