diff --git a/ajax/settings.php b/ajax/settings.php --- a/ajax/settings.php +++ b/ajax/settings.php @@ -1,66 +1,83 @@ getL10N('user_sql'); -$params = array('sql_host', 'sql_user', 'sql_database', 'sql_password', - 'sql_table', 'sql_column_username', 'sql_column_password', 'sql_type', - 'sql_column_active', 'strip_domain', 'default_domain', 'crypt_type', - 'sql_column_displayname', 'domain_settings', 'map_array', 'domain_array', - 'allow_password_change', 'sql_column_active_invert', 'sql_column_email', - 'mail_sync_mode'); +$params = $helper -> getParameterArray(); -if(isset($_POST['appname']) && $_POST['appname'] === "user_sql") +if(isset($_POST['appname']) && ($_POST['appname'] === 'user_sql') && isset($_POST['function']) && isset($_POST['domain'])) { - foreach($params as $param) + $domain = $_POST['domain']; + switch($_POST['function']) { - if(isset($_POST[$param])) - { - if($param === 'strip_domain') - { - OCP\Config::setAppValue('user_sql', 'strip_domain', true); - } - elseif($param === 'allow_password_change') - { - OCP\Config::setAppValue('user_sql', 'allow_password_change', true); - } - elseif($param === 'sql_column_active_invert') + case 'saveSettings': + foreach($params as $param) + { + if(isset($_POST[$param])) + { + if($param === 'set_strip_domain') + { + \OC::$server->getConfig()->setAppValue('user_sql', 'set_strip_domain_'.$domain, 'true'); + } + elseif($param === 'set_allow_pwchange') + { + \OC::$server->getConfig()->setAppValue('user_sql', 'set_allow_pwchange_'.$domain, 'true'); + } + elseif($param === 'set_active_invert') + { + \OC::$server->getConfig()->setAppValue('user_sql', 'set_active_invert_'.$domain, 'true'); + } + else + { + \OC::$server->getConfig()->setAppValue('user_sql', $param.'_'.$domain, $_POST[$param]); + } + } else + { + if($param === 'set_strip_domain') + { + \OC::$server->getConfig()->setAppValue('user_sql', 'set_strip_domain_'.$domain, 'false'); + } + elseif($param === 'set_allow_pwchange') + { + \OC::$server->getConfig()->setAppValue('user_sql', 'set_allow_pwchange_'.$domain, 'false'); + } + elseif($param === 'set_active_invert') + { + \OC::$server->getConfig()->setAppValue('user_sql', 'set_active_invert_'.$domain, 'false'); + } + } + } + break; + + case 'loadSettingsForDomain': + $retArr = array(); + foreach($params as $param) { - OCP\Config::setAppValue('user_sql', 'sql_column_active_invert', true); - } - else - { - OCP\Config::setAppValue('user_sql', $param, $_POST[$param]); + $retArr[$param] = \OC::$server->getConfig()->getAppValue('user_sql', $param.'_'.$domain, ''); } - } else - { - if($param === 'strip_domain') - { - OCP\Config::setAppValue('user_sql', 'strip_domain', false); - } - elseif($param === 'allow_password_change') - { - OCP\Config::setAppValue('user_sql', 'allow_password_change', false); - } - elseif($param === 'sql_column_active_invert') - { - OCP\Config::setAppValue('user_sql', 'sql_column_active_invert', false); - } - } + \OCP\JSON::success(array('settings' => $retArr)); + return true; + break; } + } else { - \OCP\JSON::error(array("data" => array("message" => $l -> t("Not submitted for us.")))); + \OCP\JSON::error(array('data' => array('message' => $l -> t('Not submitted for us.')))); return false; } -OCP\JSON::success(array('data' => array('message' => $l -> t('Application settings successfully stored.')))); +\OCP\JSON::success(array('data' => array('message' => $l -> t('Application settings successfully stored.')))); return true; diff --git a/appinfo/app.php b/appinfo/app.php --- a/appinfo/app.php +++ b/appinfo/app.php @@ -1,50 +1,42 @@ +* @copyright 2012-2015 Andreas Böhler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * */ require_once('apps/user_sql/user_sql.php'); - \OCP\App::registerAdmin('user_sql','settings'); -// define IMAP_DEFAULTs -define('OC_USER_BACKEND_SQL_DEFAULT_HOST', 'localhost'); -define('OC_USER_BACKEND_SQL_DEFAULT_USER', 'mail_admin'); -define('OC_USER_BACKEND_SQL_DEFAULT_DB', 'postfixadmin'); -define('OC_USER_BACKEND_SQL_DEFAULT_PASSWORD', 'password'); -define('OC_USER_BACKEND_SQL_DEFAULT_TABLE', 'users'); -define('OC_USER_BACKEND_SQL_DEFAULT_PW_COLUMN', 'password'); -define('OC_USER_BACKEND_SQL_DEFAULT_USER_COLUMN', 'username'); -define('OC_USER_BACKEND_SQL_DEFAULT_DRIVER', 'mysql'); +$backend = new \OCA\user_sql\OC_USER_SQL; + // register user backend -OC_User::registerBackend('SQL'); -OC_User::useBackend('SQL'); +OC_User::registerBackend($backend); +OC_User::useBackend($backend); // add settings page to navigation $entry = array( 'id' => "user_sql_settings", 'order'=>1, 'href' => OC_Helper::linkTo( "user_sql", "settings.php" ), 'name' => 'SQL' ); diff --git a/appinfo/info.xml b/appinfo/info.xml --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -1,14 +1,16 @@ user_sql SQL user backend Authenticate Users by SQL AGPL Andreas Boehler <dev (at) aboehler (dot) at > - 5.0 + 1.99 + 8.1 false + user_sql diff --git a/appinfo/update.php b/appinfo/update.php new file mode 100644 --- /dev/null +++ b/appinfo/update.php @@ -0,0 +1,71 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +$installedVersion = \OC::$server->getConfig()->getAppValue('user_sql', 'installed_version'); + +$params = array('sql_host' => 'sql_hostname', + 'sql_user' => 'sql_username', + 'sql_database' => 'sql_database', + 'sql_password' => 'sql_password', + 'sql_table' => 'sql_table', + 'sql_column_username' => 'col_username', + 'sql_column_password' => 'col_password', + 'sql_type' => 'sql_driver', + 'sql_column_active' => 'col_active', + 'strip_domain' => 'set_strip_domain', + 'default_domain' => 'set_default_domain', + 'crypt_type' => 'set_crypt_type', + 'sql_column_displayname' => 'col_displayname', + 'allow_password_change' => 'set_allow_pwchange', + 'sql_column_active_invert' => 'set_active_invert', + 'sql_column_email' => 'col_email', + 'mail_sync_mode' => 'set_mail_sync_mode' + ); + +$delParams = array('domain_settings', + 'map_array', + 'domain_array' + ); + +if(version_compare($installedVersion, '1.99', '<')) +{ + foreach($params as $oldPar => $newPar) + { + $val = \OC::$server->getConfig()->getAppValue('user_sql', $oldPar); + if(($oldPar === 'strip_domain') || ($oldPar === 'allow_password_change') || ($oldPar === 'sql_column_active_invert')) + { + if($val) + $val = 'true'; + else + $val = 'false'; + } + if($val) + \OC::$server->getConfig()->setAppValue('user_sql', $newPar.'_default', $val); + \OC::$server->getConfig()->deleteAppValue('user_sql', $oldPar); + } + + foreach($delParams as $param) + { + \OC::$server->getConfig()->deleteAppValue('user_sql', $param); + } +} diff --git a/appinfo/version b/appinfo/version deleted file mode 100644 --- a/appinfo/version +++ /dev/null @@ -1,1 +0,0 @@ -1.5 diff --git a/css/settings.css b/css/settings.css --- a/css/settings.css +++ b/css/settings.css @@ -1,14 +1,14 @@ .statusmessage { background-color: #DDDDFF; } .errormessage { background-color: #FFDDDD; } .successmessage { background-color: #DDFFDD; } .statusmessage,.errormessage,.successmessage{ display:none; - padding: 1; + padding: 1px; } diff --git a/js/settings.js b/js/settings.js --- a/js/settings.js +++ b/js/settings.js @@ -1,158 +1,135 @@ // declare namespace var user_sql = user_sql || { }; -user_sql.adminSettingsCheckRadio = function() -{ - if($('#domain_none').attr("checked") == "checked") - { - $('#default_domain').attr("disabled", true); - $('#inputServerDomain').attr("disabled", true); - $('#inputMapDomain').attr("disabled", true); - $('#domainAddMap').attr("disabled", true); - } else if($('#domain_server').attr("checked") == "checked") - { - $('#default_domain').attr("disabled", true); - $('#inputServerDomain').attr("disabled", true); - $('#inputMapDomain').attr("disabled", true); - $('#domainAddMap').attr("disabled", true); - } else if($('#domain_mapping').attr("checked") == "checked") - { - $('#default_domain').attr("disabled", true); - $('#inputServerDomain').removeAttr("disabled"); - $('#inputMapDomain').removeAttr("disabled"); - $('#domainAddMap').removeAttr("disabled"); - } else if($('#domain_default').attr("checked") == "checked") - { - $('#default_domain').removeAttr("disabled"); - $('#inputServerDomain').attr("disabled", true); - $('#inputMapDomain').attr("disabled", true); - $('#domainAddMap').attr("disabled", true); - } -}; /** * init admin settings view */ user_sql.adminSettingsUI = function() { if($('#sql').length > 0) { // enable tabs on settings page $('#sql').tabs(); $('#sqlSubmit').click(function(event) { event.preventDefault(); var self = $(this); var post = $('#sqlForm').serializeArray(); - var domainArr = new Array(); - var mapArr = new Array(); - $('#domain_map_entries tr').each(function() - { - var d = $(this).find("td:first").html(); - var m = $(this).find("td").eq(1).html(); - if(d != undefined && m != undefined) - { - mapArr.push(m); - domainArr.push(d); - } + var domain = $('#sql_domain_chooser option:selected').val(); + + post.push({ + name: 'function', + value: 'saveSettings' }); - post.push( - { - name : 'map_array', - value : mapArr + + post.push({ + name: 'domain', + value: domain }); - post.push( - { - name : 'domain_array', - value : domainArr - }); + $('#sql_update_message').show(); $('#sql_success_message').hide(); $('#sql_error_message').hide(); // Ajax foobar $.post(OC.filePath('user_sql', 'ajax', 'settings.php'), post, function(data) { $('#sql_update_message').hide(); if(data.status == 'success') { $('#sql_success_message').html(data.data.message); $('#sql_success_message').show(); window.setTimeout(function() { $('#sql_success_message').hide(); }, 10000); } else { $('#sql_error_message').html(data.data.message); $('#sql_error_message').show(); } }, 'json'); return false; }); - - $('#domain_none').click(function(event) - { - user_sql.adminSettingsCheckRadio(); - }); - - $('#domain_server').click(function(event) - { - user_sql.adminSettingsCheckRadio(); - }); - - $('#domain_mapping').click(function(event) - { - user_sql.adminSettingsCheckRadio(); - }); - - $('#domain_default').click(function(event) - { - user_sql.adminSettingsCheckRadio(); + + $('#sql_domain_chooser').change(function() { + user_sql.loadDomainSettings($('#sql_domain_chooser option:selected').val()); }); - $('#domainAddMap').click(function(event) + + } +}; + +user_sql.loadDomainSettings = function(domain) +{ + $('#sql_loading_message').show(); + var post = [ + { + name: 'appname', + value: 'user_sql' + }, { - event.preventDefault(); - var newDomain = $('#inputServerDomain').val(); - var newMap = $('#inputMapDomain').val(); - $('#domain_map_entries > tbody:last').append('' + newDomain + '' + newMap + 'delete'); - $('#inputServerDomain').val(""); - $('#inputMapDomain').val(""); - $("#domain_map_entries .deleteLink").on("click", function() + name: 'function', + value: 'loadSettingsForDomain' + }, + { + name: 'domain', + value: domain + } + ]; + $.post(OC.filePath('user_sql', 'ajax', 'settings.php'), post, function(data) + { + $('#sql_loading_message').hide(); + if(data.status == 'success') { - var tr = $(this).closest('tr'); - tr.css("background-color", "#FF3700"); - tr.fadeOut(400, function() + for(key in data.settings) { - tr.remove(); - }); - return false; - }); - }); - } + if(key == 'set_strip_domain') + { + if(data.settings[key] == 'true') + $('#' + key).prop('checked', true); + else + $('#' + key).prop('checked', false); + } + else if(key == 'set_allow_pwchange') + { + if(data.settings[key] == 'true') + $('#' + key).prop('checked', true); + else + $('#' + key).prop('checked', false); + } + else if(key == 'set_active_invert') + { + if(data.settings[key] == 'true') + $('#' + key).prop('checked', true); + else + $('#' + key).prop('checked', false); + } + else + { + $('#' + key).val(data.settings[key]); + } + } + } + else + { + $('#sql_error_message').html(data.data.message); + $('#sql_error_message').show(); + } + } + ); }; $(document).ready(function() { if($('#sql')) { user_sql.adminSettingsUI(); - user_sql.adminSettingsCheckRadio(); - - $("#domain_map_entries .deleteLink").on("click", function() - { - var tr = $(this).closest('tr'); - tr.css("background-color", "#FF3700"); - tr.fadeOut(400, function() - { - tr.remove(); - }); - return false; - }); + user_sql.loadDomainSettings($('#sql_domain_chooser option:selected').val()); } }); diff --git a/lib/helper.php b/lib/helper.php new file mode 100644 --- /dev/null +++ b/lib/helper.php @@ -0,0 +1,211 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +namespace OCA\user_sql\lib; + +class Helper { + + protected $db; + protected $db_conn; + protected $settings; + + public function __construct() + { + $this->db_conn = false; + } + + public function getParameterArray() + { + $params = array( + 'sql_hostname', + 'sql_username', + 'sql_password', + 'sql_database', + 'sql_table', + 'sql_driver', + 'col_username', + 'col_password', + 'col_active', + 'col_displayname', + 'col_email', + 'set_active_invert', + 'set_allow_pwchange', + 'set_default_domain', + 'set_strip_domain', + 'set_crypt_type', + 'set_mail_sync_mode' + ); + + return $params; + } + + public function loadSettingsForDomain($domain) + { + \OCP\Util::writeLog('OC_USER_SQL', "Trying to load settings for domain: " . $domain, \OCP\Util::DEBUG); + $settings = array(); + $sql_host = \OC::$server->getConfig()->getAppValue('user_sql', 'sql_hostname_'.$domain, ''); + if($sql_host === '') + { + $domain = 'default'; + } + $params = $this -> getParameterArray(); + foreach($params as $param) + { + $settings[$param] = \OC::$server->getConfig()->getAppValue('user_sql', $param.'_'.$domain, ''); + } + \OCP\Util::writeLog('OC_USER_SQL', "Loaded settings for domain: " . $domain, \OCP\Util::DEBUG); + return $settings; + } + + public function runQuery($type, $params, $execOnly = false, $fetchArray = false, $limits = array()) + { + \OCP\Util::writeLog('OC_USER_SQL', "Entering runQuery for type: " . $type, \OCP\Util::DEBUG); + if(!$this -> db_conn) + return false; + + switch($type) + { + case 'getMail': + $query = "SELECT ".$this->settings['col_email']." FROM ".$this->settings['sql_table']." WHERE ".$this->settings['col_username']." = :uid"; + break; + + case 'setMail': + $query = "UPDATE ".$this->settings['sql_table']." SET ".$this->settings['col_email']." = :currMail WHERE ".$this->settings['col_username']." = :uid"; + break; + + case 'getPass': + $query = "SELECT ".$this->settings['col_password']." FROM ".$this->settings['sql_table']." WHERE ".$this->settings['col_username']." = :uid"; + if($this -> settings['col_active'] !== '') + $query .= " AND " .($this -> settings['set_active_invert'] === 'true' ? "NOT " : "" ) . $this -> settings['col_active']; + break; + + case 'setPass': + $query = "UPDATE ".$this->settings['sql_table']." SET ".$this->settings['col_password']." = :enc_password WHERE ".$this->settings['col_username'] ." = :uid"; + break; + + case 'getRedmineSalt': + $query = "SELECT salt FROM ".$this->settings['sql_table']." WHERE ".$this->settings['col_username'] ." = :uid;"; + break; + + case 'countUsers': + $query = "SELECT COUNT(*) FROM ".$this->settings['sql_table']; + if($this -> settings['col_active'] !== '') + $query .= " WHERE " .($this -> settings['set_active_invert'] === 'true' ? "NOT " : "" ) . $this -> settings['col_active']; + break; + + case 'getUsers': + $query = "SELECT ".$this->settings['col_username']." FROM ".$this->settings['sql_table']; + $query .= " WHERE ".$this->settings['col_username']." LIKE :search"; + if($this -> settings['col_active'] !== '') + $query .= " AND " .($this -> settings['set_active_invert'] === 'true' ? "NOT " : "" ) . $this -> settings['col_active']; + $query .= " ORDER BY ".$this->settings['col_username']; + break; + + case 'userExists': + $query = "SELECT ".$this->settings['col_username']." FROM ".$this->settings['sql_table']." WHERE ".$this->settings['col_username']." = :uid"; + if($this -> settings['col_active'] !== '') + $query .= " AND " .($this -> settings['set_active_invert'] === 'true' ? "NOT " : "" ) . $this -> settings['col_active']; + break; + + case 'getDisplayName': + $query = "SELECT ".$this->settings['col_displayname']." FROM ".$this->settings['sql_table']." WHERE ".$this->settings['col_username']." = :uid"; + if($this -> settings['col_active'] !== '') + $query .= " AND " .($this -> settings['set_active_invert'] === 'true' ? "NOT " : "" ) . $this -> settings['col_active']; + break; + + case 'mysqlEncryptSalt': + $query = "SELECT ENCRYPT(:pw, :salt);"; + break; + + case 'mysqlEncrypt': + $query = "SELECT ENCRYPT(:pw);"; + break; + + case 'mysqlPassword': + $query = "SELECT PASSWORD(:pw);"; + break; + } + + if(isset($limits['limit']) && $limits['limit'] !== null) + { + $limit = intval($limits['limit']); + $query .= " LIMIT ".$limit; + } + + if(isset($limits['offset']) && $limits['offset'] !== null) + { + $offset = intval($limits['offset']); + $query .= " OFFSET ".$offset; + } + + \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); + $result = $this -> db -> prepare($query); + foreach($params as $param => $value) + { + $result -> bindParam(":".$param, $value); + } + \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); + if(!$result -> execute()) + { + $err = $result -> errorInfo(); + \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); + return false; + } + if($execOnly === true) + { + return true; + } + \OCP\Util::writeLog('OC_USER_SQL', "Fetching result...", \OCP\Util::DEBUG); + if($fetchArray === true) + $row = $result -> fetchAll(); + else + $row = $result -> fetch(); + + if(!$row) + { + return false; + } + return $row; + } + + public function connectToDb($settings) + { + $this -> settings = $settings; + $dsn = $this -> settings['sql_driver'] . ":host=" . $this -> settings['sql_hostname'] . ";dbname=" . $this -> settings['sql_database']; + try + { + $this -> db = new \PDO($dsn, $this -> settings['sql_username'], $this -> settings['sql_password']); + $this -> db -> query("SET NAMES 'UTF8'"); + $this -> db_conn = true; + return true; + } + catch (\PDOException $e) + { + \OCP\Util::writeLog('OC_USER_SQL', 'Failed to connect to the database: ' . $e -> getMessage(), \OCP\Util::ERROR); + $this -> db_conn = false; + return false; + } + } + + +} \ No newline at end of file diff --git a/settings.php b/settings.php --- a/settings.php +++ b/settings.php @@ -1,66 +1,52 @@ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * */ -$params = array('sql_host', 'sql_user', 'sql_database', 'sql_password', 'sql_table', - 'sql_column_username', 'sql_column_password', 'sql_type', 'sql_column_active', - 'strip_domain', 'default_domain', 'crypt_type', 'sql_column_displayname', - 'domain_map', 'domain_settings', 'sql_column_active_invert', 'sql_column_email', - 'mail_sync_mode'); + +namespace OCA\user_sql; + +use OCA\user_sql\lib\Helper; -OCP\Util::addStyle('user_sql', 'settings'); -OCP\Util::addScript('user_sql', 'settings'); -OCP\User::checkAdminUser(); +$helper = new \OCA\user_sql\lib\Helper(); +$params = $helper -> getParameterArray(); +$settings = $helper -> loadSettingsForDomain('default'); + +\OCP\Util::addStyle('user_sql', 'settings'); +\OCP\Util::addScript('user_sql', 'settings'); +\OCP\User::checkAdminUser(); // fill template -$tmpl = new OCP\Template('user_sql', 'settings'); +$tmpl = new \OCP\Template('user_sql', 'settings'); foreach($params as $param) { - $value = htmlentities(OCP\Config::getAppValue('user_sql', $param, '')); + $value = htmlentities($settings[$param]); $tmpl -> assign($param, $value); } -// settings with default values -$tmpl -> assign('sql_host', OCP\Config::getAppValue('user_sql', 'sql_host', OC_USER_BACKEND_SQL_DEFAULT_HOST)); -$tmpl -> assign('sql_user', OCP\Config::getAppValue('user_sql', 'sql_user', OC_USER_BACKEND_SQL_DEFAULT_USER)); -$tmpl -> assign('sql_database', OCP\Config::getAppValue('user_sql', 'sql_database', OC_USER_BACKEND_SQL_DEFAULT_DB)); -$tmpl -> assign('sql_password', OCP\Config::getAppValue('user_sql', 'sql_password', OC_USER_BACKEND_SQL_DEFAULT_PASSWORD)); -$tmpl -> assign('sql_table', OCP\Config::getAppValue('user_sql', 'sql_table', OC_USER_BACKEND_SQL_DEFAULT_TABLE)); -$tmpl -> assign('sql_column_password', OCP\Config::getAppValue('user_sql', 'sql_column_password', OC_USER_BACKEND_SQL_DEFAULT_PW_COLUMN)); -$tmpl -> assign('sql_column_username', OCP\Config::getAppValue('user_sql', 'sql_column_username', OC_USER_BACKEND_SQL_DEFAULT_USER_COLUMN)); -$tmpl -> assign('sql_type', OCP\Config::getAppValue('user_sql', 'sql_type', OC_USER_BACKEND_SQL_DEFAULT_DRIVER)); -$tmpl -> assign('sql_column_active', OCP\Config::getAppValue('user_sql', 'sql_column_active', '')); -$tmpl -> assign('sql_column_email', OCP\Config::getAppValue('user_sql', 'sql_column_email', '')); -$tmpl -> assign('mail_sync_mode', OCP\Config::getAppValue('user_sql', 'mail_sync_mode', 'none')); -$tmpl -> assign('strip_domain', OCP\Config::getAppValue('user_sql', 'strip_domain', 0)); -$tmpl -> assign('default_domain', OCP\Config::getAppValue('user_sql', 'default_domain', '')); -$tmpl -> assign('crypt_type', OCP\Config::getAppValue('user_sql', 'crypt_type', 'mysql_encrypt')); -$tmpl -> assign('sql_column_displayname', OCP\Config::getAppValue('user_sql', 'sql_column_displayname', '')); -$tmpl -> assign('map_array', OCP\Config::getAppValue('user_sql', 'map_array', '')); -$tmpl -> assign('domain_array', OCP\Config::getAppValue('user_sql', 'domain_array', '')); -$tmpl -> assign('domain_settings', OCP\Config::getAppValue('user_sql', 'domain_settings', '')); -$tmpl -> assign('allow_password_change', OCP\Config::getAppValue('user_sql', 'allow_password_change', 0)); -$tmpl -> assign('sql_column_active_invert', OCP\Config::getAppValue('user_sql', 'sql_column_active_invert', 0)); +$trusted_domains = \OC::$server->getConfig()->getSystemValue('trusted_domains'); +$inserted = array('default'); +array_splice($trusted_domains, 0, 0, $inserted); +$tmpl -> assign('allowed_domains', array_unique($trusted_domains)); // workaround to detect OC version -$ocVersion = @reset(OCP\Util::getVersion()); +$ocVersion = @reset(\OCP\Util::getVersion()); $tmpl -> assign('ocVersion', $ocVersion); return $tmpl -> fetchPage(); diff --git a/templates/settings.php b/templates/settings.php --- a/templates/settings.php +++ b/templates/settings.php @@ -1,135 +1,108 @@ = 7 ? 'section' : 'personalblock'; ?> +
+

