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>
@ -158,13 +159,17 @@ if (isset($_POST['submitFormData'])) {
} }
} }
// 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
@ -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,15 +254,18 @@ 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 { }
else {
$isValidLogFile = isset($_POST['logFile']) && LAMCfgMain::isValidLogFilename($_POST['logFile']); $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'])) {
@ -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".');
} }
} }
@ -387,14 +399,17 @@ if (isset($_POST['submitFormData'])) {
} }
$cfg->setModuleSettings($moduleSettings); $cfg->setModuleSettings($moduleSettings);
// save settings // save settings
if (isset($_POST['submit'])) { if (isset($_POST['submit']) && empty($errors)) {
try {
$cfg->save(); $cfg->save();
if (empty($errors)) {
$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,12 +430,12 @@ 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
]; ];
@ -439,28 +454,28 @@ if (isset($_POST['submitFormData'])) {
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,
@ -478,19 +493,19 @@ if (isset($_POST['submitFormData'])) {
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');
@ -561,39 +576,39 @@ if (isset($_POST['submitFormData'])) {
} }
$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,
@ -602,7 +617,7 @@ if (isset($_POST['submitFormData'])) {
]; ];
$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",
@ -615,10 +630,12 @@ if (isset($_POST['submitFormData'])) {
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];
@ -636,9 +653,9 @@ if (isset($_POST['submitFormData'])) {
'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,
@ -650,7 +667,7 @@ if (isset($_POST['submitFormData'])) {
// 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');
@ -688,7 +705,7 @@ if (isset($_POST['submitFormData'])) {
// 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']);
@ -745,11 +762,11 @@ if (isset($_POST['submitFormData'])) {
$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,10 +155,15 @@ 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'];
try {
$configMain->save(); $configMain->save();
$configMain = null; $configMain = null;
$msg = _("New default profile set successfully."); $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';