Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bcrypt #868

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
84 changes: 84 additions & 0 deletions Core/Frameworks/Baikal/Core/BcryptAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Baikal\Core;

/**
* This is an authentication backend that uses a database to manage passwords.
*
* Format of the database tables must match to the one of \Sabre\DAV\Auth\Backend\PDO
*
* @copyright Copyright (C) 2013 Lukasz Janyst. All rights reserved.
* @author Lukasz Janyst <ljanyst@buggybrain.net>
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class BcryptAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic {

/**
* Reference to PDO connection
*
* @var PDO
*/
protected $pdo;

/**
* PDO table name we'll be using
*
* @var string
*/
protected $tableName;

/**
* Authentication realm
*
* @var string
*/
protected $authRealm;

/**
* Creates the backend object.
*
* If the filename argument is passed in, it will parse out the specified file fist.
*
* @param PDO $pdo
* @param string $tableName The PDO table name to use
*/
function __construct(\PDO $pdo, $authRealm, $tableName = 'users') {

$this->pdo = $pdo;
$this->tableName = $tableName;
$this->authRealm = $authRealm;
}

/**
* 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
*/
function validateUserPass($username, $password) {

$stmt = $this->pdo->prepare('SELECT username, digesta1 FROM ' . $this->tableName . ' WHERE username = ?');
$stmt->execute([$username]);
$result = $stmt->fetchAll();
if (!count($result)) return false;

if (substr($result[0]['digesta1'], 0, 4) == '$2y$') {
$check = password_verify($password, $result[0]['digesta1']);
} else {
$hash = md5($username . ':' . $this->authRealm . ':' . $password);
if ($result[0]['digesta1'] === $hash) {
$check = true;
}
}
if ($check == true) {
return true;
}
return false;

}

}
6 changes: 3 additions & 3 deletions Core/Frameworks/Baikal/Core/Server.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ function start() {
protected function initServer() {

if ($this->authType === 'Basic') {
$authBackend = new \Baikal\Core\PDOBasicAuth($this->pdo, $this->authRealm);
$authBackend = new \Baikal\Core\BcryptAuth($this->pdo, $this->authRealm);
} else {
$authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo);
$authBackend->setRealm($this->authRealm);
$authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo);
$authBackend->setRealm($this->authRealm);
}
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($this->pdo);

Expand Down
98 changes: 56 additions & 42 deletions Core/Frameworks/Baikal/Core/Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@

namespace Baikal\Core;