t('SQL')); ?>

-
+ -
- t('SQL'); ?> - +
+ + +
- + 'MySQL', 'pgsql' => 'PostgreSQL'); ?> - - - - - - - - -
-
-
- - - - - - - 'MD5', 'md5crypt' => 'MD5 Crypt', 'cleartext' => 'Cleartext', 'mysql_encrypt' => 'mySQL ENCRYPT()', 'system' => 'System (crypt)', 'mysql_password' => 'mySQL PASSWORD()', 'joomla' => 'Joomla MD5 Encryption', 'joomla2' => 'Joomla > 2.5.18 phpass', 'ssha256' => 'Salted SSHA256', 'redmine' => 'Redmine'); ?> - - - + + + + +
title="Allow changing passwords. Imposes a security risk as password salts are not recreated">
+
+
+ + + + + + + 'MD5', 'md5crypt' => 'MD5 Crypt', 'cleartext' => 'Cleartext', 'mysql_encrypt' => 'mySQL ENCRYPT()', 'system' => 'System (crypt)', 'mysql_password' => 'mySQL PASSWORD()', 'joomla' => 'Joomla MD5 Encryption', 'joomla2' => 'Joomla > 2.5.18 phpass', 'ssha256' => 'Salted SSHA256', 'redmine' => 'Redmine'); ?> + + + + - - + + 'No Synchronisation', 'initial' => 'Synchronise only once', 'forceoc' => 'ownCloud always wins', 'forcesql' => 'SQL always wins'); ?> - + +
title="Allow changing passwords. Imposes a security risk as password salts are not recreated">
title="Invert the logic of the active column (for blocked users in the SQL DB)" />
title="Strip Domain Part from Username when logging in and retrieving username lists">
-
- - - - -
- - - - -
>t('No Mapping') ?>
>t('Append Server Hostname') ?>
>t('Append Default') ?>
>t('Map Domains') ?> - - - - "; - } - ?> - -
" . htmlspecialchars($domains[$i]) . "" . htmlspecialchars($maps[$i]) . "delete
title="Strip Domain Part from Username when logging in and retrieving username lists">
-
- - - -
t('Saving...'); ?>
+ + + +
t('Saving...')); ?>
+
t('Loading...')); ?>
+
diff --git a/user_sql.php b/user_sql.php --- a/user_sql.php +++ b/user_sql.php @@ -1,871 +1,656 @@ * * credits go to Ed W for several SQL injection fixes and caching support * credits go to Frédéric France for providing Joomla support * credits go to Mark Jansenn for providing Joomla 2.5.18+ / 3.2.1+ support * credits go to Dominik Grothaus for providing SSHA256 support and fixing a few bugs + * credits go to Sören Eberhardt-Biermann for providing multi-host support * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * */ -class OC_USER_SQL extends OC_User_Backend implements \OCP\IUserBackend, \OCP\UserInterface +namespace OCA\user_sql; + +use \OCA\user_sql\lib\Helper; + +class OC_USER_SQL extends \OC_User_Backend implements \OCP\IUserBackend, \OCP\UserInterface { protected $cache; // cached settings - protected $sql_host; - protected $sql_username; - protected $sql_database; - protected $sql_password; - protected $sql_table; - protected $sql_column_username; - protected $sql_column_password; - protected $sql_column_active; - protected $sql_column_active_invert; - protected $sql_column_displayname; - protected $sql_column_email; - protected $mail_sync_mode; - protected $sql_type; - protected $db_conn; - protected $db; - protected $default_domain; - protected $strip_domain; - protected $crypt_type; - protected $domain_settings; - protected $domain_array; - protected $map_array; - protected $allow_password_change; + protected $settings; + protected $helper; protected $session_cache_name; public function __construct() { - $this -> db_conn = false; $memcache = \OC::$server->getMemCacheFactory(); if ( $memcache -> isAvailable()) { $this -> cache = $memcache -> create(); } - $this -> sql_host = OCP\Config::getAppValue('user_sql', 'sql_host', ''); - $this -> sql_username = OCP\Config::getAppValue('user_sql', 'sql_user', ''); - $this -> sql_database = OCP\Config::getAppValue('user_sql', 'sql_database', ''); - $this -> sql_password = OCP\Config::getAppValue('user_sql', 'sql_password', ''); - $this -> sql_table = OCP\Config::getAppValue('user_sql', 'sql_table', ''); - $this -> sql_column_username = OCP\Config::getAppValue('user_sql', 'sql_column_username', ''); - $this -> sql_column_password = OCP\Config::getAppValue('user_sql', 'sql_column_password', ''); - $this -> sql_column_displayname = OCP\Config::getAppValue('user_sql', 'sql_column_displayname', $this->sql_column_username); - $this -> sql_column_email = OCP\Config::getAppValue('user_sql', 'sql_column_email', ''); - $this -> sql_column_active = OCP\Config::getAppValue('user_sql', 'sql_column_active', ''); - $this -> sql_column_active_invert = OCP\Config::getAppValue('user_sql', 'sql_column_active_invert', 0); - $this -> sql_type = OCP\Config::getAppValue('user_sql', 'sql_type', ''); - $this -> default_domain = OCP\Config::getAppValue('user_sql', 'default_domain', ''); - $this -> strip_domain = OCP\Config::getAppValue('user_sql', 'strip_domain', 0); - $this -> allow_password_change = OCP\Config::getAppValue('user_sql', 'allow_password_change', 0); - $this -> crypt_type = OCP\Config::getAppValue('user_sql', 'crypt_type', 'md5crypt'); - $this -> domain_settings = OCP\Config::getAppValue('user_sql', 'domain_settings', 'none'); - $this -> domain_array = explode(",", OCP\Config::getAppValue('user_sql', 'domain_array', '')); - $this -> map_array = explode(",", OCP\Config::getAppValue('user_sql', 'map_array', '')); - $this -> mail_sync_mode = OCP\Config::getAppValue('user_sql', 'mail_sync_mode', 'none'); + $this -> helper = new \OCA\user_sql\lib\Helper(); + $domain = \OC::$server->getRequest()->getServerHost(); + $this -> settings = $this -> helper -> loadSettingsForDomain($domain); + $this -> helper -> connectToDb($this -> settings); $this -> session_cache_name = 'USER_SQL_CACHE'; - $dsn = $this -> sql_type . ":host=" . $this -> sql_host . ";dbname=" . $this -> sql_database; - try - { - $this -> db = new PDO($dsn, $this -> sql_username, $this -> sql_password); - $this -> db -> query("SET NAMES 'UTF8'"); - $this -> db_conn = true; - } catch (PDOException $e) - { - \OCP\Util::writeLog('OC_USER_SQL', 'Failed to connect to the database: ' . $e -> getMessage(), \OCP\Util::ERROR); - } return false; } private function doEmailSync($uid) { \OCP\Util::writeLog('OC_USER_SQL', "Entering doEmailSync for UID: $uid", \OCP\Util::DEBUG); - if($this -> sql_column_email === '') + if($this -> settings['col_email'] === '') return false; - if($this -> mail_sync_mode === 'none') + if($this -> settings['set_mail_sync_mode'] === 'none') return false; $ocUid = $uid; $uid = $this -> doUserDomainMapping($uid); - $query = "SELECT $this->sql_column_email FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) + $row = $this -> helper -> runQuery('getMail', array('uid' => $uid)); + if($row === false) { return false; } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching result...", \OCP\Util::DEBUG); - $row = $result -> fetch(); - if(!$row) - { - return false; - } - $newMail = $row[$this -> sql_column_email]; - $currMail = OCP\Config::getUserValue($ocUid, 'settings', 'email', ''); + $newMail = $row[$this -> settings['col_email']]; + $currMail = \OCP\Config::getUserValue($ocUid, 'settings', 'email', ''); - switch($this -> mail_sync_mode) + switch($this -> settings['set_mail_sync_mode']) { case 'initial': if($currMail === '') - OCP\Config::setUserValue($ocUid, 'settings', 'email', $newMail); + \OCP\Config::setUserValue($ocUid, 'settings', 'email', $newMail); break; case 'forcesql': if($currMail !== $newMail) - OCP\Config::setUserValue($ocUid, 'settings', 'email', $newMail); + \OCP\Config::setUserValue($ocUid, 'settings', 'email', $newMail); break; case 'forceoc': if(($currMail !== '') && ($currMail !== $newMail)) { - $query = "UPDATE $this->sql_table SET $this->sql_column_email = :currMail WHERE $this->sql_column_username = :uid"; - \OCP\Util::writeLog('OC_USER_SQL', "Preapring query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":currMail", $currMail); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) + $row = $this -> helper -> runQuery('setMail', array('uid' => $uid, 'currMail' => $currMail), true); + + if($row === false) { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); \OCP\Util::writeLog('OC_USER_SQL', "Could not update E-Mail address in SQL database!", \OCP\Util::ERROR); } } break; } return true; } private function doUserDomainMapping($uid) { $uid = trim($uid); - - switch($this->domain_settings) + + if($this -> settings['set_default_domain'] !== '') { - case "default" : - \OCP\Util::writeLog('OC_USER_SQL', "Default mapping", \OCP\Util::DEBUG); - if($this -> default_domain && (strpos($uid, '@') === false)) - $uid .= "@" . $this -> default_domain; - break; - case "server" : - \OCP\Util::writeLog('OC_USER_SQL', "Server based mapping", \OCP\Util::DEBUG); - if(strpos($uid, '@') === false) - $uid .= "@" . $_SERVER['SERVER_NAME']; - break; - case "mapping" : - \OCP\Util::writeLog('OC_USER_SQL', 'Domain mapping selected', \OCP\Util::DEBUG); - if(strpos($uid, '@') === false) - { - for($i = 0; $i < count($this -> domain_array); $i++) - { - \OCP\Util::writeLog('OC_USER_SQL', 'Checking domain in mapping: ' . $this -> domain_array[$i], \OCP\Util::DEBUG); - if($_SERVER['SERVER_NAME'] === trim($this -> domain_array[$i])) - { - \OCP\Util::writeLog('OC_USER_SQL', 'Found domain in mapping: ' . $this -> domain_array[$i], \OCP\Util::DEBUG); - $uid .= "@" . trim($this -> map_array[$i]); - break; - } - } - } - break; - case "none" : - default : - \OCP\Util::writeLog('OC_USER_SQL', "No mapping", \OCP\Util::DEBUG); - break; + \OCP\Util::writeLog('OC_USER_SQL', "Append default domain: ".$this -> settings['set_default_domain'], \OCP\Util::DEBUG); + if(strpos($uid, '@') === false) + { + $uid .= "@" . $this -> settings['set_default_domain']; + } } $uid = strtolower($uid); \OCP\Util::writeLog('OC_USER_SQL', 'Returning mapped UID: ' . $uid, \OCP\Util::DEBUG); return $uid; } public function implementsAction($actions) { return (bool)((\OC_User_Backend::CHECK_PASSWORD | \OC_User_Backend::GET_DISPLAYNAME | \OC_User_Backend::COUNT_USERS ) & $actions); } public function hasUserListings() { return true; } public function createUser() { // Can't create user \OCP\Util::writeLog('OC_USER_SQL', 'Not possible to create local users from web frontend using SQL user backend', \OCP\Util::ERROR); return false; } public function deleteUser($uid) { // Can't delete user \OCP\Util::writeLog('OC_USER_SQL', 'Not possible to delete local users from web frontend using SQL user backend', \OCP\Util::ERROR); return false; } public function setPassword($uid, $password) { // Update the user's password - this might affect other services, that // use the same database, as well \OCP\Util::writeLog('OC_USER_SQL', "Entering setPassword for UID: $uid", \OCP\Util::DEBUG); - if(!$this -> db_conn || !$this->allow_password_change) + + if($this -> settings['set_allow_pwchange'] !== 'true') + return false; + + $uid = $this -> doUserDomainMapping($uid); + + $row = $this -> helper -> runQuery('getPass', array('uid' => $uid)); + if($row === false) { return false; } - $uid = $this -> doUserDomainMapping($uid); - - $query = "SELECT $this->sql_column_password FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) - { - return false; - } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching result...", \OCP\Util::DEBUG); - $row = $result -> fetch(); - if(!$row) - { - return false; - } - $old_password = $row[$this -> sql_column_password]; - if($this -> crypt_type === 'joomla2') + $old_password = $row[$this -> settings['col_password']]; + if($this -> settings['set_crypt_type'] === 'joomla2') { if(!class_exists('PasswordHash')) require_once('PasswordHash.php'); $hasher = new PasswordHash(10, true); - $enc_password = $hasher->HashPassword($password); + $enc_password = $hasher -> HashPassword($password); } // Redmine stores the salt separatedly, this doesn't play nice with the way // we check passwords - elseif($this -> crypt_type === 'redmine') + elseif($this -> settings['set_crypt_type'] === 'redmine') { - $query = "SELECT salt FROM $this->sql_table WHERE $this->sql_column_username =:uid;"; - $res = $this->db->prepare($query); - $res->bindparam(":uid", $uid); - if(!$res->execute()) - return false; - $salt = $res->fetch(); + $salt = $this -> helper -> runQuery('getRedmineSalt', array('uid' => $uid)); if(!$salt) return false; $enc_password = sha1($salt['salt'].sha1($password)); } else { $enc_password = $this -> pacrypt($password, $old_password); } - $query = "UPDATE $this->sql_table SET $this->sql_column_password = :enc_password WHERE $this->sql_column_username = :uid"; - \OCP\Util::writeLog('OC_USER_SQL', "Preapring query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":enc_password", $enc_password); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) + $res = $this -> helper -> runQuery('setPass', array('uid' => $uid, 'enc_password' => $enc_password), true); + if($res === false) { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); \OCP\Util::writeLog('OC_USER_SQL', "Could not update password!", \OCP\Util::ERROR); return false; } \OCP\Util::writeLog('OC_USER_SQL', "Updated password successfully, return true", \OCP\Util::DEBUG); return true; } /** * @brief Check if the password is correct * @param $uid The username * @param $password The password * @returns true/false * * Check if the password is correct without logging in the user */ public function checkPassword($uid, $password) { \OCP\Util::writeLog('OC_USER_SQL', "Entering checkPassword() for UID: $uid", \OCP\Util::DEBUG); - if(!$this -> db_conn) - { - return false; - } + $uid = $this -> doUserDomainMapping($uid); - $query = "SELECT $this->sql_column_username, $this->sql_column_password FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - if($this -> sql_column_active !== '') - $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) - { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); - return false; - } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching row...", \OCP\Util::DEBUG); - $row = $result -> fetch(); - if(!$row) + $row = $this -> helper -> runQuery('getPass', array('uid' => $uid)); + if($row === false) { \OCP\Util::writeLog('OC_USER_SQL', "Got no row, return false", \OCP\Util::DEBUG); return false; } + $db_pass = $row[$this -> settings['col_password']]; \OCP\Util::writeLog('OC_USER_SQL', "Encrypting and checking password", \OCP\Util::DEBUG); // Joomla 2.5.18 switched to phPass, which doesn't play nice with the way // we check passwords - if($this -> crypt_type === 'joomla2') + if($this -> settings['set_crypt_type'] === 'joomla2') { if(!class_exists('PasswordHash')) require_once('PasswordHash.php'); $hasher = new PasswordHash(10, true); - $ret = $hasher -> CheckPassword($password, $row[$this -> sql_column_password]); + $ret = $hasher -> CheckPassword($password, $db_pass); } // Redmine stores the salt separatedly, this doesn't play nice with the way // we check passwords - elseif($this -> crypt_type === 'redmine') + elseif($this -> settings['set_crypt_type'] === 'redmine') { - $query = "SELECT salt FROM $this->sql_table WHERE $this->sql_column_username =:uid;"; - $res = $this->db->prepare($query); - $res->bindparam(":uid", $uid); - if(!$res->execute()) - return false; - $salt = $res->fetch(); + $salt = $this -> helper -> runQuery('getRedmineSalt', array('uid' => $uid)); if(!$salt) return false; - $ret = sha1($salt['salt'].sha1($password)) === $row[$this->sql_column_password]; + $ret = sha1($salt['salt'].sha1($password)) === $db_pass; } else { - $ret = $this -> pacrypt($password, $row[$this -> sql_column_password]) === $row[$this -> sql_column_password]; + $ret = $this -> pacrypt($password, $db_pass) === $db_pass; } if($ret) { \OCP\Util::writeLog('OC_USER_SQL', "Passwords matching, return true", \OCP\Util::DEBUG); - if($this -> strip_domain) + if($this -> settings['set_strip_domain'] === 'true') { $uid = explode("@", $uid); $uid = $uid[0]; } return $uid; } else { \OCP\Util::writeLog('OC_USER_SQL', "Passwords do not match, return false", \OCP\Util::DEBUG); return false; } } public function countUsers() { - \OCP\Util::writeLog('OC_USER_SQL', "Entering countUsers()",\OCP\Util::DEBUG); - if(!$this -> db_conn) - { - return FALSE; - } - $query = "SELECT COUNT(*) FROM $this->sql_table"; - if($this -> sql_column_active !== '') - $query .= " WHERE " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) + \OCP\Util::writeLog('OC_USER_SQL', "Entering countUsers()", \OCP\Util::DEBUG); + + $userCount = $this -> helper -> runQuery('countUsers', array()); + if($userCount === false) { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); - return 0; + $userCount = 0; } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching results...", \OCP\Util::DEBUG); - $userCount = reset($result -> fetch()); - \OCP\Util::writeLog('OC_USER_SQL', "Return usercount", \OCP\Util::DEBUG); - return $userCount; + else { + $userCount = reset($userCount); + } + + \OCP\Util::writeLog('OC_USER_SQL', "Return usercount", \OCP\Util::DEBUG); + return $userCount; } /** * @brief Get a list of all users * @returns array with all uids * * Get a list of all users. */ public function getUsers($search = '', $limit = null, $offset = null) { \OCP\Util::writeLog('OC_USER_SQL', "Entering getUsers() with Search: $search, Limit: $limit, Offset: $offset", \OCP\Util::DEBUG); $users = array(); - if(!$this -> db_conn) - { - return false; - } - $query = "SELECT $this->sql_column_username FROM $this->sql_table"; - $query .= " WHERE $this->sql_column_username LIKE :search"; - if($this -> sql_column_active !== '') - $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; - $query .= " ORDER BY $this->sql_column_username"; - if($limit !== null) - { - $limit = intval($limit); - $query .= " LIMIT $limit"; - } - if($offset !== null) - { - $offset = intval($offset); - $query .= " OFFSET $offset"; - } - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); + if($search !== '') { $search = "%".$this -> doUserDomainMapping($search."%")."%"; } else { $search = "%".$this -> doUserDomainMapping("")."%"; } - $result -> bindParam(":search", $search); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) + $rows = $this -> helper -> runQuery('getUsers', array('search' => $search), false, true, array('limit' => $limit, 'offset' => $offset)); + if($rows === false) + return array(); + + foreach($rows as $row) { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); - return array(); - } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching results...", \OCP\Util::DEBUG); - while($row = $result -> fetch()) - { - $uid = $row[$this -> sql_column_username]; - if($this -> strip_domain) + $uid = $row[$this -> settings['col_username']]; + if($this -> settings['set_strip_domain'] === 'true') { $uid = explode("@", $uid); $uid = $uid[0]; } $users[] = strtolower($uid); } \OCP\Util::writeLog('OC_USER_SQL', "Return list of results", \OCP\Util::DEBUG); return $users; } /** * @brief check if a user exists * @param string $uid the username * @return boolean */ public function userExists($uid) { $cacheKey = 'sql_user_exists_' . $uid; $cacheVal = $this -> getCache ($cacheKey); \OCP\Util::writeLog('OC_USER_SQL', "userExists() for UID: $uid cacheVal: $cacheVal", \OCP\Util::DEBUG); if(!is_null($cacheVal)) return (bool)$cacheVal; \OCP\Util::writeLog('OC_USER_SQL', "Entering userExists() for UID: $uid", \OCP\Util::DEBUG); - if(!$this -> db_conn) - { - return false; - } + $uid = $this -> doUserDomainMapping($uid); - $query = "SELECT $this->sql_column_username FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - if($this -> sql_column_active !== '') - $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) - { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); - return false; - } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching results...", \OCP\Util::DEBUG); - $exists = (bool)$result -> fetch(); + $exists = (bool)$this -> helper -> runQuery('userExists', array('uid' => $uid));; $this -> setCache ($cacheKey, $exists, 60); if(!$exists) { \OCP\Util::writeLog('OC_USER_SQL', "Empty row, user does not exists, return false", \OCP\Util::DEBUG); return false; } else { \OCP\Util::writeLog('OC_USER_SQL', "User exists, return true", \OCP\Util::DEBUG); return true; } } public function getDisplayName($uid) { \OCP\Util::writeLog('OC_USER_SQL', "Entering getDisplayName() for UID: $uid", \OCP\Util::DEBUG); - if(!$this -> db_conn) - { - return false; - } + $this -> doEmailSync($uid); $uid = $this -> doUserDomainMapping($uid); if(!$this -> userExists($uid)) { return false; } - $query = "SELECT $this->sql_column_displayname FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - if($this -> sql_column_active !== '') - $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; - \OCP\Util::writeLog('OC_USER_SQL', "Preparing query: $query", \OCP\Util::DEBUG); - $result = $this -> db -> prepare($query); - $result -> bindParam(":uid", $uid); - \OCP\Util::writeLog('OC_USER_SQL', "Executing query...", \OCP\Util::DEBUG); - if(!$result -> execute()) - { - $err = $result -> errorInfo(); - \OCP\Util::writeLog('OC_USER_SQL', "Query failed: " . $err[2], \OCP\Util::DEBUG); - return false; - } - \OCP\Util::writeLog('OC_USER_SQL', "Fetching results...", \OCP\Util::DEBUG); - $row = $result -> fetch(); + $row = $this -> helper -> runQuery('getDisplayName', array('uid' => $uid)); + if(!$row) { \OCP\Util::writeLog('OC_USER_SQL', "Empty row, user has no display name or does not exist, return false", \OCP\Util::DEBUG); return false; } else { \OCP\Util::writeLog('OC_USER_SQL', "User exists, return true", \OCP\Util::DEBUG); - $displayName = $row[$this -> sql_column_displayname]; + $displayName = $row[$this -> settings['col_displayname']]; return $displayName; ; } return false; } public function getDisplayNames($search = '', $limit = null, $offset = null) { $uids = $this -> getUsers($search, $limit, $offset); $displayNames = array(); foreach($uids as $uid) { $displayNames[$uid] = $this -> getDisplayName($uid); } return $displayNames; } /** * Returns the backend name * @return string */ public function getBackendName() { return 'SQL'; } /** * The following functions were directly taken from PostfixAdmin and just * slightly modified * to suit our needs. * Encrypt a password, using the apparopriate hashing mechanism as defined in * config.inc.php ($this->crypt_type). * When wanting to compare one pw to another, it's necessary to provide the * salt used - hence * the second parameter ($pw_db), which is the existing hash from the DB. * * @param string $pw * @param string $encrypted password * @return string encrypted password. */ private function pacrypt($pw, $pw_db = "") { \OCP\Util::writeLog('OC_USER_SQL', "Entering private pacrypt()", \OCP\Util::DEBUG); $pw = stripslashes($pw); $password = ""; $salt = ""; - if($this -> crypt_type === 'md5crypt') + if($this -> settings['set_crypt_type'] === 'md5crypt') { $split_salt = preg_split('/\$/', $pw_db); if(isset($split_salt[2])) { $salt = $split_salt[2]; } $password = $this -> md5crypt($pw, $salt); - } elseif($this -> crypt_type === 'md5') + } elseif($this -> settings['set_crypt_type'] === 'md5') { $password = md5($pw); - } elseif($this -> crypt_type === 'system') + } elseif($this -> settings['set_crypt_type'] === 'system') { // We never generate salts, as user creation is not allowed here $password = crypt($pw, $pw_db); - } elseif($this -> crypt_type === 'cleartext') + } elseif($this -> settings['set_crypt_type'] === 'cleartext') { $password = $pw; } // See // https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583 // this is apparently useful for pam_mysql etc. - elseif($this -> crypt_type === 'mysql_encrypt') + elseif($this -> settings['set_crypt_type'] === 'mysql_encrypt') { - if(!$this -> db_conn) - { - return false; - } if($pw_db !== "") { $salt = substr($pw_db, 0, 2); - $query = "SELECT ENCRYPT(:pw, :salt);"; + + $row = $this -> helper -> runQuery('mysqlEncryptSalt', array('pw' => $pw, 'salt' => $salt)); } else { - $query = "SELECT ENCRYPT(:pw);"; + $row = $this -> helper -> runQuery('mysqlEncrypt', array('pw' => $pw)); } - $result = $this -> db -> prepare($query); - $result -> bindParam(":pw", $pw); - if($pw_db !== "") - $result -> bindParam(":salt", $salt); - if(!$result -> execute()) - { - return false; - } - $row = $result -> fetch(); - if(!$row) + if($row === false) { return false; } $password = $row[0]; - } elseif($this -> crypt_type === 'mysql_password') + } elseif($this -> settings['set_crypt_type'] === 'mysql_password') { - if(!$this -> db_conn) - { - return false; - } - $query = "SELECT PASSWORD(:pw);"; + $this -> helper -> runQuery('mysqlPassword', array('pw' => $pw)); - $result = $this -> db -> prepare($query); - $result -> bindParam(":pw", $pw); - if(!$result -> execute()) - { - return false; - } - $row = $result -> fetch(); - if(!$row) + if($row === false) { return false; } $password = $row[0]; } // The following is by Frédéric France - elseif($this -> crypt_type === 'joomla') + elseif($this -> settings['set_crypt_type'] === 'joomla') { $split_salt = preg_split('/:/', $pw_db); if(isset($split_salt[1])) { $salt = $split_salt[1]; } $password = ($salt) ? md5($pw . $salt) : md5($pw); $password .= ':' . $salt; } - elseif($this-> crypt_type === 'ssha256') + elseif($this-> settings['set_crypt_type'] === 'ssha256') { $salted_password = base64_decode(preg_replace('/{SSHA256}/i','',$pw_db)); $salt = substr($salted_password,-(strlen($salted_password)-32)); $password = $this->ssha256($pw,$salt); } else { - \OCP\Util::writeLog('OC_USER_SQL', "unknown/invalid crypt_type settings: $this->crypt_type", \OCP\Util::ERROR); - die('unknown/invalid Encryption type setting: ' . $this -> crypt_type); + \OCP\Util::writeLog('OC_USER_SQL', "unknown/invalid crypt_type settings: ".$this->settings['set_crypt_type'], \OCP\Util::ERROR); + die('unknown/invalid Encryption type setting: ' . $this -> settings['set_crypt_type']); } \OCP\Util::writeLog('OC_USER_SQL', "pacrypt() done, return", \OCP\Util::DEBUG); return $password; } // // md5crypt // Action: Creates MD5 encrypted password // Call: md5crypt (string cleartextpassword) // private function md5crypt($pw, $salt = "", $magic = "") { $MAGIC = "$1$"; if($magic === "") $magic = $MAGIC; if($salt === "") $salt = $this -> create_salt(); $slist = explode("$", $salt); if($slist[0] === "1") $salt = $slist[1]; $salt = substr($salt, 0, 8); $ctx = $pw . $magic . $salt; $final = $this -> pahex2bin(md5($pw . $salt . $pw)); for($i = strlen($pw); $i > 0; $i -= 16) { if($i > 16) { $ctx .= substr($final, 0, 16); } else { $ctx .= substr($final, 0, $i); } } $i = strlen($pw); while($i > 0) { if($i & 1) $ctx .= chr(0); else $ctx .= $pw[0]; $i = $i>>1; } $final = $this -> pahex2bin(md5($ctx)); for($i = 0; $i < 1000; $i++) { $ctx1 = ""; if($i & 1) { $ctx1 .= $pw; } else { $ctx1 .= substr($final, 0, 16); } if($i % 3) $ctx1 .= $salt; if($i % 7) $ctx1 .= $pw; if($i & 1) { $ctx1 .= substr($final, 0, 16); } else { $ctx1 .= $pw; } $final = $this -> pahex2bin(md5($ctx1)); } $passwd = ""; $passwd .= $this -> to64(((ord($final[0])<<16) | (ord($final[6])<<8) | (ord($final[12]))), 4); $passwd .= $this -> to64(((ord($final[1])<<16) | (ord($final[7])<<8) | (ord($final[13]))), 4); $passwd .= $this -> to64(((ord($final[2])<<16) | (ord($final[8])<<8) | (ord($final[14]))), 4); $passwd .= $this -> to64(((ord($final[3])<<16) | (ord($final[9])<<8) | (ord($final[15]))), 4); $passwd .= $this -> to64(((ord($final[4])<<16) | (ord($final[10])<<8) | (ord($final[5]))), 4); $passwd .= $this -> to64(ord($final[11]), 2); return "$magic$salt\$$passwd"; } private function create_salt() { srand((double) microtime() * 1000000); $salt = substr(md5(rand(0, 9999999)), 0, 8); return $salt; } private function ssha256($pw, $salt) { return '{SSHA256}'.base64_encode(hash('sha256',$pw.$salt,true).$salt); } private function pahex2bin($str) { if(function_exists('hex2bin')) { return hex2bin($str); } else { $len = strlen($str); $nstr = ""; for($i = 0; $i < $len; $i += 2) { $num = sscanf(substr($str, $i, 2), "%x"); $nstr .= chr($num[0]); } return $nstr; } } private function to64($v, $n) { $ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; $ret = ""; while(($n - 1) >= 0) { $n--; $ret .= $ITOA64[$v & 0x3f]; $v = $v>>6; } return $ret; } /** * Store a value in memcache or the session, if no memcache is available * @param string $key * @param mixed $value * @param int $ttl (optional) defaults to 3600 seconds. */ private function setCache($key,$value,$ttl=3600) { if ($this -> cache === NULL) { $_SESSION[$this -> session_cache_name][$key] = array( 'value' => $value, 'time' => time(), 'ttl' => $ttl, ); } else { $this -> cache -> set($key,$value,$ttl); } } /** * Fetch a value from memcache or session, if memcache is not available. * Returns NULL if there's no value stored or the value expired. * @param string $key * @return mixed|NULL */ private function getCache($key) { $retVal = NULL; if ($this -> cache === NULL) { if (isset($_SESSION[$this -> session_cache_name],$_SESSION[$this -> session_cache_name][$key])) { $value = $_SESSION[$this -> session_cache_name][$key]; if (time() < $value['time'] + $value['ttl']) { $retVal = $value['value']; } } } else { $retVal = $this -> cache -> get ($key); } return $retVal; } } ?>