diff --git a/sources/extra_files/app/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php b/sources/extra_files/app/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php new file mode 100644 index 0000000..e7859ea --- /dev/null +++ b/sources/extra_files/app/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php @@ -0,0 +1,130 @@ + + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +abstract class AbstractExternalAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { + + /** + * enable autocreation of user + * + * @var PDO + */ + protected $enableAutoCreation; + + /** + * Reference to PDO connection + * + * @var PDO + */ + private $pdo; + + /** + * PDO table name we'll be using + * + * @var string + */ + private $tableName; + + /** + * Creates the backend object. + * + * If the filename argument is passed in, it will parse out the specified file fist. + * + * @param PDO $pdo + * @param string $realm + * @param string $tableName The PDO table name to use + */ + public function __construct(\PDO $pdo, $realm = 'BaikalDAV', $tableName = 'users') { + + $this->pdo = $pdo; + $this->tableName = $tableName; + $this->enableAutoCreation = true; + } + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + public function validateUserPass($username, $password) { + + if (!$this->validateUserPassExternal($username, $password)) + return false; + + $this->currentUser = $username; + if ($this->enableAutoCreation) + $this->autoUserCreation($username); + + return true; + } + + /** + * Validates a username and password agains external backend + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + public abstract function validateUserPassExternal($username, $password); + + /** + * return the displayname and email from the external Backend + * + * @param string $username + * @return array ('displayname' => string, 'email' => string) + */ + public function getAccountValues($username) { + + return array(); + } + + /** + * create an internal user, when user not exists + * + * @param string $username + */ + private function autoUserCreation($username) { + + /* search user in DB and do nothing, when user exists */ + $stmt = $this->pdo->prepare('SELECT username FROM '.$this->tableName.' WHERE username = ?'); + $stmt->execute(array($username)); + $result = $stmt->fetchAll(); + if (count($result) != 0) + return; + + /* get account values from backend */ + $values = $this->getAccountValues($username); + if (!isset($values['displayname']) OR strlen($values['displayname']) === 0) + $values['displayname'] = $username; + if (!isset($values['email']) OR strlen($values['email']) === 0) { + if(filter_var($username, FILTER_VALIDATE_EMAIL)) + $values['email'] = $username; + else + $values['email'] = 'unset-mail'; + } + + /* create user */ + $user = new \Baikal\Model\User(); + $user->set('username', $username); + $user->set('displayname', $values['displayname']); + $user->set('email', $values['email']); + $user->persist(); + } + +} diff --git a/sources/extra_files/app/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php b/sources/extra_files/app/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php new file mode 100644 index 0000000..d558a78 --- /dev/null +++ b/sources/extra_files/app/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php @@ -0,0 +1,79 @@ + + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class LDAPUserBindAuth extends AbstractExternalAuth { + + /** + * AccountValues for getAccountValues + * + * @var array ('displayname' => string, 'email' => string) + */ + private $accountValues; + + /** + * Validates a username and password over ldap + * + * @param string $username + * @param string $password + * @return bool + */ + public function validateUserPassExternal($username, $password) { + $config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml"); + + /* create ldap connection */ + $conn = ldap_connect($config['system']['dav_ldap_uri']); + if (!$conn) + return false; + if (!ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3)) + return false; + + /* bind with user + * error_handler have to change, because a failed bind raises an error + * this raise a secuity issue because in the stack trace is the password of user readable + */ + $arr = explode('@', $username, 2); + $dn = str_replace('%n', $username, $config['system']['dav_ldap_dn_template']); + $dn = str_replace('%u', $arr[0], $dn); + if(isset($arr[1])) $dn = str_replace('%d', $arr[1], $dn); + + set_error_handler("\Baikal\Core\LDAPUserBindAuth::exception_error_handler"); + $bind = ldap_bind($conn, $dn, $password); + restore_error_handler(); + if (!$bind) { + ldap_close($conn); + return false; + } + + /* read displayname and email from user */ + $this->accountValues = array(); + $dav_ldap_displayname_attr = $config['system']['dav_ldap_displayname_attr']; + $dav_ldap_email_attr = $config['system']['dav_ldap_email_attr']; + $sr = ldap_read($conn, $dn, '(objectclass=*)', array($dav_ldap_displayname_attr, $dav_ldap_email_attr)); + $entry = ldap_get_entries($conn, $sr); + if (isset($entry[0][$dav_ldap_displayname_attr][0])) + $this->accountValues['displayname'] = $entry[0][$dav_ldap_displayname_attr][0]; + if (isset($entry[0][$dav_ldap_email_attr][0])) + $this->accountValues['email'] = $entry[0][$dav_ldap_email_attr][0]; + + /* close */ + ldap_close($conn); + return true; + } + + public function getAccountValues($username) { + + return $this->accountValues; + } + + # WorkAround error_handler in failed bind of LDAP + public static function exception_error_handler($errno, $errstr, $errfile, $errline) { + } +} diff --git a/sources/extra_files/app/bin/upgrade.sh b/sources/extra_files/app/bin/upgrade.sh new file mode 100755 index 0000000..b77d96b --- /dev/null +++ b/sources/extra_files/app/bin/upgrade.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env php + +* All rights reserved +* +* http://baikal-server.com +* +* This script is part of the Baïkal Server project. The Baïkal +* Server project is free software; you can redistribute it +* and/or modify it under the terms of the GNU General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* +* This script 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 General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + +ini_set("session.cookie_httponly", 1); +ini_set("log_errors", 1); +error_reporting(E_WARNING | E_ERROR); + +define("BAIKAL_CONTEXT", true); +define("BAIKAL_CONTEXT_INSTALL", true); +define("PROJECT_CONTEXT_BASEURI", "/admin/install/"); + +define('PROJECT_PATH_ROOT', realpath(__DIR__ . '/..') . '/' ); + +if (!file_exists(PROJECT_PATH_ROOT . 'vendor/')) { + echo "Baïkal is not completely installed!\n"; + exit(1); +} + +require PROJECT_PATH_ROOT . "vendor/autoload.php"; +use Symfony\Component\Yaml\Yaml; + +# Extend VersionUpgrade for cli usage +class CLIUpgrade extends \BaikalAdmin\Controller\Install\VersionUpgrade { + + function run() { + try { + $config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml"); + } catch (\Exception $e) { + $this->output('Error reading baikal.yaml file : ' . $e->getMessage()); + } + + $sBaikalVersion = BAIKAL_VERSION; + $sBaikalConfiguredVersion = $config['system']['configured_version']; + + if (isset($config['system']['configured_version']) && $sBaikalConfiguredVersion === BAIKAL_VERSION) { + $this->output("Baïkal is already configured for version " . $sBaikalVersion); + return true; + } else { + $this->output("Upgrading Baïkal from version " . $sBaikalConfiguredVersion . " to version " . $sBaikalVersion); + } + + try { + $bSuccess = $this->upgrade($sBaikalConfiguredVersion, BAIKAL_VERSION); + } catch (\Exception $e) { + $bSuccess = false; + $this->output("Uncaught exception during upgrade: " . (string)$e); + } + if (!empty($oUpgrade->aErrors)) { + $this->output("Some errors occured:\n" . implode("\n - ", $oUpgrade->aErrors)); + } + if (!empty($oUpgrade->aSuccess)) { + $this->output(implode("\n", $oUpgrade->aSuccess)); + } + if ($bSuccess === false) { + $this->output("Error: unable to upgrade Baïkal."); + } else { + $this->output("Baïkal has been upgraded!"); + } + + return $bSuccess; + } + + function output($message) { + echo $message . "\n"; + } +} + +# Bootstraping Flake +\Flake\Framework::bootstrap(); + +# Bootstrap BaikalAdmin +\BaikalAdmin\Framework::bootstrap(); + +# Run the upgrade +$oUpgrade = new CLIUpgrade(); +if (!$oUpgrade->run()) { + exit(1); +} diff --git a/sources/patches/app-add-ldap-auth.patch b/sources/patches/app-add-ldap-auth.patch new file mode 100644 index 0000000..eb51c42 --- /dev/null +++ b/sources/patches/app-add-ldap-auth.patch @@ -0,0 +1,84 @@ +diff --git a/Core/Frameworks/Baikal/Core/Server.php b/Core/Frameworks/Baikal/Core/Server.php +index e96fe39..b90b49e 100644 +--- a/Core/Frameworks/Baikal/Core/Server.php ++++ b/Core/Frameworks/Baikal/Core/Server.php +@@ -133,6 +133,8 @@ class Server { + + if ($this->authType === 'Basic') { + $authBackend = new \Baikal\Core\PDOBasicAuth($this->pdo, $this->authRealm); ++ } elseif ($this->authType === 'LDAP-UserBind') { ++ $authBackend = new \Baikal\Core\LDAPUserBindAuth($this->pdo, $this->authRealm); + } else { + $authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo); + $authBackend->setRealm($this->authRealm); +diff --git a/Core/Frameworks/Baikal/Model/Config/Standard.php b/Core/Frameworks/Baikal/Model/Config/Standard.php +index 1ef5a51..32ec217 100644 +--- a/Core/Frameworks/Baikal/Model/Config/Standard.php ++++ b/Core/Frameworks/Baikal/Model/Config/Standard.php +@@ -51,6 +51,22 @@ class Standard extends \Baikal\Model\Config { + "type" => "string", + "comment" => "HTTP authentication type for WebDAV; default Digest" + ], ++ "dav_ldap_uri" => [ ++ "type" => "string", ++ "comment" => "URI to LDAP Server (for ldap-userbind auth); default ldapi:///" ++ ], ++ "dav_ldap_dn_template" => [ ++ "type" => "string", ++ "comment" => "User DN for bind; with replacments %n => username, %u => user part, %d => domain part of username" ++ ], ++ "dav_ldap_displayname_attr" => [ ++ "type" => "string", ++ "comment" => "LDAP-attribute for displayname; default cn" ++ ], ++ "dav_ldap_email_attr" => [ ++ "type" => "string", ++ "comment" => "LDAP-attribute for email; default mail" ++ ], + "admin_passwordhash" => [ + "type" => "string", + "comment" => "Baïkal Web admin password hash; Set via Baïkal Web Admin", +@@ -64,6 +80,10 @@ class Standard extends \Baikal\Model\Config { + "card_enabled" => true, + "cal_enabled" => true, + "dav_auth_type" => "Digest", ++ "dav_ldap_uri" => "ldapi:///", ++ "dav_ldap_dn_template" => "uid=%n,dc=example,dc=com", ++ "dav_ldap_displayname_attr" => "cn", ++ "dav_ldap_email_attr" => "mail", + "admin_passwordhash" => "", + "auth_realm" => "BaikalDAV", + "base_uri" => "" +@@ -103,7 +123,31 @@ class Standard extends \Baikal\Model\Config { + $oMorpho->add(new \Formal\Element\Listbox([ + "prop" => "dav_auth_type", + "label" => "WebDAV authentication type", +- "options" => ["Digest", "Basic"] ++ "options" => ["Digest", "Basic", "LDAP-UserBind"] ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_uri", ++ "label" => "LDAP URI" ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_dn_template", ++ "label" => "LDAP DN template", ++ "popover" => [ ++ "title" => "posible placeholder", ++ "content" => "%n - username
%u - user part of username , when it is an email address)
%d - domain part", ++ ] ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_displayname_attr", ++ "label" => "LDAP attribute for DisplayName" ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_email_attr", ++ "label" => "LDAP attribute for eMail" + ])); + + $oMorpho->add(new \Formal\Element\Password([