class Tools {
static function &db() {
return $GLOBALS["pdo"];
class Tools
{
static function &db()
{
return $GLOBALS['pdo'];
}

static function assertEnvironmentIsOk() {
static function assertEnvironmentIsOk()
{
# Asserting Baikal Context
if (!defined("BAIKAL_CONTEXT") || BAIKAL_CONTEXT !== true) {
die("Bootstrap.php may not be included outside the Baikal context");
if (!defined('BAIKAL_CONTEXT') || BAIKAL_CONTEXT !== true) {
die('Bootstrap.php may not be included outside the Baikal context');
}

# Asserting PDO
Expand All @@ -50,21 +53,24 @@ static function assertEnvironmentIsOk() {
}
}

static function configureEnvironment() {
static function configureEnvironment()
{
set_exception_handler('\Baikal\Core\Tools::handleException');
ini_set("error_reporting", E_ALL);
ini_set('error_reporting', E_ALL);
}

static function handleException($exception) {
echo "<pre>" . $exception . "<pre>";
static function handleException($exception)
{
echo '<pre>' . $exception . '<pre>';
}

static function assertBaikalIsOk() {
static function assertBaikalIsOk()
{

# DB connexion has not been asserted earlier by Flake, to give us a chance to trigger the install tool
# We assert it right now
if (!\Flake\Framework::isDBInitialized() && (!defined("BAIKAL_CONTEXT_INSTALL") || BAIKAL_CONTEXT_INSTALL === false)) {
throw new \Exception("<strong>Fatal error</strong>: no connection to a database is available.");
if (!\Flake\Framework::isDBInitialized() && (!defined('BAIKAL_CONTEXT_INSTALL') || BAIKAL_CONTEXT_INSTALL === false)) {
throw new \Exception('<strong>Fatal error</strong>: no connection to a database is available.');
}

# Asserting that the database is structurally complete
Expand All @@ -73,51 +79,52 @@ static function assertBaikalIsOk() {
#}

# Asserting config file exists
if (!file_exists(PROJECT_PATH_SPECIFIC . "config.php")) {
throw new \Exception("Specific/config.php does not exist. Please use the Install tool to create it.");
if (!file_exists(PROJECT_PATH_SPECIFIC . 'config.php')) {
throw new \Exception('Specific/config.php does not exist. Please use the Install tool to create it.');
}

# Asserting config file is readable
if (!is_readable(PROJECT_PATH_SPECIFIC . "config.php")) {
if (!is_readable(PROJECT_PATH_SPECIFIC . 'config.php')) {
throw new \Exception("Specific/config.php is not readable. Please give read permissions to httpd user on file 'Specific/config.php'.");
}

# Asserting config file is writable
if (!is_writable(PROJECT_PATH_SPECIFIC . "config.php")) {
if (!is_writable(PROJECT_PATH_SPECIFIC . 'config.php')) {
throw new \Exception("Specific/config.php is not writable. Please give write permissions to httpd user on file 'Specific/config.php'.");
}

# Asserting system config file exists
if (!file_exists(PROJECT_PATH_SPECIFIC . "config.system.php")) {
throw new \Exception("Specific/config.system.php does not exist. Please use the Install tool to create it.");
if (!file_exists(PROJECT_PATH_SPECIFIC . 'config.system.php')) {
throw new \Exception('Specific/config.system.php does not exist. Please use the Install tool to create it.');
}

# Asserting system config file is readable
if (!is_readable(PROJECT_PATH_SPECIFIC . "config.system.php")) {
if (!is_readable(PROJECT_PATH_SPECIFIC . 'config.system.php')) {
throw new \Exception("Specific/config.system.php is not readable. Please give read permissions to httpd user on file 'Specific/config.system.php'.");
}

# Asserting system config file is writable
if (!is_writable(PROJECT_PATH_SPECIFIC . "config.system.php")) {
if (!is_writable(PROJECT_PATH_SPECIFIC . 'config.system.php')) {
throw new \Exception("Specific/config.system.php is not writable. Please give write permissions to httpd user on file 'Specific/config.system.php'.");
}
}

static function getRequiredTablesList() {
static function getRequiredTablesList()
{
return [
"addressbooks",
"calendarobjects",
"calendars",
"cards",
"groupmembers",
"locks",
"principals",
"users",
'addressbooks',
'calendarobjects',
'calendars',
'cards',
'groupmembers',
'locks',
'principals',
'users',
];
}

static function isDBStructurallyComplete(\Flake\Core\Database $oDB) {

static function isDBStructurallyComplete(\Flake\Core\Database $oDB)
{
$aRequiredTables = self::getRequiredTablesList();
$aPresentTables = $oDB->tables();

Expand All @@ -129,19 +136,23 @@ static function isDBStructurallyComplete(\Flake\Core\Database $oDB) {
return true;
}

static function bashPrompt($prompt) {
static function bashPrompt($prompt)
{
echo $prompt;
@flush();
@ob_flush();
$confirmation = @trim(fgets(STDIN));

return $confirmation;
}

static function bashPromptSilent($prompt = "Enter Password:") {
static function bashPromptSilent($prompt = 'Enter Password:')
{
$command = "/usr/bin/env bash -c 'echo OK'";

if (rtrim(shell_exec($command)) !== 'OK') {
trigger_error("Can't invoke bash");

return;
}

Expand All @@ -151,20 +162,21 @@ static function bashPromptSilent($prompt = "Enter Password:") {

$password = rtrim(shell_exec($command));
echo "\n";

return $password;
}

static function getCopyrightNotice($sLinePrefixChar = "#", $sLineSuffixChar = "", $sOpening = false, $sClosing = false) {

static function getCopyrightNotice($sLinePrefixChar = '#', $sLineSuffixChar = '', $sOpening = false, $sClosing = false)
{
if ($sOpening === false) {
$sOpening = str_repeat("#", 78);
$sOpening = str_repeat('#', 78);
}

if ($sClosing === false) {
$sClosing = str_repeat("#", 78);
$sClosing = str_repeat('#', 78);
}

$iYear = date("Y");
$iYear = date('Y');

$sCode = <<<CODE
Copyright notice
Expand Down Expand Up @@ -196,21 +208,23 @@ static function getCopyrightNotice($sLinePrefixChar = "#", $sLineSuffixChar = ""
$aCode[$iLineNum] = trim($sLinePrefixChar . "\t" . $aCode[$iLineNum]);
}

if (trim($sOpening) !== "") {
if (trim($sOpening) !== '') {
array_unshift($aCode, $sOpening);
}

if (trim($sClosing) !== "") {
if (trim($sClosing) !== '') {
$aCode[] = $sClosing;
}

return implode("\n", $aCode);
}

static function timezones() {
static function timezones()
{
$aZones = \DateTimeZone::listIdentifiers();

reset($aZones);

return $aZones;
}
}
39 changes: 27 additions & 12 deletions Core/Frameworks/Baikal/Model/Config/Standard.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ class Standard extends \Baikal\Model\Config {
],
"BAIKAL_DAV_AUTH_TYPE" => [
"type" => "string",
"comment" => "HTTP authentication type for WebDAV; default Digest"
"comment" => "HTTP authentication type for WebDAV; default Basic"
],
"BAIKAL_USER_AUTH_TYPE" => [
"type" => "string",
"comment" => "Authentication mechanism for user accounts"
],
"BAIKAL_ADMIN_PASSWORDHASH" => [
"type" => "string",
Expand All @@ -62,7 +66,8 @@ class Standard extends \Baikal\Model\Config {
"BAIKAL_CARD_ENABLED" => true,
"BAIKAL_CAL_ENABLED" => true,
"BAIKAL_INVITE_FROM" => "",
"BAIKAL_DAV_AUTH_TYPE" => "Digest",
"BAIKAL_DAV_AUTH_TYPE" => "Basic",
"BAIKAL_USER_AUTH_TYPE" => "Bcrypt",
"BAIKAL_ADMIN_PASSWORDHASH" => ""
];

Expand Down Expand Up @@ -96,7 +101,14 @@ function formMorphologyForThisModelInstance() {
$oMorpho->add(new \Formal\Element\Listbox([
"prop" => "BAIKAL_DAV_AUTH_TYPE",
"label" => "WebDAV authentication type",
"options" => ["Digest", "Basic"]
"options" => ["Basic", "Digest"]
]));

$oMorpho->add(new \Formal\Element\Listbox([
"prop" => "BAIKAL_USER_AUTH_TYPE",
"label" => "Password Storage Hash Type",
"options" => ["Bcrypt", "MD5"],
"help" => "If set to BCrypt, WebDAV must be set to BASIC."
]));

$oMorpho->add(new \Formal\Element\Password([
Expand Down Expand Up @@ -132,14 +144,13 @@ function set($sProp, $sValue) {
# Special handling for password and passwordconfirm

if ($sProp === "BAIKAL_ADMIN_PASSWORDHASH" && $sValue !== "") {
parent::set(
"BAIKAL_ADMIN_PASSWORDHASH",
\BaikalAdmin\Core\Auth::hashAdminPassword($sValue)
);
}

parent::set(
"BAIKAL_ADMIN_PASSWORDHASH",
password_hash($sValue, PASSWORD_BCRYPT)
);
}
return $this;
}
}

parent::set($sProp, $sValue);
}
Expand Down Expand Up @@ -191,8 +202,12 @@ protected static function getDefaultConfig() {
# CalDAV invite From: mail address (comment or leave blank to disable notifications)
define("BAIKAL_INVITE_FROM", "noreply@$_SERVER[SERVER_NAME]");

# WebDAV authentication type; default Digest
define("BAIKAL_DAV_AUTH_TYPE", "Digest");
# WebDAV authentication type; default Basic
define("BAIKAL_DAV_AUTH_TYPE", "Basic");


# Baikal user password hash method; default bcrypt
define("BAIKAL_USER_AUTH_TYPE", "Bcrypt");

# Baïkal Web admin password hash; Set via Baïkal Web Admin
define("BAIKAL_ADMIN_PASSWORDHASH", "");
Expand Down
Loading