Merge pull request #400 from LDAPAccountManager/feature/390_no-default-passwords

Feature/390 no default passwords
This commit is contained in:
gruberroland 2025-01-13 19:56:34 +01:00 committed by GitHub
commit 1a489d6688
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 534 additions and 319 deletions

View file

@ -14,7 +14,6 @@ Configuration:
All settings can be edited via the webfrontend. Please All settings can be edited via the webfrontend. Please
point your browser to the LAM start page and then select point your browser to the LAM start page and then select
"LAM configuration". "LAM configuration".
The default password for the configuration is "lam".
Lamdaemon: Lamdaemon:

View file

@ -4,7 +4,7 @@
# This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) # This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
# Copyright (C) 2019 Felix Bartels # Copyright (C) 2019 Felix Bartels
# 2019 - 2024 Roland Gruber # 2019 - 2025 Roland Gruber
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -59,7 +59,6 @@ if [ "$LAM_SKIP_PRECONFIGURE" != "true" ]; then
LAM_CONFIGURATION_PASSWORD="${LAM_CONFIGURATION_PASSWORD:-}" LAM_CONFIGURATION_PASSWORD="${LAM_CONFIGURATION_PASSWORD:-}"
sed -i -f- /etc/ldap-account-manager/config.cfg <<- EOF sed -i -f- /etc/ldap-account-manager/config.cfg <<- EOF
s|"password": "[^"]*"|"password": "${LAM_PASSWORD_SSHA}"|;
s|"license": "[^"]*"|"license": "${LAM_LICENSE}"|; s|"license": "[^"]*"|"license": "${LAM_LICENSE}"|;
s|"configDatabaseType": "[^"]*"|"configDatabaseType": "${LAM_CONFIGURATION_DATABASE}"|; s|"configDatabaseType": "[^"]*"|"configDatabaseType": "${LAM_CONFIGURATION_DATABASE}"|;
s|"configDatabaseServer": "[^"]*"|"configDatabaseServer": "${LAM_CONFIGURATION_HOST}"|; s|"configDatabaseServer": "[^"]*"|"configDatabaseServer": "${LAM_CONFIGURATION_HOST}"|;
@ -68,6 +67,9 @@ if [ "$LAM_SKIP_PRECONFIGURE" != "true" ]; then
s|"configDatabaseUser": "[^"]*"|"configDatabaseUser": "${LAM_CONFIGURATION_USER}"|; s|"configDatabaseUser": "[^"]*"|"configDatabaseUser": "${LAM_CONFIGURATION_USER}"|;
s|"configDatabasePassword": "[^"]*"|"configDatabasePassword": "${LAM_CONFIGURATION_PASSWORD}"|; s|"configDatabasePassword": "[^"]*"|"configDatabasePassword": "${LAM_CONFIGURATION_PASSWORD}"|;
EOF EOF
if ! grep -e '"password":' /etc/ldap-account-manager/config.cfg > /dev/null; then
sed -i "2i\ \ \"password\": \"${LAM_PASSWORD_SSHA}\"," /etc/ldap-account-manager/config.cfg
fi
unset LAM_PASSWORD unset LAM_PASSWORD
set +e set +e
@ -82,12 +84,14 @@ EOF
sed -i -f- /var/lib/ldap-account-manager/config/lam.conf <<- EOF sed -i -f- /var/lib/ldap-account-manager/config/lam.conf <<- EOF
s|"ServerURL": "[^"]*"|"ServerURL": "${LDAP_SERVER}"|; s|"ServerURL": "[^"]*"|"ServerURL": "${LDAP_SERVER}"|;
s|"Admins": "[^"]*"|"Admins": "${LDAP_ADMIN_USER}"|; s|"Admins": "[^"]*"|"Admins": "${LDAP_ADMIN_USER}"|;
s|"Passwd": "[^"]*"|"Passwd": "${LAM_PASSWORD_SSHA}"|;
s|"treeViewSuffix": "[^"]*"|"treeViewSuffix": "${LDAP_BASE_DN}"|; s|"treeViewSuffix": "[^"]*"|"treeViewSuffix": "${LDAP_BASE_DN}"|;
s|"defaultLanguage": "[^"]*"|"defaultLanguage": "${LAM_LANG}.utf8"|; s|"defaultLanguage": "[^"]*"|"defaultLanguage": "${LAM_LANG}.utf8"|;
s|"suffix_user": "[^"]*"|"suffix_user": "${LDAP_USERS_DN}"|; s|"suffix_user": "[^"]*"|"suffix_user": "${LDAP_USERS_DN}"|;
s|"suffix_group": "[^"]*"|"suffix_group": "${LDAP_GROUPS_DN}"|; s|"suffix_group": "[^"]*"|"suffix_group": "${LDAP_GROUPS_DN}"|;
EOF EOF
if ! grep -e '"Passwd":' /var/lib/ldap-account-manager/config/lam.conf > /dev/null; then
sed -i "2i\ \ \"Passwd\": \"${LAM_PASSWORD_SSHA}\"," /var/lib/ldap-account-manager/config/lam.conf
fi
fi fi

View file

@ -1,5 +1,6 @@
March 2025 9.1 March 2025 9.1
- Usability improvements (348, 360) - Usability improvements (348, 360)
- Security: LAM no longer ships with any default passwords, main configuration password is requested on login if not yet set (#390)
- Fixed bugs: - Fixed bugs:
-> Ambiguous tooltip on profile editor for Shadow users (#394) -> Ambiguous tooltip on profile editor for Shadow users (#394)
-> Self service photo file enhancements (#396) -> Self service photo file enhancements (#396)

View file

@ -15,9 +15,6 @@ LAM - Readme
Installation and documentation: Installation and documentation:
Please see the LAM manual in docs/manual/index.html. Please see the LAM manual in docs/manual/index.html.
Default password:
The default password to edit the configuration options is "lam".
Download: Download:
You can get the newest version at https://www.ldap-account-manager.org/. You can get the newest version at https://www.ldap-account-manager.org/.

View file

@ -3,7 +3,6 @@
"useTLS": "yes", "useTLS": "yes",
"followReferrals": "false", "followReferrals": "false",
"pagedResults": "false", "pagedResults": "false",
"Passwd": "{CRYPT-SHA512}$6$ZJcXwaxHP0GQH0Rd$Ggkn8Wz\/8ntCM9v0TywomjkgSvV.3BoayFwnc9QP3MV.b7HWaqLOA8urP2e7HyEmU\/JmC8xR7jTqrXCHC4kFr. WkpjWHdheEhQMEdRSDBSZA==",
"Admins": "cn=Manager,dc=my-domain,dc=com", "Admins": "cn=Manager,dc=my-domain,dc=com",
"defaultLanguage": "en_GB.utf8", "defaultLanguage": "en_GB.utf8",
"scriptPath": "", "scriptPath": "",

View file

@ -1,5 +1,4 @@
{ {
"password": "{CRYPT-SHA512}$6$WheNHdlVwDoL4s.x$DrZ10TpIGQa5wd0jbvtm8eaTleJCf1nec3ihOaNwMdPUKVFCphXwtnTSmFFXjhGa45RlrSEWhDVyjLCMiV\/.c. V2hlTkhkbFZ3RG9MNHMueA==",
"default": "lam", "default": "lam",
"sessionTimeout": "30", "sessionTimeout": "30",
"hideLoginErrorDetails": "false", "hideLoginErrorDetails": "false",

View file

@ -3,7 +3,6 @@
"useTLS": "yes", "useTLS": "yes",
"followReferrals": "false", "followReferrals": "false",
"pagedResults": "false", "pagedResults": "false",
"Passwd": "{CRYPT-SHA512}$6$MUWJEkvtUY7G5sFA$QS6voQCksH9gNbbbQpjDKt65iez9bgKQI2x60DAffCK5.LO\/\/QfYTetQ6V2PlUR32CTkuhlSXSGXnH9scD\/zb0 TVVXSkVrdnRVWTdHNXNGQQ==",
"Admins": "cn=Manager,dc=my-domain,dc=com", "Admins": "cn=Manager,dc=my-domain,dc=com",
"defaultLanguage": "en_GB.utf8", "defaultLanguage": "en_GB.utf8",
"scriptPath": "", "scriptPath": "",

View file

@ -3,7 +3,6 @@
"useTLS": "no", "useTLS": "no",
"followReferrals": "false", "followReferrals": "false",
"pagedResults": "false", "pagedResults": "false",
"Passwd": "{CRYPT-SHA512}$6$zvb8WVEHSAKEGtGO$573kA9Us8LtGLLm5Gu87P\/vIiF\/2Ol\/DauzPmUpvC4eCL\/t0WWiwBaY19Rx5G3wzbeZWWlE1kp2fikrpZTZ51\/ enZiOFdWRUhTQUtFR3RHTw==",
"Admins": "cn=Manager,dc=my-domain,dc=com", "Admins": "cn=Manager,dc=my-domain,dc=com",
"defaultLanguage": "en_GB.utf8", "defaultLanguage": "en_GB.utf8",
"scriptPath": "", "scriptPath": "",

View file

@ -3,7 +3,6 @@
"useTLS": "no", "useTLS": "no",
"followReferrals": "false", "followReferrals": "false",
"pagedResults": "false", "pagedResults": "false",
"Passwd": "{CRYPT-SHA512}$6$9IWWua4lbp7uiLCC$AHPgST1YAm3yUAWKGeNZ5f9GCo1wBGyVo3MGvAt6.UOtQ9dYxs4WeQ4mlzjR30rD6cRayMNRBWqYFuBLvzn9T0 OUlXV3VhNGxicDd1aUxDQw==",
"Admins": "cn=Administrator,cn=users,dc=my-domain,dc=com", "Admins": "cn=Administrator,cn=users,dc=my-domain,dc=com",
"defaultLanguage": "en_GB.utf8", "defaultLanguage": "en_GB.utf8",
"scriptPath": "", "scriptPath": "",

View file

@ -28,27 +28,23 @@
<orderedlist> <orderedlist>
<listitem> <listitem>
<para>Locate config.cfg: On DEB/RPM installations it is in <para>Locate config.cfg: On DEB/RPM installations it is in
/usr/share/ldap-account-manager/config and for tar.bz2 in config <emphasis
role="bold">/usr/share/ldap-account-manager/config</emphasis> and
for tar.bz2 in <emphasis role="bold">config</emphasis>
folder.</para> folder.</para>
</listitem> </listitem>
<listitem> <listitem>
<para>Locate the "password" entry in the file</para> <para>Locate the "password" line in the file</para>
</listitem> </listitem>
<listitem> <listitem>
<para>Replace the password hash after "password: " with your new <para>Remove the password line in the configuration file</para>
clear-text password (e.g. "secret")</para>
</listitem> </listitem>
</orderedlist> </orderedlist>
<para>After the change the line should look like this:</para> <para>When you open LAM's start page you will now be asked to set a
new password.</para>
<literallayout>password: secret</literallayout>
<para>You can now login using your new password. Set the password once
again via GUI in main configuration settings. This will then put again
a hash value in the config.cfg file.</para>
</section> </section>
<section> <section>

View file

@ -44,11 +44,6 @@
<section id="generalSettings"> <section id="generalSettings">
<title>General settings</title> <title>General settings</title>
<para>After selecting "Edit general settings" you will need to enter the
<link linkend="a_configPasswords">master configuration password</link>.
The default password for new installations is "lam". Now you can edit the
general settings.</para>
<section> <section>
<title>Configuration Database</title> <title>Configuration Database</title>

View file

@ -87,26 +87,15 @@
<listitem> <listitem>
<para>Edge (max. 2 years old)</para> <para>Edge (max. 2 years old)</para>
</listitem> </listitem>
<listitem>
<para>Opera (max. 2 years old)</para>
</listitem>
</itemizedlist> </itemizedlist>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
<para>The default password to edit the configuration options is
"lam".</para>
<para><emphasis role="bold">License:</emphasis></para> <para><emphasis role="bold">License:</emphasis></para>
<para>LAM is published under the GNU General Public License. The complete <para>LAM is published under the GNU General Public License. The complete
list of licenses can be found in the copyright file.</para> list of licenses can be found in the copyright file.</para>
<para><emphasis role="bold">Default password:</emphasis></para>
<para>The default password for the LAM configuration is "lam".</para>
<literallayout> <literallayout>
Have fun! Have fun!
The LAM development team</literallayout> The LAM development team</literallayout>

View file

@ -12,7 +12,7 @@ use function LAM\TYPES\getScopeFromTypeId;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2024 Roland Gruber Copyright (C) 2003 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -50,6 +50,19 @@ include_once __DIR__ . "/types.inc";
/** 2-factor */ /** 2-factor */
include_once __DIR__ . '/2factor.inc'; include_once __DIR__ . '/2factor.inc';
/**
* Checks if the configuration password is secure.
*
* @param string $password password
* @return bool is secure
*/
function isValidConfigurationPassword(string $password): bool {
return preg_match('/[a-zA-Z]/', $password)
&& preg_match('/\d/', $password)
&& preg_match('/[^a-zA-Z0-9]/', $password)
&& (strlen($password) >= 8);
}
/** /**
* Sets the environment variables for custom SSL CA certificates. * Sets the environment variables for custom SSL CA certificates.
*/ */
@ -1453,6 +1466,15 @@ class LAMConfig {
return "{CRYPT-SHA512}" . crypt($password, '$6$' . $salt) . " " . base64_encode($salt); return "{CRYPT-SHA512}" . crypt($password, '$6$' . $salt) . " " . base64_encode($salt);
} }
/**
* Returns if the server profile has a password set.
*
* @return bool password is set
*/
public function hasPasswordSet(): bool {
return ($this->Passwd != null) && ($this->Passwd !== '');
}
/** /**
* Returns the LDAP suffix for the given account type * Returns the LDAP suffix for the given account type
* *
@ -3242,8 +3264,9 @@ class LAMCfgMain {
/** /**
* Saves the configuration to the persistence layer. * Saves the configuration to the persistence layer.
* @throws LAMException error saving config
*/ */
public function save() { public function save(): void {
if ($this->configDatabaseType === self::DATABASE_MYSQL) { if ($this->configDatabaseType === self::DATABASE_MYSQL) {
$this->saveLocal(true); $this->saveLocal(true);
$this->saveDb(); $this->saveDb();
@ -3261,7 +3284,7 @@ class LAMCfgMain {
@chmod($sslPath, 0600); @chmod($sslPath, 0600);
} }
else { else {
StatusMessage("ERROR", _("Cannot write certificate file. Please check the permissions of config/serverCerts.pem.")); throw new LAMException(_("Cannot write certificate file. Please check the permissions of config/serverCerts.pem."));
} }
} }
// delete SSL certificate // delete SSL certificate
@ -3269,7 +3292,7 @@ class LAMCfgMain {
$sslPath = $this->getInternalSSLCaCertFileName(); $sslPath = $this->getInternalSSLCaCertFileName();
$result = @unlink($sslPath); $result = @unlink($sslPath);
if (!$result) { if (!$result) {
StatusMessage("ERROR", _("Cannot write certificate file. Please check the permissions of config/serverCerts.pem.")); throw new LAMException(_("Cannot write certificate file. Please check the permissions of config/serverCerts.pem."));
} }
} }
} }
@ -3296,6 +3319,7 @@ class LAMCfgMain {
* Saves preferences to config file config.cfg * Saves preferences to config file config.cfg
* *
* @param bool $persistenceOnly store only persistence related data * @param bool $persistenceOnly store only persistence related data
* @throws LAMException error saving config
*/ */
public function saveLocal(bool $persistenceOnly): void { public function saveLocal(bool $persistenceOnly): void {
$data = $persistenceOnly ? $this->exportPersistenceData() : $this->exportData(); $data = $persistenceOnly ? $this->exportPersistenceData() : $this->exportData();
@ -3308,10 +3332,19 @@ class LAMCfgMain {
chmod($this->conffile, 0600); chmod($this->conffile, 0600);
} }
else { else {
StatusMessage("ERROR", "", _("Cannot open config file!") . " (" . $this->conffile . ")"); throw new LAMException(_("Cannot open config file!") . " (" . $this->conffile . ")");
} }
} }
/**
* Returns if the main config has a password set.
*
* @return bool password is set
*/
public function hasPasswordSet(): bool {
return ($this->password != null) && ($this->password !== '');
}
/** /**
* Sets a new config password. * Sets a new config password.
* *

View file

@ -5,12 +5,13 @@
# Julio C. Ortega, 2022-2024 # Julio C. Ortega, 2022-2024
# Julio C. Ortega, 2015-2021 # Julio C. Ortega, 2015-2021
# Leandro Lattanzio, 2023 # Leandro Lattanzio, 2023
# Roland Gruber <post@rolandgruber.de>, 2025
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: LDAP Account Manager\n" "Project-Id-Version: LDAP Account Manager\n"
"Report-Msgid-Bugs-To: post@rolandgruber.de\n" "Report-Msgid-Bugs-To: post@rolandgruber.de\n"
"PO-Revision-Date: 2011-09-29 18:53+0000\n" "PO-Revision-Date: 2011-09-29 18:53+0000\n"
"Last-Translator: Julio C. Ortega, 2022-2024\n" "Last-Translator: Roland Gruber <post@rolandgruber.de>, 2025\n"
"Language-Team: Spanish (Spain) (http://app.transifex.com/gruberroland/lam/language/es_ES/)\n" "Language-Team: Spanish (Spain) (http://app.transifex.com/gruberroland/lam/language/es_ES/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -8775,11 +8776,11 @@ msgstr "Móvil"
#: ../lib/modules/inetOrgPerson.inc:3760 ../lib/modules/inetOrgPerson.inc:3802 #: ../lib/modules/inetOrgPerson.inc:3760 ../lib/modules/inetOrgPerson.inc:3802
#: ../lib/modules/inetOrgPerson.inc:4147 #: ../lib/modules/inetOrgPerson.inc:4147
msgid "Mobile number" msgid "Mobile number"
msgstr "Numero de ḿmóbil" msgstr "Número de móvil"
#: ../lib/modules/inetOrgPerson.inc:2751 #: ../lib/modules/inetOrgPerson.inc:2751
msgid "Mobile telephone number" msgid "Mobile telephone number"
msgstr "Número de teléfono movil" msgstr "Número de teléfono móvil"
#: ../lib/modules/qmailGroup.inc:183 ../lib/modules/qmailGroup.inc:187 #: ../lib/modules/qmailGroup.inc:183 ../lib/modules/qmailGroup.inc:187
#: ../lib/modules/qmailGroup.inc:316 ../lib/modules/qmailGroup.inc:376 #: ../lib/modules/qmailGroup.inc:316 ../lib/modules/qmailGroup.inc:376
@ -16357,7 +16358,7 @@ msgstr "Número de Fax del usuario."
#: ../lib/modules/windowsLDSUser.inc:312 ../lib/modules/inetOrgPerson.inc:648 #: ../lib/modules/windowsLDSUser.inc:312 ../lib/modules/inetOrgPerson.inc:648
#: ../lib/modules/inetOrgPerson.inc:652 ../lib/modules/windowsUser.inc:423 #: ../lib/modules/inetOrgPerson.inc:652 ../lib/modules/windowsUser.inc:423
msgid "The user's mobile number." msgid "The user's mobile number."
msgstr "Numero de móvil del usuario." msgstr "Número de móvil del usuario."
#: ../lib/modules/windowsLDSUser.inc:284 ../lib/modules/windowsLDSUser.inc:288 #: ../lib/modules/windowsLDSUser.inc:284 ../lib/modules/windowsLDSUser.inc:288
#: ../lib/modules/inetOrgPerson.inc:780 ../lib/modules/inetOrgPerson.inc:784 #: ../lib/modules/inetOrgPerson.inc:780 ../lib/modules/inetOrgPerson.inc:784

View file

@ -1,22 +1,22 @@
<?php <?php
namespace LAM\CONFIG; namespace LAM\CONFIG;
use \htmlStatusMessage; use htmlStatusMessage;
use \htmlResponsiveRow; use htmlResponsiveRow;
use \LAMCfgMain; use LAMCfgMain;
use \htmlButton; use htmlButton;
use \htmlOutputText; use htmlOutputText;
use \htmlLink; use htmlLink;
use \htmlDiv; use htmlDiv;
use \htmlResponsiveSelect; use htmlResponsiveSelect;
use \htmlResponsiveInputField; use htmlResponsiveInputField;
use \htmlHorizontalLine; use htmlHorizontalLine;
use LAMException; use LAMException;
use ServerProfilePersistenceManager; use ServerProfilePersistenceManager;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2023 Roland Gruber Copyright (C) 2003 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -74,18 +74,29 @@ for ($i = 0; $i < count($sessionKeys); $i++) {
echo $_SESSION['header']; echo $_SESSION['header'];
$serverProfilePersistenceManager = new ServerProfilePersistenceManager(); $serverProfilePersistenceManager = new ServerProfilePersistenceManager();
$files = []; $profileNames = [];
$profileNamesWithoutPassword = [];
try { try {
$files = $serverProfilePersistenceManager->getProfiles(); $profileNames = $serverProfilePersistenceManager->getProfiles();
foreach ($profileNames as $profileName) {
$profile = $serverProfilePersistenceManager->loadProfile($profileName);
if (!$profile->hasPasswordSet()) {
$profileNamesWithoutPassword[] = $profileName;
}
}
} }
catch (LAMException $e) { catch (LAMException $e) {
logNewMessage(LOG_ERR, 'Unable to read server profiles: ' . $e->getTitle()); logNewMessage(LOG_ERR, 'Unable to read server profiles: ' . $e->getTitle());
} }
printHeaderContents(_("Login"), '../..'); printHeaderContents(_("Login"), '../..');
if (count($files) < 1) { if (count($profileNames) < 1) {
$message = new htmlStatusMessage('INFO', _("No server profiles found. Please create one.")); $message = new htmlStatusMessage('INFO', _("No server profiles found. Please create one."));
} }
if ($profileNamesWithoutPassword !== []) {
$message = new htmlStatusMessage('INFO', _("There is at least one server profile without password. Please click on the manage server profiles link to set a password."),
htmlspecialchars(implode(', ', $profileNamesWithoutPassword)));
}
?> ?>
</head> </head>
<body> <body>
@ -128,36 +139,34 @@ printJsIncludes('../..');
// message // message
if ($message !== null) { if ($message !== null) {
$row->add($message, 12); $row->add($message);
$row->addVerticalSpacer('2rem'); $row->addVerticalSpacer('2rem');
} }
$box = new htmlResponsiveRow(); $box = new htmlResponsiveRow();
if (count($files) > 0) { if (count($profileNames) > 0) {
$box->add(new htmlOutputText(_("Please enter your password to change the server preferences:")), 12); $box->add(new htmlOutputText(_("Please enter your password to change the server preferences:")));
$box->addVerticalSpacer('1.5rem'); $box->addVerticalSpacer('1.5rem');
$conf = new LAMCfgMain(); $conf = new LAMCfgMain();
$selectedProfile = []; $selectedProfile = [];
$profilesExisting = false; if (!empty($_COOKIE["lam_default_profile"]) && in_array($_COOKIE["lam_default_profile"], $profileNames)) {
$profiles = $files;
if (!empty($_COOKIE["lam_default_profile"]) && in_array($_COOKIE["lam_default_profile"], $files)) {
$selectedProfile[] = $_COOKIE["lam_default_profile"]; $selectedProfile[] = $_COOKIE["lam_default_profile"];
} }
else { else {
$selectedProfile[] = $conf->default; $selectedProfile[] = $conf->default;
} }
$box->add(new htmlResponsiveSelect('filename', $profiles, $selectedProfile, _('Profile name')), 12); $box->add(new htmlResponsiveSelect('filename', $profileNames, $selectedProfile, _('Profile name')));
$passwordInput = new htmlResponsiveInputField(_('Password'), 'passwd', '', '200'); $passwordInput = new htmlResponsiveInputField(_('Password'), 'passwd', '', '200');
$passwordInput->setIsPassword(true); $passwordInput->setIsPassword(true);
$passwordInput->setCSSClasses(['lam-initial-focus']); $passwordInput->setCSSClasses(['lam-initial-focus']);
$box->add($passwordInput, 12); $box->add($passwordInput);
$box->addVerticalSpacer('1rem'); $box->addVerticalSpacer('1rem');
$button = new htmlButton('submit', _("Ok")); $button = new htmlButton('submit', _("Ok"));
$button->setCSSClasses(['lam-primary']); $button->setCSSClasses(['lam-primary']);
$box->addLabel($button); $box->addLabel($button);
$box->add(new htmlOutputText(''), 0, 6); $box->add(new htmlOutputText(''), 0, 6);
$box->addVerticalSpacer('1.5rem'); $box->addVerticalSpacer('1.5rem');
$box->add(new htmlHorizontalLine(), 12); $box->add(new htmlHorizontalLine());
$box->addVerticalSpacer('1.5rem'); $box->addVerticalSpacer('1.5rem');
} }
$manageLink = new htmlLink(_("Manage server profiles"), 'profmanage.php'); $manageLink = new htmlLink(_("Manage server profiles"), 'profmanage.php');
@ -165,7 +174,7 @@ printJsIncludes('../..');
$boxDiv = new htmlDiv(null, $box); $boxDiv = new htmlDiv(null, $box);
$boxDiv->setCSSClasses(['roundedShadowBox', 'limitWidth', 'text-center']); $boxDiv->setCSSClasses(['roundedShadowBox', 'limitWidth', 'text-center']);
$row->add($boxDiv, 12); $row->add($boxDiv);
// back link // back link
$row->addVerticalSpacer('2rem'); $row->addVerticalSpacer('2rem');

View file

@ -27,7 +27,7 @@ use ServerProfilePersistenceManager;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2023 Roland Gruber Copyright (C) 2003 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -842,10 +842,13 @@ function checkInput(): array {
$conf->setTwoFactorRememberDevicePassword($_POST['twoFactorRememberDevicePassword']); $conf->setTwoFactorRememberDevicePassword($_POST['twoFactorRememberDevicePassword']);
} }
// check if password was changed // check if password was changed
if (isset($_POST['passwd1']) && ($_POST['passwd1'] != '')) { if (!empty($_POST['passwd1'])) {
if ($_POST['passwd1'] != $_POST['passwd2']) { if ($_POST['passwd1'] !== $_POST['passwd2']) {
$errors[] = ["ERROR", _("Passwords are different!")]; $errors[] = ["ERROR", _("Passwords are different!")];
} }
elseif (!isValidConfigurationPassword($_POST['passwd1'])) {
$errors[] = ["ERROR", _('Profile password'), _('Please enter at least 8 characters including letters, a number and a symbol.')];
}
else { else {
// set new password // set new password
$conf->set_Passwd($_POST['passwd1']); $conf->set_Passwd($_POST['passwd1']);

View file

@ -4,28 +4,28 @@ namespace LAM\CONFIG;
use htmlJavaScript; use htmlJavaScript;
use htmlResponsiveTable; use htmlResponsiveTable;
use LAM\LOGIN\WEBAUTHN\WebauthnManager; use LAM\LOGIN\WEBAUTHN\WebauthnManager;
use \LAMCfgMain; use LAMCfgMain;
use \htmlTable; use htmlTable;
use \htmlTitle; use htmlTitle;
use \htmlStatusMessage; use htmlStatusMessage;
use \htmlSubTitle; use htmlSubTitle;
use \htmlSpacer; use htmlSpacer;
use \htmlOutputText; use htmlOutputText;
use \htmlLink; use htmlLink;
use \htmlGroup; use htmlGroup;
use \htmlButton; use htmlButton;
use \htmlHelpLink; use htmlHelpLink;
use \htmlInputField; use htmlInputField;
use \htmlInputFileUpload; use htmlInputFileUpload;
use \DateTime; use DateTime;
use \DateTimeZone; use DateTimeZone;
use \htmlResponsiveRow; use htmlResponsiveRow;
use \htmlResponsiveInputTextarea; use htmlResponsiveInputTextarea;
use \htmlResponsiveSelect; use htmlResponsiveSelect;
use \htmlResponsiveInputCheckbox; use htmlResponsiveInputCheckbox;
use \htmlResponsiveInputField; use htmlResponsiveInputField;
use \htmlDiv; use htmlDiv;
use \htmlHiddenInput; use htmlHiddenInput;
use LAMException; use LAMException;
use LamTemporaryFilesManager; use LamTemporaryFilesManager;
use PDO; use PDO;
@ -33,7 +33,7 @@ use PDO;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2024 Roland Gruber Copyright (C) 2003 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -114,7 +114,8 @@ printHeaderContents(_("Edit general settings"), '../..');
<?php <?php
if (is_dir(__DIR__ . '/../../docs/manual')) { if (is_dir(__DIR__ . '/../../docs/manual')) {
?> ?>
<a class="lam-header-right lam-menu-icon hide-on-tablet" href="javascript:void(0);" class="icon" onclick="window.lam.topmenu.toggle();"> <a class="lam-header-right lam-menu-icon hide-on-tablet" href="javascript:void(0);" class="icon"
onclick="window.lam.topmenu.toggle();">
<img class="align-middle" width="16" height="16" alt="menu" src="../../graphics/menu.svg"> <img class="align-middle" width="16" height="16" alt="menu" src="../../graphics/menu.svg">
<span class="padding0">&nbsp;</span> <span class="padding0">&nbsp;</span>
</a> </a>
@ -134,37 +135,41 @@ $errors = [];
$messages = []; $messages = [];
// check if submit button was pressed // check if submit button was pressed
if (isset($_POST['submitFormData'])) { if (isset($_POST['submitFormData'])) {
if (extension_loaded('PDO')) { if (extension_loaded('PDO')) {
// set database // set database
$cfg->configDatabaseType = $_POST['configDatabaseType']; $cfg->configDatabaseType = $_POST['configDatabaseType'];
$cfg->configDatabaseServer = $_POST['configDatabaseServer']; $cfg->configDatabaseServer = $_POST['configDatabaseServer'];
$cfg->configDatabasePort = $_POST['configDatabasePort']; $cfg->configDatabasePort = $_POST['configDatabasePort'];
$cfg->configDatabaseName = $_POST['configDatabaseName']; $cfg->configDatabaseName = $_POST['configDatabaseName'];
$cfg->configDatabaseUser = $_POST['configDatabaseUser']; $cfg->configDatabaseUser = $_POST['configDatabaseUser'];
$cfg->configDatabasePassword = $_POST['configDatabasePassword']; $cfg->configDatabasePassword = $_POST['configDatabasePassword'];
if ($cfg->configDatabaseType === LAMCfgMain::DATABASE_MYSQL) { if ($cfg->configDatabaseType === LAMCfgMain::DATABASE_MYSQL) {
if (empty($cfg->configDatabaseServer) || !get_preg($cfg->configDatabaseServer, 'hostname')) { if (empty($cfg->configDatabaseServer) || !get_preg($cfg->configDatabaseServer, 'hostname')) {
$errors[] = _('Please enter a valid database host name.'); $errors[] = _('Please enter a valid database host name.');
} }
if (empty($cfg->configDatabaseName)) { if (empty($cfg->configDatabaseName)) {
$errors[] = _('Please enter a valid database name.'); $errors[] = _('Please enter a valid database name.');
} }
if (empty($cfg->configDatabaseUser)) { if (empty($cfg->configDatabaseUser)) {
$errors[] = _('Please enter a valid database user.'); $errors[] = _('Please enter a valid database user.');
} }
if (empty($cfg->configDatabasePassword)) { if (empty($cfg->configDatabasePassword)) {
$errors[] = _('Please enter a valid database password.'); $errors[] = _('Please enter a valid database password.');
} }
} }
} }
// set master password // set master password
if (isset($_POST['masterpassword']) && ($_POST['masterpassword'] != "")) { if (!empty($_POST['masterpassword'])) {
if ($_POST['masterpassword'] && $_POST['masterpassword2'] && ($_POST['masterpassword'] == $_POST['masterpassword2'])) { if (($_POST['masterpassword'] !== $_POST['masterpassword2'])) {
$errors[] = _("Master passwords are different.");
}
elseif (!isValidConfigurationPassword($_POST['masterpassword'])) {
$errors[] = _('Please enter at least 8 characters including letters, a number and a symbol.');
}
else {
$cfg->setPassword($_POST['masterpassword']); $cfg->setPassword($_POST['masterpassword']);
$msg = _("New master password set successfully."); $msg = _("New master password set successfully.");
unset($_SESSION["mainconf_password"]); unset($_SESSION["mainconf_password"]);
} else {
$errors[] = _("Master passwords are different or empty!");
} }
} }
// set license // set license
@ -176,19 +181,19 @@ if (isset($_POST['submitFormData'])) {
$cfg->licenseEmailFrom = $_POST['licenseEmailFrom']; $cfg->licenseEmailFrom = $_POST['licenseEmailFrom'];
$cfg->licenseEmailTo = $_POST['licenseEmailTo']; $cfg->licenseEmailTo = $_POST['licenseEmailTo'];
if ((($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_EMAIL) || ($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_ALL)) if ((($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_EMAIL) || ($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_ALL))
&& !get_preg($cfg->licenseEmailFrom, 'email')) { && !get_preg($cfg->licenseEmailFrom, 'email')) {
$errors[] = _('Licence') . ': ' . _('From address') . ' - ' . _('Please enter a valid email address!'); $errors[] = _('Licence') . ': ' . _('From address') . ' - ' . _('Please enter a valid email address!');
} }
if (($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_EMAIL) || ($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_ALL)) { if (($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_EMAIL) || ($cfg->licenseWarningType === LAMCfgMain::LICENSE_WARNING_ALL)) {
$toEmails = preg_split('/;[ ]*/', $cfg->licenseEmailTo); $toEmails = preg_split('/;[ ]*/', $cfg->licenseEmailTo);
if ($toEmails !== false) { if ($toEmails !== false) {
foreach ($toEmails as $toEmail) { foreach ($toEmails as $toEmail) {
if (!get_preg($toEmail, 'email')) { if (!get_preg($toEmail, 'email')) {
$errors[] = _('Licence') . ': ' . _('To address') . ' - ' . _('Please enter a valid email address!'); $errors[] = _('Licence') . ': ' . _('To address') . ' - ' . _('Please enter a valid email address!');
break; break;
} }
} }
} }
} }
} }
// set session timeout // set session timeout
@ -213,11 +218,12 @@ if (isset($_POST['submitFormData'])) {
} }
} }
$allowedHosts = implode(",", $allowedHostsList); $allowedHosts = implode(",", $allowedHostsList);
} else { }
else {
$allowedHosts = ""; $allowedHosts = "";
} }
$cfg->allowedHosts = $allowedHosts; $cfg->allowedHosts = $allowedHosts;
// set allowed hosts for self service // set allowed hosts for self-service
if (isLAMProVersion()) { if (isLAMProVersion()) {
if (isset($_POST['allowedHostsSelfService'])) { if (isset($_POST['allowedHostsSelfService'])) {
$allowedHostsSelfService = $_POST['allowedHostsSelfService']; $allowedHostsSelfService = $_POST['allowedHostsSelfService'];
@ -237,7 +243,8 @@ if (isset($_POST['submitFormData'])) {
} }
$allowedHostsSelfServiceList = array_unique($allowedHostsSelfServiceList); $allowedHostsSelfServiceList = array_unique($allowedHostsSelfServiceList);
$allowedHostsSelfService = implode(",", $allowedHostsSelfServiceList); $allowedHostsSelfService = implode(",", $allowedHostsSelfServiceList);
} else { }
else {
$allowedHostsSelfService = ""; $allowedHostsSelfService = "";
} }
$cfg->allowedHostsSelfService = $allowedHostsSelfService; $cfg->allowedHostsSelfService = $allowedHostsSelfService;
@ -247,34 +254,37 @@ if (isset($_POST['submitFormData'])) {
// set log destination // set log destination
if ($_POST['logDestination'] == "none") { if ($_POST['logDestination'] == "none") {
$cfg->logDestination = "NONE"; $cfg->logDestination = "NONE";
} elseif ($_POST['logDestination'] == "syslog") { }
elseif ($_POST['logDestination'] == "syslog") {
$cfg->logDestination = "SYSLOG"; $cfg->logDestination = "SYSLOG";
} elseif ($_POST['logDestination'] == "remote") { }
elseif ($_POST['logDestination'] == "remote") {
$cfg->logDestination = "REMOTE:" . $_POST['logRemote']; $cfg->logDestination = "REMOTE:" . $_POST['logRemote'];
$remoteParts = explode(':', $_POST['logRemote']); $remoteParts = explode(':', $_POST['logRemote']);
if ((count($remoteParts) !== 2) || !get_preg($remoteParts[0], 'DNSname') || !get_preg($remoteParts[1], 'digit')) { if ((count($remoteParts) !== 2) || !get_preg($remoteParts[0], 'DNSname') || !get_preg($remoteParts[1], 'digit')) {
$errors[] = _("Please enter a valid remote server in format \"server:port\"."); $errors[] = _("Please enter a valid remote server in format \"server:port\".");
} }
} else { }
$isValidLogFile = isset($_POST['logFile']) && LAMCfgMain::isValidLogFilename($_POST['logFile']); else {
$isValidLogFile = isset($_POST['logFile']) && LAMCfgMain::isValidLogFilename($_POST['logFile']);
$blockedPrefixes = ['/usr', '/etc', '/dev', '/boot', '/lib', '/proc', '/root', '/run', '/sys', '/snap']; $blockedPrefixes = ['/usr', '/etc', '/dev', '/boot', '/lib', '/proc', '/root', '/run', '/sys', '/snap'];
if (!empty($_SERVER['DOCUMENT_ROOT'])) { if (!empty($_SERVER['DOCUMENT_ROOT'])) {
$blockedPrefixes[] = $_SERVER['DOCUMENT_ROOT']; $blockedPrefixes[] = $_SERVER['DOCUMENT_ROOT'];
} }
foreach ($blockedPrefixes as $blockedPrefix) { foreach ($blockedPrefixes as $blockedPrefix) {
if (!$isValidLogFile) { if (!$isValidLogFile) {
break; break;
} }
if (str_starts_with($_POST['logFile'], $blockedPrefix)) { if (str_starts_with($_POST['logFile'], $blockedPrefix)) {
$isValidLogFile = false; $isValidLogFile = false;
} }
} }
if ($isValidLogFile) { if ($isValidLogFile) {
$cfg->logDestination = $_POST['logFile']; $cfg->logDestination = $_POST['logFile'];
} }
else { else {
$errors[] = _("The log file is empty or contains invalid characters! Valid characters are: a-z, A-Z, 0-9, /, ., _ and -. The file must end with '.log' or '.txt'."); $errors[] = _("The log file is empty or contains invalid characters! Valid characters are: a-z, A-Z, 0-9, /, ., _ and -. The file must end with '.log' or '.txt'.");
} }
} }
// password policies // password policies
$cfg->passwordMinLength = $_POST['passwordMinLength']; $cfg->passwordMinLength = $_POST['passwordMinLength'];
@ -315,8 +325,8 @@ if (isset($_POST['submitFormData'])) {
else { else {
$messages[] = _('You might need to restart your webserver for changes to take effect.'); $messages[] = _('You might need to restart your webserver for changes to take effect.');
} }
} }
} }
} }
} }
if (isset($_POST['sslCaCertDelete'])) { if (isset($_POST['sslCaCertDelete'])) {
@ -327,7 +337,7 @@ if (isset($_POST['submitFormData'])) {
$matches = []; $matches = [];
if (preg_match('/^ldaps:\/\/([a-zA-Z0-9_.-]+)(:(\d+))?$/', $_POST['serverurl'], $matches)) { if (preg_match('/^ldaps:\/\/([a-zA-Z0-9_.-]+)(:(\d+))?$/', $_POST['serverurl'], $matches)) {
$port = '636'; $port = '636';
if (isset($matches[3]) && !empty($matches[3])) { if (!empty($matches[3])) {
$port = $matches[3]; $port = $matches[3];
} }
$pemResult = getLDAPSSLCertificate($matches[1], $port); $pemResult = getLDAPSSLCertificate($matches[1], $port);
@ -335,10 +345,12 @@ if (isset($_POST['submitFormData'])) {
$messages[] = _('Imported certificate from server.'); $messages[] = _('Imported certificate from server.');
$messages[] = _('You might need to restart your webserver for changes to take effect.'); $messages[] = _('You might need to restart your webserver for changes to take effect.');
$cfg->uploadSSLCaCert($pemResult); $cfg->uploadSSLCaCert($pemResult);
} else { }
else {
$errors[] = _('Unable to import server certificate. Please use the upload function.'); $errors[] = _('Unable to import server certificate. Please use the upload function.');
} }
} else { }
else {
$errors[] = _('Invalid server name. Please enter "server" or "server:port".'); $errors[] = _('Invalid server name. Please enter "server" or "server:port".');
} }
} }
@ -355,19 +367,19 @@ if (isset($_POST['submitFormData'])) {
$cfg->mailEncryption = $_POST['mailEncryption']; $cfg->mailEncryption = $_POST['mailEncryption'];
$cfg->mailServer = $_POST['mailServer']; $cfg->mailServer = $_POST['mailServer'];
if (!empty($cfg->mailServer) && !get_preg($cfg->mailServer, 'hostAndPort')) { if (!empty($cfg->mailServer) && !get_preg($cfg->mailServer, 'hostAndPort')) {
$errors[] = _('Please enter the mail server with host name and port.'); $errors[] = _('Please enter the mail server with host name and port.');
} }
$mailAttribute = strtolower($_POST['mailAttribute']); $mailAttribute = strtolower($_POST['mailAttribute']);
$mailBackupAttribute = strtolower($_POST['mailBackupAttribute']); $mailBackupAttribute = strtolower($_POST['mailBackupAttribute']);
if (empty($mailAttribute)) { if (empty($mailAttribute)) {
$cfg->mailAttribute = LAMCfgMain::MAIL_ATTRIBUTE_DEFAULT; $cfg->mailAttribute = LAMCfgMain::MAIL_ATTRIBUTE_DEFAULT;
} }
elseif (preg_match('/^[a-z0-9_-]+$/', $mailAttribute)) { elseif (preg_match('/^[a-z0-9_-]+$/', $mailAttribute)) {
$cfg->mailAttribute = $mailAttribute; $cfg->mailAttribute = $mailAttribute;
} }
else { else {
$errors[] = _('The mail attributes are invalid.'); $errors[] = _('The mail attributes are invalid.');
} }
if (empty($mailBackupAttribute)) { if (empty($mailBackupAttribute)) {
$cfg->mailBackupAttribute = LAMCfgMain::MAIL_BACKUP_ATTRIBUTE_DEFAULT; $cfg->mailBackupAttribute = LAMCfgMain::MAIL_BACKUP_ATTRIBUTE_DEFAULT;
} }
@ -379,22 +391,25 @@ if (isset($_POST['submitFormData'])) {
} }
} }
$cfg->errorReporting = $_POST['errorReporting']; $cfg->errorReporting = $_POST['errorReporting'];
// module settings // module settings
$allModules = getAllModules(); $allModules = getAllModules();
$moduleSettings = $cfg->getModuleSettings(); $moduleSettings = $cfg->getModuleSettings();
foreach ($allModules as $module) { foreach ($allModules as $module) {
$module->checkGlobalConfigOptions($moduleSettings, $messages, $errors); $module->checkGlobalConfigOptions($moduleSettings, $messages, $errors);
} }
$cfg->setModuleSettings($moduleSettings); $cfg->setModuleSettings($moduleSettings);
// save settings // save settings
if (isset($_POST['submit'])) { if (isset($_POST['submit']) && empty($errors)) {
$cfg->save(); try {
if (empty($errors)) { $cfg->save();
$scriptTag = new htmlJavaScript('window.lam.dialog.showSuccessMessageAndRedirect("' . _("Your settings were successfully saved.") . '", "", "' . _('Ok') . '", "../login.php")'); $scriptTag = new htmlJavaScript('window.lam.dialog.showSuccessMessageAndRedirect("' . _("Your settings were successfully saved.") . '", "", "' . _('Ok') . '", "../login.php")');
parseHtml(null, $scriptTag, [], false, null); parseHtml(null, $scriptTag, [], false, null);
echo '</body></html>'; echo '</body></html>';
exit(); exit();
} }
catch (LAMException $e) {
$errors[] = $e->getTitle();
}
} }
} }
@ -415,82 +430,82 @@ if (isset($_POST['submitFormData'])) {
// check if config file is writable // check if config file is writable
if (!$cfg->isWritable()) { if (!$cfg->isWritable()) {
$row->add(new htmlStatusMessage('WARN', _('The config file is not writable.'), _('Your changes cannot be saved until you make the file writable for the webserver user.')), 12); $row->add(new htmlStatusMessage('WARN', _('The config file is not writable.'), _('Your changes cannot be saved until you make the file writable for the webserver user.')));
} }
// database // database
if (extension_loaded('PDO')) { if (extension_loaded('PDO')) {
$row->add(new htmlSubTitle(_('Configuration storage')), 12); $row->add(new htmlSubTitle(_('Configuration storage')));
$storageProviders = [ $storageProviders = [
_('Local file system') => LAMCfgMain::DATABASE_FILE_SYSTEM _('Local file system') => LAMCfgMain::DATABASE_FILE_SYSTEM
]; ];
if (in_array('mysql', PDO::getAvailableDrivers())) { if (in_array('mysql', PDO::getAvailableDrivers())) {
$storageProviders['MySQL'] = LAMCfgMain::DATABASE_MYSQL; $storageProviders['MySQL'] = LAMCfgMain::DATABASE_MYSQL;
} }
$storageProviderSelect = new htmlResponsiveSelect('configDatabaseType', $storageProviders, [$cfg->configDatabaseType], _('Database type'), '293'); $storageProviderSelect = new htmlResponsiveSelect('configDatabaseType', $storageProviders, [$cfg->configDatabaseType], _('Database type'), '293');
$storageProviderSelect->setHasDescriptiveElements(true); $storageProviderSelect->setHasDescriptiveElements(true);
$dbRowsToShow = [ $dbRowsToShow = [
LAMCfgMain::DATABASE_FILE_SYSTEM => [], LAMCfgMain::DATABASE_FILE_SYSTEM => [],
LAMCfgMain::DATABASE_MYSQL => ['configDatabaseServer', 'configDatabasePort', 'configDatabaseName', 'configDatabaseUser', 'configDatabasePassword'] LAMCfgMain::DATABASE_MYSQL => ['configDatabaseServer', 'configDatabasePort', 'configDatabaseName', 'configDatabaseUser', 'configDatabasePassword']
]; ];
$storageProviderSelect->setTableRowsToShow($dbRowsToShow); $storageProviderSelect->setTableRowsToShow($dbRowsToShow);
$dbRowsToHide = [ $dbRowsToHide = [
LAMCfgMain::DATABASE_FILE_SYSTEM => ['configDatabaseServer', 'configDatabasePort', 'configDatabaseName', 'configDatabaseUser', 'configDatabasePassword'], LAMCfgMain::DATABASE_FILE_SYSTEM => ['configDatabaseServer', 'configDatabasePort', 'configDatabaseName', 'configDatabaseUser', 'configDatabasePassword'],
LAMCfgMain::DATABASE_MYSQL => [] LAMCfgMain::DATABASE_MYSQL => []
]; ];
$storageProviderSelect->setTableRowsToHide($dbRowsToHide); $storageProviderSelect->setTableRowsToHide($dbRowsToHide);
$row->add($storageProviderSelect, 12); $row->add($storageProviderSelect);
$dbHost = new htmlResponsiveInputField(_('Database host'), 'configDatabaseServer', $cfg->configDatabaseServer, '273'); $dbHost = new htmlResponsiveInputField(_('Database host'), 'configDatabaseServer', $cfg->configDatabaseServer, '273');
$dbHost->setRequired(true); $dbHost->setRequired(true);
$row->add($dbHost, 12); $row->add($dbHost);
$dbPort = new htmlResponsiveInputField(_('Database port'), 'configDatabasePort', $cfg->configDatabasePort, '274'); $dbPort = new htmlResponsiveInputField(_('Database port'), 'configDatabasePort', $cfg->configDatabasePort, '274');
$row->add($dbPort, 12); $row->add($dbPort);
$dbName = new htmlResponsiveInputField(_('Database name'), 'configDatabaseName', $cfg->configDatabaseName, '276'); $dbName = new htmlResponsiveInputField(_('Database name'), 'configDatabaseName', $cfg->configDatabaseName, '276');
$dbName->setRequired(true); $dbName->setRequired(true);
$row->add($dbName, 12); $row->add($dbName);
$dbUser = new htmlResponsiveInputField(_('Database user'), 'configDatabaseUser', $cfg->configDatabaseUser, '275'); $dbUser = new htmlResponsiveInputField(_('Database user'), 'configDatabaseUser', $cfg->configDatabaseUser, '275');
$dbUser->setRequired(true); $dbUser->setRequired(true);
$row->add($dbUser, 12); $row->add($dbUser);
$dbPassword = new htmlResponsiveInputField(_('Database password'), 'configDatabasePassword', deobfuscateText($cfg->configDatabasePassword), '275'); $dbPassword = new htmlResponsiveInputField(_('Database password'), 'configDatabasePassword', deobfuscateText($cfg->configDatabasePassword), '275');
$dbPassword->setRequired(true); $dbPassword->setRequired(true);
$dbPassword->setIsPassword(true); $dbPassword->setIsPassword(true);
$row->add($dbPassword, 12); $row->add($dbPassword);
} }
// license // license
if (isLAMProVersion()) { if (isLAMProVersion()) {
$row->add(new htmlSubTitle(_('Licence')), 12); $row->add(new htmlSubTitle(_('Licence')));
$row->add(new htmlResponsiveInputTextarea('license', implode("\n", $cfg->getLicenseLines()), '30', '10', _('Licence'), '287'), 12); $row->add(new htmlResponsiveInputTextarea('license', implode("\n", $cfg->getLicenseLines()), '30', '10', _('Licence'), '287'));
$warningOptions = [ $warningOptions = [
_('Screen') => LAMCfgMain::LICENSE_WARNING_SCREEN, _('Screen') => LAMCfgMain::LICENSE_WARNING_SCREEN,
_('Email') => LAMCfgMain::LICENSE_WARNING_EMAIL, _('Email') => LAMCfgMain::LICENSE_WARNING_EMAIL,
_('Both') => LAMCfgMain::LICENSE_WARNING_ALL, _('Both') => LAMCfgMain::LICENSE_WARNING_ALL,
_('None') => LAMCfgMain::LICENSE_WARNING_NONE _('None') => LAMCfgMain::LICENSE_WARNING_NONE
]; ];
$warningTypeSelect = new htmlResponsiveSelect('licenseWarningType', $warningOptions, [$cfg->getLicenseWarningType()], _('Expiration warning'), '288'); $warningTypeSelect = new htmlResponsiveSelect('licenseWarningType', $warningOptions, [$cfg->getLicenseWarningType()], _('Expiration warning'), '288');
$warningTypeSelect->setHasDescriptiveElements(true); $warningTypeSelect->setHasDescriptiveElements(true);
$warningTypeSelect->setSortElements(false); $warningTypeSelect->setSortElements(false);
$warningTypeSelect->setTableRowsToHide([ $warningTypeSelect->setTableRowsToHide([
LAMCfgMain::LICENSE_WARNING_SCREEN => ['licenseEmailFrom', 'licenseEmailTo'], LAMCfgMain::LICENSE_WARNING_SCREEN => ['licenseEmailFrom', 'licenseEmailTo'],
LAMCfgMain::LICENSE_WARNING_NONE => ['licenseEmailFrom', 'licenseEmailTo'] LAMCfgMain::LICENSE_WARNING_NONE => ['licenseEmailFrom', 'licenseEmailTo']
]); ]);
$warningTypeSelect->setTableRowsToShow([ $warningTypeSelect->setTableRowsToShow([
LAMCfgMain::LICENSE_WARNING_EMAIL => ['licenseEmailFrom', 'licenseEmailTo'], LAMCfgMain::LICENSE_WARNING_EMAIL => ['licenseEmailFrom', 'licenseEmailTo'],
LAMCfgMain::LICENSE_WARNING_ALL => ['licenseEmailFrom', 'licenseEmailTo'] LAMCfgMain::LICENSE_WARNING_ALL => ['licenseEmailFrom', 'licenseEmailTo']
]); ]);
$row->add($warningTypeSelect, 12); $row->add($warningTypeSelect);
$licenseFrom = new htmlResponsiveInputField(_('From address'), 'licenseEmailFrom', $cfg->licenseEmailFrom, '289'); $licenseFrom = new htmlResponsiveInputField(_('From address'), 'licenseEmailFrom', $cfg->licenseEmailFrom, '289');
$licenseFrom->setRequired(true); $licenseFrom->setRequired(true);
$row->add($licenseFrom, 12); $row->add($licenseFrom);
$licenseTo = new htmlResponsiveInputField(_('To address'), 'licenseEmailTo', $cfg->licenseEmailTo, '290'); $licenseTo = new htmlResponsiveInputField(_('To address'), 'licenseEmailTo', $cfg->licenseEmailTo, '290');
$licenseTo->setRequired(true); $licenseTo->setRequired(true);
$row->add($licenseTo, 12); $row->add($licenseTo);
$row->add(new htmlSpacer(null, '1rem'), true); $row->add(new htmlSpacer(null, '1rem'), true);
} }
// security settings // security settings
$row->add(new htmlSubTitle(_("Security settings")), 12); $row->add(new htmlSubTitle(_("Security settings")));
$options = [5, 10, 20, 30, 60, 90, 120, 240]; $options = [5, 10, 20, 30, 60, 90, 120, 240];
$row->add(new htmlResponsiveSelect('sessionTimeout', $options, [$cfg->sessionTimeout], _("Session timeout"), '238')); $row->add(new htmlResponsiveSelect('sessionTimeout', $options, [$cfg->sessionTimeout], _("Session timeout"), '238'));
$hideLoginErrorDetails = ($cfg->hideLoginErrorDetails === 'true'); $hideLoginErrorDetails = ($cfg->hideLoginErrorDetails === 'true');
@ -545,80 +560,82 @@ if (isset($_POST['submitFormData'])) {
$serial = $sslCerts[$i]['serialNumber'] ?? ''; $serial = $sslCerts[$i]['serialNumber'] ?? '';
$validTo = $sslCerts[$i]['validTo_time_t'] ?? ''; $validTo = $sslCerts[$i]['validTo_time_t'] ?? '';
if (get_preg($validTo, 'digit')) { if (get_preg($validTo, 'digit')) {
$date = DateTime::createFromFormat('U', $validTo, new DateTimeZone('UTC')); $date = DateTime::createFromFormat('U', $validTo, new DateTimeZone('UTC'));
if ($date !== false) { if ($date !== false) {
$validTo = $date->format('Y-m-d'); $validTo = $date->format('Y-m-d');
} }
} }
$cn = $sslCerts[$i]['subject']['CN'] ?? ''; $cn = $sslCerts[$i]['subject']['CN'] ?? '';
$delBtn = new htmlButton('deleteCert_' . $i, 'del.svg', true); $delBtn = new htmlButton('deleteCert_' . $i, 'del.svg', true);
$certsData[] = [ $certsData[] = [
new htmlOutputText($cn), new htmlOutputText($cn),
new htmlDiv(null, new htmlOutputText($validTo), ['nowrap']), new htmlDiv(null, new htmlOutputText($validTo), ['nowrap']),
new htmlOutputText($serial), new htmlOutputText($serial),
$delBtn $delBtn
]; ];
} }
$certsTable = new htmlResponsiveTable($certsTitles, $certsData); $certsTable = new htmlResponsiveTable($certsTitles, $certsData);
$certsTable->setCSSClasses(['text-left']); $certsTable->setCSSClasses(['text-left']);
$row->add($certsTable, 12); $row->add($certsTable);
} }
// password policy // password policy
$row->add(new htmlSubTitle(_("Password policy")), 12); $row->add(new htmlSubTitle(_("Password policy")));
$optionsPwdLength = []; $optionsPwdLength = [];
for ($i = 0; $i <= 50; $i++) { for ($i = 0; $i <= 50; $i++) {
$optionsPwdLength[] = $i; $optionsPwdLength[] = $i;
} }
$options4 = [0, 1, 2, 3, 4]; $options4 = [0, 1, 2, 3, 4];
$row->add(new htmlResponsiveSelect('passwordMinLength', $optionsPwdLength, [$cfg->passwordMinLength], _('Minimum password length'), '242'), 12); $row->add(new htmlResponsiveSelect('passwordMinLength', $optionsPwdLength, [$cfg->passwordMinLength], _('Minimum password length'), '242'));
$row->addVerticalSpacer('1rem'); $row->addVerticalSpacer('1rem');
$row->add(new htmlResponsiveSelect('passwordMinLower', $optionsPwdLength, [$cfg->passwordMinLower], _('Minimum lowercase characters'), '242'), 12); $row->add(new htmlResponsiveSelect('passwordMinLower', $optionsPwdLength, [$cfg->passwordMinLower], _('Minimum lowercase characters'), '242'));
$row->add(new htmlResponsiveSelect('passwordMinUpper', $optionsPwdLength, [$cfg->passwordMinUpper], _('Minimum uppercase characters'), '242'), 12); $row->add(new htmlResponsiveSelect('passwordMinUpper', $optionsPwdLength, [$cfg->passwordMinUpper], _('Minimum uppercase characters'), '242'));
$row->add(new htmlResponsiveSelect('passwordMinNumeric', $optionsPwdLength, [$cfg->passwordMinNumeric], _('Minimum numeric characters'), '242'), 12); $row->add(new htmlResponsiveSelect('passwordMinNumeric', $optionsPwdLength, [$cfg->passwordMinNumeric], _('Minimum numeric characters'), '242'));
$row->add(new htmlResponsiveSelect('passwordMinSymbol', $optionsPwdLength, [$cfg->passwordMinSymbol], _('Minimum symbolic characters'), '242'), 12); $row->add(new htmlResponsiveSelect('passwordMinSymbol', $optionsPwdLength, [$cfg->passwordMinSymbol], _('Minimum symbolic characters'), '242'));
$row->add(new htmlResponsiveSelect('passwordMinClasses', $options4, [$cfg->passwordMinClasses], _('Minimum character classes'), '242'), 12); $row->add(new htmlResponsiveSelect('passwordMinClasses', $options4, [$cfg->passwordMinClasses], _('Minimum character classes'), '242'));
$row->addVerticalSpacer('1rem'); $row->addVerticalSpacer('1rem');
$rulesCountOptions = [_('all') => '-1', '3' => '3', '4' => '4']; $rulesCountOptions = [_('all') => '-1', '3' => '3', '4' => '4'];
$rulesCountSelect = new htmlResponsiveSelect('passwordRulesCount', $rulesCountOptions, [$cfg->checkedRulesCount], _('Number of rules that must match'), '246'); $rulesCountSelect = new htmlResponsiveSelect('passwordRulesCount', $rulesCountOptions, [$cfg->checkedRulesCount], _('Number of rules that must match'), '246');
$rulesCountSelect->setHasDescriptiveElements(true); $rulesCountSelect->setHasDescriptiveElements(true);
$row->add($rulesCountSelect, 12); $row->add($rulesCountSelect);
$passwordMustNotContainUser = ($cfg->passwordMustNotContainUser === 'true'); $passwordMustNotContainUser = ($cfg->passwordMustNotContainUser === 'true');
$row->add(new htmlResponsiveInputCheckbox('passwordMustNotContainUser', $passwordMustNotContainUser, _('Password must not contain user name'), '247'), 12); $row->add(new htmlResponsiveInputCheckbox('passwordMustNotContainUser', $passwordMustNotContainUser, _('Password must not contain user name'), '247'));
$passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true'); $passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true');
$row->add(new htmlResponsiveInputCheckbox('passwordMustNotContain3Chars', $passwordMustNotContain3Chars, _('Password must not contain part of user/first/last name'), '248'), 12); $row->add(new htmlResponsiveInputCheckbox('passwordMustNotContain3Chars', $passwordMustNotContain3Chars, _('Password must not contain part of user/first/last name'), '248'));
if (function_exists('curl_init')) { if (function_exists('curl_init')) {
$row->addVerticalSpacer('1rem'); $row->addVerticalSpacer('1rem');
$row->add(new htmlResponsiveInputField(_('External password check'), 'externalPwdCheckUrl', $cfg->externalPwdCheckUrl, '249'), 12); $row->add(new htmlResponsiveInputField(_('External password check'), 'externalPwdCheckUrl', $cfg->externalPwdCheckUrl, '249'));
} }
// logging // logging
$row->add(new htmlSubTitle(_("Logging")), 12); $row->add(new htmlSubTitle(_("Logging")));
$levelOptions = [ $levelOptions = [
_("Debug") => LOG_DEBUG, _("Debug") => LOG_DEBUG,
_("Notice") => LOG_NOTICE, _("Notice") => LOG_NOTICE,
_("Warning") => LOG_WARNING, _("Warning") => LOG_WARNING,
_("Error") => LOG_ERR _("Error") => LOG_ERR
]; ];
$levelSelect = new htmlResponsiveSelect('logLevel', $levelOptions, [$cfg->logLevel], _("Log level"), '239'); $levelSelect = new htmlResponsiveSelect('logLevel', $levelOptions, [$cfg->logLevel], _("Log level"), '239');
$levelSelect->setHasDescriptiveElements(true); $levelSelect->setHasDescriptiveElements(true);
$row->add($levelSelect, 12); $row->add($levelSelect);
$destinationOptions = [ $destinationOptions = [
_("No logging") => "none", _("No logging") => "none",
_("System logging") => "syslog", _("System logging") => "syslog",
_("File") => 'file', _("File") => 'file',
_("Remote") => 'remote' _("Remote") => 'remote'
]; ];
$destinationSelected = 'file'; $destinationSelected = 'file';
$destinationPath = $cfg->logDestination; $destinationPath = $cfg->logDestination;
$destinationRemote = ''; $destinationRemote = '';
if ($cfg->logDestination == 'NONE') { if ($cfg->logDestination == 'NONE') {
$destinationSelected = 'none'; $destinationSelected = 'none';
$destinationPath = ''; $destinationPath = '';
} elseif ($cfg->logDestination == 'SYSLOG') { }
elseif ($cfg->logDestination == 'SYSLOG') {
$destinationSelected = 'syslog'; $destinationSelected = 'syslog';
$destinationPath = ''; $destinationPath = '';
} elseif (str_starts_with($cfg->logDestination, 'REMOTE')) { }
elseif (str_starts_with($cfg->logDestination, 'REMOTE')) {
$destinationSelected = 'remote'; $destinationSelected = 'remote';
$remoteParts = explode(':', $cfg->logDestination, 2); $remoteParts = explode(':', $cfg->logDestination, 2);
$destinationRemote = empty($remoteParts[1]) ? '' : $remoteParts[1]; $destinationRemote = empty($remoteParts[1]) ? '' : $remoteParts[1];
@ -626,31 +643,31 @@ if (isset($_POST['submitFormData'])) {
} }
$logDestinationSelect = new htmlResponsiveSelect('logDestination', $destinationOptions, [$destinationSelected], _("Log destination"), '240'); $logDestinationSelect = new htmlResponsiveSelect('logDestination', $destinationOptions, [$destinationSelected], _("Log destination"), '240');
$logDestinationSelect->setTableRowsToHide([ $logDestinationSelect->setTableRowsToHide([
'none' => ['logFile', 'logRemote'], 'none' => ['logFile', 'logRemote'],
'syslog' => ['logFile', 'logRemote'], 'syslog' => ['logFile', 'logRemote'],
'remote' => ['logFile'], 'remote' => ['logFile'],
'file' => ['logRemote'] 'file' => ['logRemote']
]); ]);
$logDestinationSelect->setTableRowsToShow([ $logDestinationSelect->setTableRowsToShow([
'file' => ['logFile'], 'file' => ['logFile'],
'remote' => ['logRemote'] 'remote' => ['logRemote']
]); ]);
$logDestinationSelect->setHasDescriptiveElements(true); $logDestinationSelect->setHasDescriptiveElements(true);
$row->add($logDestinationSelect, 12); $row->add($logDestinationSelect);
$row->add(new htmlResponsiveInputField(_('File'), 'logFile', $destinationPath), 12); $row->add(new htmlResponsiveInputField(_('File'), 'logFile', $destinationPath));
$row->add(new htmlResponsiveInputField(_('Remote server'), 'logRemote', $destinationRemote, '251'), 12); $row->add(new htmlResponsiveInputField(_('Remote server'), 'logRemote', $destinationRemote, '251'));
$errorLogOptions = [ $errorLogOptions = [
_('PHP system setting') => LAMCfgMain::ERROR_REPORTING_SYSTEM, _('PHP system setting') => LAMCfgMain::ERROR_REPORTING_SYSTEM,
_('default') => LAMCfgMain::ERROR_REPORTING_DEFAULT, _('default') => LAMCfgMain::ERROR_REPORTING_DEFAULT,
_('all') => LAMCfgMain::ERROR_REPORTING_ALL _('all') => LAMCfgMain::ERROR_REPORTING_ALL
]; ];
$errorLogSelect = new htmlResponsiveSelect('errorReporting', $errorLogOptions, [$cfg->errorReporting], _('PHP error reporting'), '244'); $errorLogSelect = new htmlResponsiveSelect('errorReporting', $errorLogOptions, [$cfg->errorReporting], _('PHP error reporting'), '244');
$errorLogSelect->setHasDescriptiveElements(true); $errorLogSelect->setHasDescriptiveElements(true);
$row->add($errorLogSelect); $row->add($errorLogSelect);
// mail options // mail options
if (isLAMProVersion()) { if (isLAMProVersion()) {
$row->add(new htmlSubTitle(_('Mail options')), 12); $row->add(new htmlSubTitle(_('Mail options')));
$mailServer = new htmlResponsiveInputField(_("Mail server"), 'mailServer', $cfg->mailServer, '253'); $mailServer = new htmlResponsiveInputField(_("Mail server"), 'mailServer', $cfg->mailServer, '253');
$row->add($mailServer); $row->add($mailServer);
$mailUser = new htmlResponsiveInputField(_("User name"), 'mailUser', $cfg->mailUser, '254'); $mailUser = new htmlResponsiveInputField(_("User name"), 'mailUser', $cfg->mailUser, '254');
@ -659,10 +676,10 @@ if (isset($_POST['submitFormData'])) {
$mailPassword->setIsPassword(true); $mailPassword->setIsPassword(true);
$row->add($mailPassword); $row->add($mailPassword);
$mailEncryptionOptions = [ $mailEncryptionOptions = [
'TLS' => LAMCfgMain::SMTP_TLS, 'TLS' => LAMCfgMain::SMTP_TLS,
'SSL' => LAMCfgMain::SMTP_SSL, 'SSL' => LAMCfgMain::SMTP_SSL,
_('None') => LAMCfgMain::SMTP_NONE _('None') => LAMCfgMain::SMTP_NONE
]; ];
$selectedMailEncryption = empty($cfg->mailEncryption) ? LAMCfgMain::SMTP_TLS : $cfg->mailEncryption; $selectedMailEncryption = empty($cfg->mailEncryption) ? LAMCfgMain::SMTP_TLS : $cfg->mailEncryption;
$mailEncryptionSelect = new htmlResponsiveSelect('mailEncryption', $mailEncryptionOptions, [$selectedMailEncryption], _('Encryption protocol'), '256'); $mailEncryptionSelect = new htmlResponsiveSelect('mailEncryption', $mailEncryptionOptions, [$selectedMailEncryption], _('Encryption protocol'), '256');
$mailEncryptionSelect->setHasDescriptiveElements(true); $mailEncryptionSelect->setHasDescriptiveElements(true);
@ -675,20 +692,20 @@ if (isset($_POST['submitFormData'])) {
. "', '" . getSecurityTokenValue() . "', '" . _('Ok') . "', '" . _('Cancel') . "', '" . _('Test settings') . "')"); . "', '" . getSecurityTokenValue() . "', '" . _('Ok') . "', '" . _('Cancel') . "', '" . _('Test settings') . "')");
$row->addLabel(new htmlOutputText("&nbsp;", false)); $row->addLabel(new htmlOutputText("&nbsp;", false));
$row->addField($mailTestButton); $row->addField($mailTestButton);
$testDialogDivContent = new htmlResponsiveRow(); $testDialogDivContent = new htmlResponsiveRow();
$fromAddressInput = new htmlResponsiveInputField(_('From address'), 'testSmtpFrom', null, null, true); $fromAddressInput = new htmlResponsiveInputField(_('From address'), 'testSmtpFrom', null, null, true);
$fromAddressInput->setType('email'); $fromAddressInput->setType('email');
$testDialogDivContent->add($fromAddressInput); $testDialogDivContent->add($fromAddressInput);
$toAddressInput = new htmlResponsiveInputField(_('To address'), 'testSmtpTo', null, null, true); $toAddressInput = new htmlResponsiveInputField(_('To address'), 'testSmtpTo', null, null, true);
$toAddressInput->setType('email'); $toAddressInput->setType('email');
$testDialogDivContent->add($toAddressInput); $testDialogDivContent->add($toAddressInput);
$testDialogDiv = new htmlDiv('smtpTestDialogDiv', $testDialogDivContent, ['hidden']); $testDialogDiv = new htmlDiv('smtpTestDialogDiv', $testDialogDivContent, ['hidden']);
$row->add($testDialogDiv); $row->add($testDialogDiv);
} }
// webauthn management // webauthn management
if (extension_loaded('PDO') if (extension_loaded('PDO')
&& in_array('sqlite', \PDO::getAvailableDrivers())) { && in_array('sqlite', PDO::getAvailableDrivers())) {
include_once __DIR__ . '/../../lib/webauthn.inc'; include_once __DIR__ . '/../../lib/webauthn.inc';
$webAuthnManager = new WebauthnManager(); $webAuthnManager = new WebauthnManager();
try { try {
@ -696,7 +713,7 @@ if (isset($_POST['submitFormData'])) {
if ($database->hasRegisteredCredentials()) { if ($database->hasRegisteredCredentials()) {
$row->add(new htmlSubTitle(_('WebAuthn devices'))); $row->add(new htmlSubTitle(_('WebAuthn devices')));
$webauthnSearchField = new htmlResponsiveInputField(_('User DN'), 'webauthn_searchTerm', null, '252'); $webauthnSearchField = new htmlResponsiveInputField(_('User DN'), 'webauthn_searchTerm', null, '252');
$row->add($webauthnSearchField, 12); $row->add($webauthnSearchField);
$row->addVerticalSpacer('0.5rem'); $row->addVerticalSpacer('0.5rem');
$row->add(new htmlButton('webauthn_search', _('Search')), 12, 12, 12, 'text-center'); $row->add(new htmlButton('webauthn_search', _('Search')), 12, 12, 12, 'text-center');
$resultDiv = new htmlDiv('webauthn_results', new htmlOutputText(''), ['lam-webauthn-results', 'text-left']); $resultDiv = new htmlDiv('webauthn_results', new htmlOutputText(''), ['lam-webauthn-results', 'text-left']);
@ -708,48 +725,48 @@ if (isset($_POST['submitFormData'])) {
} }
} }
catch (LAMException $e) { catch (LAMException $e) {
logNewMessage(LOG_ERR, 'Webauthn error: ' . $e->getTitle() . ' ' . $e->getMessage()); logNewMessage(LOG_ERR, 'Webauthn error: ' . $e->getTitle() . ' ' . $e->getMessage());
$row->add(new htmlStatusMessage('ERROR', $e->getTitle())); $row->add(new htmlStatusMessage('ERROR', $e->getTitle()));
} }
} }
// module settings // module settings
$modules = getAllModules(); $modules = getAllModules();
$supportsGlobalCronJob = false; $supportsGlobalCronJob = false;
foreach ($modules as $module) { foreach ($modules as $module) {
$supportsGlobalCronJob = $supportsGlobalCronJob || $module->supportsGlobalCronJob(); $supportsGlobalCronJob = $supportsGlobalCronJob || $module->supportsGlobalCronJob();
$moduleOptions = $module->getGlobalConfigOptions($cfg->getModuleSettings()); $moduleOptions = $module->getGlobalConfigOptions($cfg->getModuleSettings());
if (empty($moduleOptions)) { if (empty($moduleOptions)) {
continue; continue;
} }
$row->add(new htmlSubTitle($module->get_alias())); $row->add(new htmlSubTitle($module->get_alias()));
foreach ($moduleOptions as $moduleOption) { foreach ($moduleOptions as $moduleOption) {
$row->add($moduleOption); $row->add($moduleOption);
} }
$row->addVerticalSpacer('3rem'); $row->addVerticalSpacer('3rem');
} }
// global cron job // global cron job
if ($supportsGlobalCronJob) { if ($supportsGlobalCronJob) {
$row->add(new htmlSubTitle(_('Global cron job'))); $row->add(new htmlSubTitle(_('Global cron job')));
$cronCommand = dirname(__FILE__, 3) . '/lib/runCronJobs.sh all'; $cronCommand = dirname(__FILE__, 3) . '/lib/runCronJobs.sh all';
$row->addLabel(new htmlOutputText('Cron command')); $row->addLabel(new htmlOutputText('Cron command'));
$cmdGroup = new htmlGroup(); $cmdGroup = new htmlGroup();
$cmdGroup->addElement(new htmlOutputText('0 0 * * * ' . $cronCommand)); $cmdGroup->addElement(new htmlOutputText('0 0 * * * ' . $cronCommand));
$cmdGroup->addElement(new htmlSpacer('2rem', null)); $cmdGroup->addElement(new htmlSpacer('2rem', null));
$cmdGroup->addElement(new htmlHelpLink('294')); $cmdGroup->addElement(new htmlHelpLink('294'));
$row->addField($cmdGroup); $row->addField($cmdGroup);
} }
// change master password // change master password
$row->add(new htmlSubTitle(_("Change master password"))); $row->add(new htmlSubTitle(_("Change master password")));
$pwd1 = new htmlResponsiveInputField(_("New master password"), 'masterpassword', '', '235'); $pwd1 = new htmlResponsiveInputField(_("New master password"), 'masterpassword', '', '235');
$pwd1->setIsPassword(true, false, true); $pwd1->setIsPassword(true, false, true);
$row->add($pwd1, 12); $row->add($pwd1);
$pwd2 = new htmlResponsiveInputField(_("Reenter password"), 'masterpassword2', ''); $pwd2 = new htmlResponsiveInputField(_("Reenter password"), 'masterpassword2', '');
$pwd2->setIsPassword(true, false, true); $pwd2->setIsPassword(true, false, true);
$pwd2->setSameValueFieldID('masterpassword'); $pwd2->setSameValueFieldID('masterpassword');
$row->add($pwd2, 12); $row->add($pwd2);
$row->addVerticalSpacer('3rem'); $row->addVerticalSpacer('3rem');
// buttons // buttons
@ -760,8 +777,8 @@ if (isset($_POST['submitFormData'])) {
$buttonTable->addElement($saveButton); $buttonTable->addElement($saveButton);
$buttonTable->addElement(new htmlSpacer('0.5rem', null)); $buttonTable->addElement(new htmlSpacer('0.5rem', null));
$buttonTable->addElement(new htmlButton('cancel', _("Cancel"))); $buttonTable->addElement(new htmlButton('cancel', _("Cancel")));
$row->add($buttonTable, 12); $row->add($buttonTable);
$row->add(new htmlHiddenInput('submitFormData', '1'), 12); $row->add(new htmlHiddenInput('submitFormData', '1'));
} }
$box = new htmlDiv(null, $row); $box = new htmlDiv(null, $row);

View file

@ -19,7 +19,7 @@ use ServerProfilePersistenceManager;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2024 Roland Gruber Copyright (C) 2003 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -155,9 +155,14 @@ if (isset($_POST['action'])) {
if (preg_match("/^[a-z0-9_-]+$/i", (string) $_POST['defaultfilename'])) { if (preg_match("/^[a-z0-9_-]+$/i", (string) $_POST['defaultfilename'])) {
$configMain = new LAMCfgMain(); $configMain = new LAMCfgMain();
$configMain->default = $_POST['defaultfilename']; $configMain->default = $_POST['defaultfilename'];
$configMain->save(); try {
$configMain = null; $configMain->save();
$msg = _("New default profile set successfully."); $configMain = null;
$msg = _("New default profile set successfully.");
}
catch (LAMException $e) {
$error = $e->getTitle();
}
} }
else { else {
$error = _("Profile name is invalid!"); $error = _("Profile name is invalid!");

View file

@ -26,7 +26,7 @@ use ServerProfilePersistenceManager;
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2006 Michael Duergner Copyright (C) 2003 - 2006 Michael Duergner
2005 - 2024 Roland Gruber 2005 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -60,10 +60,19 @@ include __DIR__ . '/../lib/checkEnvironment.inc';
/** security functions */ /** security functions */
include_once(__DIR__ . "/../lib/security.inc"); include_once(__DIR__ . "/../lib/security.inc");
/** self service functions */ /** self-service functions */
include_once(__DIR__ . "/../lib/selfService.inc"); include_once(__DIR__ . "/../lib/selfService.inc");
/** access to configuration options */ /** access to configuration options */
include_once(__DIR__ . "/../lib/config.inc"); include_once(__DIR__ . "/../lib/config.inc");
$cfgMain = new LAMCfgMain();
// check if main config password is set
if (!$cfgMain->hasPasswordSet()) {
metaRefresh('setInitialPassword.php');
die();
}
$licenseValidator = null; $licenseValidator = null;
if (isLAMProVersion()) { if (isLAMProVersion()) {
include_once(__DIR__ . "/../lib/env.inc"); include_once(__DIR__ . "/../lib/env.inc");
@ -110,12 +119,10 @@ if (isset($_POST['language'])) {
setcookie('lam_last_language', htmlspecialchars((string) $_POST['language']), $cookieOptions); setcookie('lam_last_language', htmlspecialchars((string) $_POST['language']), $cookieOptions);
} }
// init some session variables $_SESSION["cfgMain"] = $cfgMain;
$default_Config = new LAMCfgMain();
$_SESSION["cfgMain"] = $default_Config;
setSSLCaCert(); setSSLCaCert();
$default_Profile = $default_Config->default; $default_Profile = $cfgMain->default;
if (isset($_COOKIE["lam_default_profile"]) && in_array($_COOKIE["lam_default_profile"], $profiles)) { if (isset($_COOKIE["lam_default_profile"]) && in_array($_COOKIE["lam_default_profile"], $profiles)) {
$default_Profile = $_COOKIE["lam_default_profile"]; $default_Profile = $_COOKIE["lam_default_profile"];
} }
@ -142,7 +149,7 @@ catch (LAMException $e) {
$error_message = $e->getTitle(); $error_message = $e->getTitle();
} }
if (!isset($default_Config->default) || !in_array($default_Config->default, $profiles)) { if (!isset($cfgMain->default) || !in_array($cfgMain->default, $profiles)) {
$error_message = _('No default profile set. Please set it in the server profile configuration.'); $error_message = _('No default profile set. Please set it in the server profile configuration.');
} }
@ -549,7 +556,7 @@ if (isset($_POST['checklogin'])) {
cleanLDAPResult($searchInfo); cleanLDAPResult($searchInfo);
if (empty($searchInfo)) { if (empty($searchInfo)) {
$searchSuccess = false; $searchSuccess = false;
if ($default_Config->isHideLoginErrorDetails()) { if ($cfgMain->isHideLoginErrorDetails()) {
$searchError = _('Wrong password/user name combination. Please try again.'); $searchError = _('Wrong password/user name combination. Please try again.');
} }
else { else {
@ -560,7 +567,7 @@ if (isset($_POST['checklogin'])) {
} }
elseif (count($searchInfo) > 1) { elseif (count($searchInfo) > 1) {
$searchSuccess = false; $searchSuccess = false;
if ($default_Config->isHideLoginErrorDetails()) { if ($cfgMain->isHideLoginErrorDetails()) {
$searchError = _('Wrong password/user name combination. Please try again.'); $searchError = _('Wrong password/user name combination. Please try again.');
} }
else { else {
@ -575,7 +582,7 @@ if (isset($_POST['checklogin'])) {
} }
else { else {
$searchSuccess = false; $searchSuccess = false;
if ($default_Config->isHideLoginErrorDetails()) { if ($cfgMain->isHideLoginErrorDetails()) {
$searchError = _('Wrong password/user name combination. Please try again.'); $searchError = _('Wrong password/user name combination. Please try again.');
} }
else { else {
@ -590,7 +597,7 @@ if (isset($_POST['checklogin'])) {
} }
else { else {
$searchSuccess = false; $searchSuccess = false;
if ($default_Config->isHideLoginErrorDetails()) { if ($cfgMain->isHideLoginErrorDetails()) {
$searchError = _('Wrong password/user name combination. Please try again.'); $searchError = _('Wrong password/user name combination. Please try again.');
} }
else { else {
@ -641,13 +648,13 @@ if (isset($_POST['checklogin'])) {
header("HTTP/1.1 403 Forbidden"); header("HTTP/1.1 403 Forbidden");
$extraMessage = null; $extraMessage = null;
if (($searchLDAP !== null) && ($e->getLdapErrorCode() == 49)) { if (($searchLDAP !== null) && ($e->getLdapErrorCode() == 49)) {
if (!$default_Config->isHideLoginErrorDetails()) { if (!$cfgMain->isHideLoginErrorDetails()) {
$extraMessage = getExtraInvalidCredentialsMessage($searchLDAP->server(), $username); $extraMessage = getExtraInvalidCredentialsMessage($searchLDAP->server(), $username);
} }
$searchLDAP->close(); $searchLDAP->close();
} }
$message = $e->getMessage(); $message = $e->getMessage();
if ($default_Config->isHideLoginErrorDetails()) { if ($cfgMain->isHideLoginErrorDetails()) {
$message = null; $message = null;
} }
display_LoginPage($licenseValidator, $e->getTitle(), $message, $extraMessage); display_LoginPage($licenseValidator, $e->getTitle(), $message, $extraMessage);

View file

@ -0,0 +1,162 @@
<?php
namespace LAM\INIT;
use htmlButton;
use htmlJavaScript;
use htmlOutputText;
use htmlResponsiveInputField;
use htmlResponsiveRow;
use htmlStatusMessage;
use htmlTitle;
use LAMCfgMain;
use LAMException;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2025 Roland Gruber
This program 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.
This program 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Password dialog for initial configuration.
*
* @author Roland Gruber
* @package main
*/
/** security functions */
include_once(__DIR__ . "/../lib/security.inc");
/** access to configuration settings */
include_once(__DIR__ . "/../lib/config.inc");
/** status messages */
include_once(__DIR__ . "/../lib/status.inc");
// set session save path
if (isFileBasedSession()) {
session_save_path(__DIR__ . '/../sess');
}
lam_start_session();
$cfgMain = new LAMCfgMain();
if ($cfgMain->hasPasswordSet()) {
logNewMessage(LOG_ERR, 'Invalid attempt to set initial config password');
die();
}
setlanguage();
if (!empty($_POST)) {
validateSecurityToken();
}
$message = null;
// check if user already pressed button
if (isset($_POST['changePassword'])) {
// check new password
$password1 = $_POST['password1'];
$password2 = $_POST['password2'];
if ($password1 == '') {
$message = new htmlStatusMessage('ERROR', _('No password was entered!'));
printContent($message);
exit();
}
// check if passwords match
if ($password1 != $password2) {
$message = new htmlStatusMessage('ERROR', _('Passwords are different!'));
printContent($message);
exit();
}
// check password strength
$pwdPolicyResult = isValidConfigurationPassword($password1);
if (!$pwdPolicyResult) {
$message = new htmlStatusMessage('ERROR', _('Please enter at least 8 characters including letters, a number and a symbol.'));
printContent($message);
exit();
}
// set new password
$cfgMain->setPassword($password1);
try {
$cfgMain->save();
metaRefresh('config/mainlogin.php');
exit();
}
catch (LAMException $e) {
$message = new htmlStatusMessage('ERROR', $e->getTitle(), $e->getMessage());
}
}
printContent($message);
/**
* Displays the content area
*
* @param htmlStatusMessage|null $message status message
* @param bool $showPasswordInputs show password input fields
*/
function printContent(htmlStatusMessage $message = null, bool $showPasswordInputs = true): void {
echo '
<!DOCTYPE html>
<html><head>
<meta name="robots" content="noindex, nofollow">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
';
printHeaderContents('LDAP Account Manager', '..');
echo '</head>
<body>';
printJsIncludes('..');
echo '<div class="smallPaddingContent">';
echo "<form action=\"setInitialPassword.php\" method=\"post\">\n";
addSecurityTokenToSession();
$container = new htmlResponsiveRow();
if ($message !== null) {
$container->addVerticalSpacer('1rem');
$container->add($message);
}
$container->addVerticalSpacer('2rem');
if ($showPasswordInputs) {
$container->add(new htmlTitle(_('Initial configuration')));
$container->addVerticalSpacer('3rem');
$container->add(new htmlOutputText(_("Please enter your new LAM main configuration password.")), 12, 12, 12, 'text-center');
$container->addVerticalSpacer('2rem');
$pwdInput1 = new htmlResponsiveInputField(_('New password'), 'password1', '');
$pwdInput1->setIsPassword(true, true, true);
$container->add($pwdInput1);
$pwdInput2 = new htmlResponsiveInputField(_('Repeat password'), 'password2', '');
$pwdInput2->setIsPassword(true);
$pwdInput2->setSameValueFieldID('password1');
$container->add($pwdInput2);
$container->addVerticalSpacer('1rem');
$container->add(new htmlButton('changePassword', _("Submit")), 12, 12, 12, 'text-center');
addSecurityTokenToMetaHTML($container);
$container->add(new htmlJavaScript('checkFieldsHaveSameValues("password1", "password2");'));
}
parseHtml(null, $container, [], false, 'user');
echo '</form><br>
</div>
<br><br>
</body>
</html>';
}

View file

@ -3,7 +3,7 @@ use PHPUnit\Framework\TestCase;
/* /*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2020 - 2023 Roland Gruber Copyright (C) 2020 - 2025 Roland Gruber
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -56,6 +56,7 @@ class LAMCfgMainTest extends TestCase {
/** /**
* Mail related settings * Mail related settings
* @throws LAMException error saving config
*/ */
public function testMail() { public function testMail() {
$this->assertEquals(LAMCfgMain::MAIL_ATTRIBUTE_DEFAULT, $this->conf->getMailAttribute()); $this->assertEquals(LAMCfgMain::MAIL_ATTRIBUTE_DEFAULT, $this->conf->getMailAttribute());
@ -81,6 +82,7 @@ class LAMCfgMainTest extends TestCase {
/** /**
* License related settings. * License related settings.
* @throws LAMException error saving config
*/ */
public function testLicense() { public function testLicense() {
$timestamp = '12345'; $timestamp = '12345';