Commit a0e3196a authored by Côme Chilliet's avatar Côme Chilliet
Browse files

Merge branch '5778-create-a-security-library-for-fusiondirectory' into '1.4-dev'

Resolve "Create a security library for fusiondirectory"

See merge request fusiondirectory/fd!291
parents 5bc05872 4415be6f
......@@ -270,12 +270,14 @@ attributetype ( 1.3.6.1.4.1.38414.8.15.5 NAME 'fdSessionLifeTime'
attributetype ( 1.3.6.1.4.1.38414.8.15.6 NAME 'fdHttpAuthActivated'
DESC 'FusionDirectory - HTTP Basic Auth activation'
OBSOLETE
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.38414.8.15.7 NAME 'fdHttpHeaderAuthActivated'
DESC 'FusionDirectory - HTTP Header Auth activation'
OBSOLETE
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE )
......@@ -287,6 +289,13 @@ attributetype ( 1.3.6.1.4.1.38414.8.15.8 NAME 'fdHttpHeaderAuthHeaderName'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.38414.8.15.9 NAME 'fdLoginMethod'
DESC 'FusionDirectory - Active login method'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
# Debugging
attributetype ( 1.3.6.1.4.1.38414.8.16.1 NAME 'fdDisplayErrors'
......@@ -421,6 +430,7 @@ attributetype ( 1.3.6.1.4.1.38414.8.20.3 NAME 'fdSslCertPath'
attributetype ( 1.3.6.1.4.1.38414.8.21.1 NAME 'fdCasActivated'
DESC 'FusionDirectory - CAS activation'
OBSOLETE
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE )
......@@ -563,7 +573,8 @@ objectclass ( 1.3.6.1.4.1.38414.8.2.1 NAME 'fusionDirectoryConf'
fdPluginsMenuBlacklist $
fdAclTabOnObjects $ fdDepartmentCategories $
fdSslCaCertPath $ fdSslKeyPath $ fdSslCertPath $
fdCasActivated $ fdCasServerCaCertPath $ fdCasHost $ fdCasPort $ fdCasContext
fdCasActivated $ fdCasServerCaCertPath $ fdCasHost $ fdCasPort $ fdCasContext $
fdLoginMethod
) )
objectclass ( 1.3.6.1.4.1.38414.8.2.2 NAME 'fusionDirectoryPluginsConf'
......
......@@ -26,79 +26,6 @@ require_once ("variables.inc");
require_once ("class_logging.inc");
header("Content-type: text/html; charset=UTF-8");
/* Display the login page and exit() */
function displayLogin()
{
global $smarty,$message,$config,$ssl,$error_collector,$error_collector_mailto;
$lang = session::global_get('lang');
error_reporting(E_ALL | E_STRICT);
/* Fill template with required values */
$username = '';
if (isset($_POST['username'])) {
$username = trim($_POST['username']);
}
$smarty->assign ('date', gmdate('D, d M Y H:i:s'));
$smarty->assign ('username', $username);
$smarty->assign ('revision', FD_VERSION);
$smarty->assign ('year', date('Y'));
$smarty->append ('css_files', get_template_path('login.css'));
/* Some error to display? */
if (!isset($message)) {
$message = "";
}
$smarty->assign ("message", $message);
/* Display SSL mode warning? */
if (($ssl != '') && ($config->get_cfg_value('warnSSL') == 'TRUE')) {
$smarty->assign ('ssl', sprintf(_('Warning: <a href="%s">Session is not encrypted!</a>'), $ssl));
} else {
$smarty->assign ('ssl', '');
}
if (!$config->check_session_lifetime()) {
$smarty->assign ('lifetime', _('Warning: The session lifetime configured in your fusiondirectory.conf will be overridden by php.ini settings.'));
} else {
$smarty->assign ('lifetime', '');
}
/* Generate server list */
$servers = array();
if (isset($_POST['server'])) {
$selected = $_POST['server'];
} else {
$selected = $config->data['MAIN']['DEFAULT'];
}
foreach ($config->data['LOCATIONS'] as $key => $ignored) {
$servers[$key] = $key;
}
$smarty->assign ("server_options", $servers);
$smarty->assign ("server_id", $selected);
/* show login screen */
$smarty->assign ("PHPSESSID", session_id());
if (session::is_set('errors')) {
$smarty->assign("errors", session::get('errors'));
}
if ($error_collector != "") {
$smarty->assign("php_errors", preg_replace("/%BUGBODY%/", $error_collector_mailto, $error_collector)."</div>");
} else {
$smarty->assign("php_errors", "");
}
$smarty->assign("msg_dialogs", msg_dialog::get_dialogs());
$smarty->assign("usePrototype", "false");
$smarty->assign("date", date("l, dS F Y H:i:s O"));
$smarty->assign("lang", preg_replace('/_.*$/', '', $lang));
$smarty->assign("rtl", Language::isRTL($lang));
$smarty->display (get_template_path('headers.tpl'));
$smarty->assign("version", FD_VERSION);
$smarty->display(get_template_path('login.tpl'));
exit();
}
/*****************************************************************************
* M A I N *
*****************************************************************************/
......@@ -182,8 +109,6 @@ clean_smarty_compile_dir($smarty->compile_dir);
Language::init();
$smarty->assign ('focusfield', 'username');
if (isset($_POST['server'])) {
$server = $_POST['server'];
} else {
......@@ -194,7 +119,8 @@ $config->set_current($server);
if (
($config->get_cfg_value('casActivated') == 'TRUE') ||
($config->get_cfg_value('httpAuthActivated') == 'TRUE') ||
($config->get_cfg_value('httpHeaderAuthActivated') == 'TRUE')) {
($config->get_cfg_value('httpHeaderAuthActivated') == 'TRUE') ||
in_array($config->get_cfg_value('LoginMethod'), array('LoginCas', 'LoginHTTPAuth', 'LoginHTTPHeader'))) {
session::global_set('DEBUGLEVEL', 0);
}
......@@ -220,27 +146,5 @@ if (isset($_REQUEST['message'])) {
}
}
$loginMethods = LoginMethod::getMethods();
foreach ($loginMethods as $method) {
if ($method::active()) {
$method::loginProcess();
}
}
/* Translation of cookie-warning. Whether to display it, is determined by JavaScript */
$smarty->assign ('cookies', '<b>'._('Warning').':</b> '._('Your browser has cookies disabled. Please enable cookies and reload this page before logging in!'));
/* Set focus to the error button if we've an error message */
$focus = '';
if (session::is_set('errors') && session::get('errors') != '') {
$focus = '<script type="text/javascript">';
$focus .= 'document.forms[0].error_accept.focus();';
$focus .= '</script>';
}
$smarty->assign('focus', $focus);
displayLogin();
LoginMethod::loginProcess();
?>
</body>
</html>
......@@ -23,6 +23,12 @@
*/
class LoginCAS extends LoginMethod
{
/*! \brief Displayed name */
static function getLabel()
{
return _('CAS');
}
/*! \brief All login steps in the right order for CAS login */
static function loginProcess()
{
......@@ -89,12 +95,20 @@ class LoginCAS extends LoginMethod
if ($success) {
/* Everything went well, redirect to main.php */
static::redirect();
} else {
echo msg_dialog::get_dialogs();
if (!empty($message)) {
msg_dialog::display(
_('Error'),
sprintf(
_('Login with user "%s" triggered error: %s'),
static::$username,
$message
),
FATAL_ERROR_DIALOG
);
}
exit();
}
}
static function active()
{
global $config;
return ($config->get_cfg_value('casActivated') == 'TRUE');
}
}
......@@ -23,6 +23,12 @@
*/
class LoginHTTPAuth extends LoginMethod
{
/*! \brief Displayed name */
static function getLabel()
{
return _('HTTP Basic Auth');
}
/*! \brief All login steps in the right order for HTTP auth login */
static function loginProcess()
{
......@@ -53,9 +59,12 @@ class LoginHTTPAuth extends LoginMethod
}
}
static function active()
/*! \brief Return HTTP authentication header */
static function authenticateHeader($message = 'Authentication required')
{
global $config;
return ($config->get_cfg_value('httpAuthActivated') == 'TRUE');
header('WWW-Authenticate: Basic realm="FusionDirectory"');
header('HTTP/1.0 401 Unauthorized');
echo "$message\n";
exit;
}
}
......@@ -23,6 +23,12 @@
*/
class LoginHTTPHeader extends LoginMethod
{
/*! \brief Displayed name */
static function getLabel()
{
return _('HTTP Header');
}
/*! \brief All login steps in the right order for HTTP Header login */
static function loginProcess()
{
......@@ -85,12 +91,20 @@ class LoginHTTPHeader extends LoginMethod
if ($success) {
/* Everything went well, redirect to main.php */
static::redirect();
} else {
echo msg_dialog::get_dialogs();
if (!empty($message)) {
msg_dialog::display(
_('Error'),
sprintf(
_('Login with user "%s" triggered error: %s'),
static::$username,
$message
),
FATAL_ERROR_DIALOG
);
}
exit();
}
}
static function active()
{
global $config;
return ($config->get_cfg_value('httpHeaderAuthActivated') == 'TRUE');
}
}
......@@ -157,15 +157,6 @@ class LoginMethod
exit;
}
/*! \brief Return HTTP authentication header */
static function authenticateHeader($message = 'Authentication required')
{
header('WWW-Authenticate: Basic realm="FusionDirectory"');
header('HTTP/1.0 401 Unauthorized');
echo "$message\n";
exit;
}
/*! \brief Run each step in $steps, stop on errors */
static function runSteps($steps)
{
......@@ -184,20 +175,44 @@ class LoginMethod
/*! \brief All login steps in the right order */
static function loginProcess()
{
global $config;
$method = $config->get_cfg_value('LoginMethod', '');
if (empty($method)) {
// Try to detect configurations from FD<1.4
if ($config->get_cfg_value('httpAuthActivated') == 'TRUE') {
$method = 'LoginHTTPAuth';
} elseif ($config->get_cfg_value('casActivated') == 'TRUE') {
$method = 'LoginCAS';
} elseif ($config->get_cfg_value('httpHeaderAuthActivated') == 'TRUE') {
$method = 'LoginHTTPHeader';
} else {
$method = 'LoginPost';
}
}
$method::loginProcess();
}
static function active()
/*! \brief Displayed name for each login method. Returning FALSE disables a method */
static function getLabel()
{
return FALSE;
}
static function getMethods()
{
return array(
'LoginHTTPAuth',
$methods = array(
'LoginPost',
'LoginCAS',
'LoginHTTPAuth',
'LoginHTTPHeader',
'LoginPost',
);
$return = array();
foreach ($methods as $method) {
$label = $method::getLabel();
if ($label) {
$return[$method] = $label;
}
}
return $return;
}
}
......@@ -23,35 +23,128 @@
*/
class LoginPost extends LoginMethod
{
/*! \brief Displayed name */
static function getLabel()
{
return _('HTML form');
}
/*! \brief All login steps in the right order for standard POST login */
static function loginProcess()
{
global $config, $message;
global $smarty, $config, $message;
static::init();
/* Reset error messages */
$message = '';
static::$username = $_POST['username'];
static::$password = $_POST['password'];
$smarty->assign ('focusfield', 'username');
if (($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['login']) && isset($_POST['username']) && isset($_POST['password'])) {
$success = static::runSteps(array(
'validateUserInput',
'ldapLoginUser',
'checkForLockingBranch',
'loginAndCheckExpired',
'runSchemaCheck',
));
static::$username = $_POST['username'];
static::$password = $_POST['password'];
if ($success) {
/* Everything went well, redirect to main.php */
static::redirect();
$success = static::runSteps(array(
'validateUserInput',
'ldapLoginUser',
'checkForLockingBranch',
'loginAndCheckExpired',
'runSchemaCheck',
));
if ($success) {
/* Everything went well, redirect to main.php */
static::redirect();
}
}
/* Translation of cookie-warning. Whether to display it, is determined by JavaScript */
$smarty->assign('cookies', '<b>'._('Warning').':</b> '._('Your browser has cookies disabled. Please enable cookies and reload this page before logging in!'));
/* Set focus to the error button if we've an error message */
$focus = '';
if (session::is_set('errors') && session::get('errors') != '') {
$focus = '<script type="text/javascript">';
$focus .= 'document.forms[0].error_accept.focus();';
$focus .= '</script>';
}
$smarty->assign('focus', $focus);
static::displayLogin();
}
static function active()
/*! \brief Display the login page and exit() */
static protected function displayLogin()
{
return (($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['login']));
global $smarty,$message,$config,$ssl,$error_collector,$error_collector_mailto;
$lang = session::global_get('lang');
error_reporting(E_ALL | E_STRICT);
/* Fill template with required values */
$username = '';
if (isset($_POST['username'])) {
$username = trim($_POST['username']);
}
$smarty->assign('date', gmdate('D, d M Y H:i:s'));
$smarty->assign('username', $username);
$smarty->assign('revision', FD_VERSION);
$smarty->assign('year', date('Y'));
$smarty->append('css_files', get_template_path('login.css'));
/* Some error to display? */
if (!isset($message)) {
$message = '';
}
$smarty->assign('message', $message);
/* Display SSL mode warning? */
if (($ssl != '') && ($config->get_cfg_value('warnSSL') == 'TRUE')) {
$smarty->assign('ssl', sprintf(_('Warning: <a href="%s">Session is not encrypted!</a>'), $ssl));
} else {
$smarty->assign('ssl', '');
}
if (!$config->check_session_lifetime()) {
$smarty->assign('lifetime', _('Warning: The session lifetime configured in your fusiondirectory.conf will be overridden by php.ini settings.'));
} else {
$smarty->assign('lifetime', '');
}
/* Generate server list */
$servers = array();
if (isset($_POST['server'])) {
$selected = $_POST['server'];
} else {
$selected = $config->data['MAIN']['DEFAULT'];
}
foreach ($config->data['LOCATIONS'] as $key => $ignored) {
$servers[$key] = $key;
}
$smarty->assign('server_options', $servers);
$smarty->assign('server_id', $selected);
/* show login screen */
$smarty->assign('PHPSESSID', session_id());
if (session::is_set('errors')) {
$smarty->assign('errors', session::get('errors'));
}
if ($error_collector != '') {
$smarty->assign('php_errors', preg_replace('/%BUGBODY%/', $error_collector_mailto, $error_collector).'</div>');
} else {
$smarty->assign('php_errors', '');
}
$smarty->assign('msg_dialogs', msg_dialog::get_dialogs());
$smarty->assign('usePrototype', 'false');
$smarty->assign('date', date('l, dS F Y H:i:s O'));
$smarty->assign('lang', preg_replace('/_.*$/', '', $lang));
$smarty->assign('rtl', Language::isRTL($lang));
$smarty->display(get_template_path('headers.tpl'));
$smarty->assign('version', FD_VERSION);
$smarty->display(get_template_path('login.tpl'));
exit();
}
}
......@@ -211,15 +211,10 @@ class configInLdap extends simplePlugin
'fdSessionLifeTime', TRUE,
0 /*min*/, FALSE /*no max*/, 1800
),
new BooleanAttribute (
_('HTTP Basic authentication'), _('Use HTTP Basic authentication protocol instead of the login form.'),
'fdHttpAuthActivated', FALSE,
FALSE
),
new BooleanAttribute (
_('HTTP Header authentication'), _('Use HTTP Header authentication instead of the login form.'),
'fdHttpHeaderAuthActivated', FALSE,
FALSE
new SelectAttribute (
_('Login method'),
_('Which login method should be used for connecting to FusionDirectory'),
'fdLoginMethod', TRUE
),
new StringAttribute (
_('Header name'), _('Name of the header containing user identifier.'),
......@@ -251,11 +246,6 @@ class configInLdap extends simplePlugin
'cas' => array(
'name' => _('CAS'),
'attrs' => array(
new BooleanAttribute (
_('Enable CAS'), _('CAS login will be used instead of LDAP bind'),
'fdCasActivated', FALSE,
FALSE
),
new TrimmedStringAttribute (
_('CA certificate path'), _('Path to the CA certificate of the CAS server'),
'fdCasServerCaCertPath', FALSE,
......@@ -463,6 +453,10 @@ class configInLdap extends simplePlugin
$methods[] = 'sasl';
}
$attributesInfo['password']['attrs'][0]->setChoices($methods);
/* Login methods */
$methods = LoginMethod::getMethods();
$attributesInfo['login']['attrs'][4]->setChoices(array_keys($methods), array_values($methods));
//TODO Support previous settings
$groupsAndRoles = array_merge(
array_map(
......@@ -509,15 +503,6 @@ class configInLdap extends simplePlugin
$this->fusionConfigMd5 = md5_file(CACHE_DIR."/".CLASS_CACHE);
$this->attributesAccess['fdHttpAuthActivated']->setManagedAttributes(
array(
'erase' => array (
TRUE => array (
'fdCasActivated',
)
)
)
);
$this->attributesAccess['fdEnableSnapshots']->setManagedAttributes(
array(
'disable' => array (
......@@ -539,36 +524,52 @@ class configInLdap extends simplePlugin
)
)
);
$this->attributesAccess['fdCasActivated']->setManagedAttributes(
$this->attributesAccess['fdSplitPostalAddress']->setManagedAttributes(
array(
'disable' => array (
FALSE => array (
'fdCasServerCaCertPath',
'fdCasHost',
'fdCasPort',
'fdCasContext',
'fdPostalAddressPattern',
)
)
)
);
$this->attributesAccess['fdHttpHeaderAuthActivated']->setManagedAttributes(
$this->attributesAccess['fdLoginMethod']->setManagedAttributes(
array(
'disable' => array (
FALSE => array (
'multiplevalues' => array(
'noncas' => array(
'LoginPost',
'LoginHTTPAuth',
'LoginHTTPHeader',
),
'nonheader' => array(
'LoginPost',
'LoginCAS',
'LoginHTTPAuth'
),
),
'disable' => array(
'noncas' => array (
'fdCasServerCaCertPath',
'fdCasHost',
'fdCasPort',
'fdCasContext',
),
'nonheader' => array (
'fdHttpHeaderAuthHeaderName',
)
)
)
);
$this->attributesAccess['fdSplitPostalAddress']->setManagedAttributes(
array(
'disable' => array (
FALSE => array (
'fdPostalAddressPattern',
)
)
)
);
if (empty($this->attrs['fdLoginMethod'][0])) {
// Reading OBSOLETE attributes from FD<1.4 to ease migration
if (isset($this->attrs['fdHttpAuthActivated'][0]) && ($this->attrs['fdHttpAuthActivated'][0] == 'TRUE')) {
$this->fdLoginMethod = 'LoginHTTPAuth';
} elseif (isset($this->attrs['fdCasActivated'][0]) && ($this->attrs['fdCasActivated'][0] == 'TRUE')) {
$this->fdLoginMethod = 'LoginCAS';
} elseif (isset($this->attrs['fdHttpHeaderAuthActivated'][0]) && ($this->attrs['fdHttpHeaderAuthActivated'][0] == 'TRUE')) {
$this->fdLoginMethod = 'LoginHTTPHeader';
}
}
}
function compute_dn()
......
  • SonarQube analysis indicates that quality gate is failed.

    • Security Rating on New Code is passed: Actual value 1
    • Reliability Rating on New Code is passed: Actual value 1
    • Maintainability Rating on New Code is passed: Actual value 1
    • Duplicated Lines on New Code (%) is failed: Actual value 8.639587362991618 > 5

    SonarQube analysis reported no issues.