* Setup/Authentication: added an authentication log and fallback authentication for all backends

This commit is contained in:
ralf 2023-07-06 15:50:53 +02:00
parent 47f9f80e61
commit 1a0660a6cc
7 changed files with 108 additions and 35 deletions

View File

@ -65,13 +65,13 @@ class Auth
*
* @return string
*/
public function backendType()
public static function backendType()
{
return Cache::getSession(__CLASS__, 'backend');
}
/**
* Instanciate a backend
* Instantiate a backend
*
* Type will be stored in session, to automatic use the same type eg. for conditional use of SAML.
*
@ -83,7 +83,7 @@ class Auth
{
if (is_null($type))
{
$type = Cache::getSession(__CLASS__, 'backend') ?: null;
$type = self::backendType() ?: null;
}
// do we have a hostname specific auth type set
if (is_null($type) && !empty($GLOBALS['egw_info']['server']['auth_type_host']) &&
@ -93,24 +93,53 @@ class Auth
}
if (is_null($type)) $type = $GLOBALS['egw_info']['server']['auth_type'];
$account_storage = $GLOBALS['egw_info']['server']['account_storage'] ?? $type;
if (!empty($GLOBALS['egw_info']['server']['auth_fallback']) && $type !== $account_storage)
{
$backend = new Auth\Fallback($type, $account_storage);
self::log("Instantiated Auth\\Fallback('$type', '$account_storage')");
}
else
{
$backend_class = __CLASS__.'\\'.ucfirst($type);
// try old location / name, if not found
if (!class_exists($backend_class))
if (!class_exists($backend_class) && class_exists('auth_'.$type))
{
$backend_class = 'auth_'.$type;
}
$backend = new $backend_class;
self::log("Instantiated $backend_class() (for type '$type')");
}
if (!($backend instanceof Auth\Backend))
{
throw new Exception\AssertionFailed("Auth backend class $backend_class is NO EGroupware\\Api\Auth\\Backend!");
}
if ($save_in_session) Cache::setSession(__CLASS__, 'backend', $type);
if ($save_in_session)
{
Cache::setSession(__CLASS__, 'backend', $type);
}
return $backend;
}
/**
* Log $message to auth.log, if enabled
*
* @param string $message
* @return void
*/
public static function log(string $message)
{
if (!empty($GLOBALS['egw_info']['server']['auth_log']) &&
($fp = fopen($GLOBALS['egw_info']['server']['files_dir'].'/auth.log', 'a')))
{
fwrite($fp, date('Y-m-d H:i:s: ').$message."\n");
fclose($fp);
}
}
/**
* Attempt a SSO login
*
@ -318,11 +347,24 @@ class Auth
*/
function authenticate($username, $passwd, $passwd_type='text')
{
return Cache::getCache($GLOBALS['egw_info']['server']['install_id'],
__CLASS__, sha1($username.':'.$passwd.':'.$passwd_type), function($username, $passwd, $passwd_type)
if (preg_match(Auth\Token::TOKEN_REGEXP, $passwd, $matches))
{
return $this->backend->authenticate($username, $passwd, $passwd_type);
$log_passwd = substr($passwd, 0, strlen(Auth\Token::PREFIX)+1+strlen($matches[1]));
$log_passwd .= str_repeat('*', strlen($passwd)-strlen($log_passwd));
}
else
{
$log_passwd = str_repeat('*', strlen($passwd));
}
$ret = Cache::getCache($GLOBALS['egw_info']['server']['install_id'],
__CLASS__, sha1($username.':'.$passwd.':'.$passwd_type), function($username, $passwd, $passwd_type) use ($log_passwd)
{
$ret = $this->backend->authenticate($username, $passwd, $passwd_type);
self::log(get_class($this->backend)."('$username', '$log_passwd', '$passwd_type') returned ".json_encode($ret));
return $ret;
}, [$username, $passwd, $passwd_type], self::AUTH_CACHE_TIME);
self::log(__METHOD__."('$username', '$log_passwd', '$passwd_type') returned ".json_encode($ret));
return $ret;
}
/**
@ -339,6 +381,7 @@ class Auth
{
if (($err = self::crackcheck($new_passwd,null,null,null,$account_id)))
{
self::log(__METHOD__."(..., $account_id) new password rejected by crackcheck: $err");
throw new Exception\WrongUserinput($err);
}
if (($ret = $this->backend->change_password($old_passwd, $new_passwd, $account_id)))
@ -360,6 +403,7 @@ class Auth
Cache::unsetCache($GLOBALS['egw_info']['server']['install_id'],
__CLASS__, sha1(Accounts::id2name($account_id).':'.$old_passwd.':text'));
}
self::log(__METHOD__."(..., $account_id) returned ".json_encode($ret));
return $ret;
}

View File

@ -66,18 +66,21 @@ class Fallback implements Backend
{
$backup_currentapp = $GLOBALS['egw_info']['flags']['currentapp'];
$GLOBALS['egw_info']['flags']['currentapp'] = 'admin'; // otherwise
$this->fallback_backend->change_password('', $passwd, $account_id);
$ret = $this->fallback_backend->change_password('', $passwd, $account_id);
Api\Auth::log(__METHOD__."('$username', ...) fallback_backend(".get_class($this->fallback_backend).
")->change_password('', '".str_repeat('*', strlen($passwd))."', $account_id) returned ".json_encode($ret));
$GLOBALS['egw_info']['flags']['currentapp'] = $backup_currentapp;
//error_log(__METHOD__."('$username', \$passwd) updated password for #$account_id on fallback ".($ret ? 'successfull' : 'failed!'));
}
return true;
}
if ($this->fallback_backend->authenticate($username,$passwd, $passwd_type))
if (($ret = $this->fallback_backend->authenticate($username,$passwd, $passwd_type)))
{
Api\Cache::setInstance(__CLASS__,'backend_used-'.$username,'fallback');
return true;
}
return false;
Api\Auth::log(__METHOD__."('$username', ...) fallback_backend(".get_class($this->fallback_backend).
")->authenticate('$username', '".str_repeat('*', strlen($passwd))."', ...) returned ".json_encode($ret));
return $ret;
}
/**
@ -107,12 +110,16 @@ class Fallback implements Backend
if (($ret = $this->primary_backend->change_password($old_passwd, $new_passwd, $account_id)))
{
// if password successfully changed on primary, also update fallback
$this->fallback_backend->change_password($old_passwd, $new_passwd, $account_id);
$change_pwd = $this->fallback_backend->change_password($old_passwd, $new_passwd, $account_id);
Api\Auth::log(__METHOD__."(..., $account_id) fallback_backend(".get_class($this->fallback_backend).
")->change_password('', '".str_repeat('*', strlen($new_passwd))."', $account_id) returned ".json_encode($change_pwd));
}
}
else
{
$ret = $this->fallback_backend->change_password($old_passwd, $new_passwd, $account_id);
Api\Auth::log(__METHOD__."(..., $account_id) fallback_backend(".get_class($this->fallback_backend).
")->change_password('', '".str_repeat('*', strlen($new_passwd))."', $account_id) returned ".json_encode($ret));
}
//error_log(__METHOD__."('$old_passwd', '$new_passwd', $account_id) username='$username', backend=".Api\Cache::getInstance(__CLASS__,'backend_used-'.$username)." returning ".array2string($ret));
return $ret;

View File

@ -51,6 +51,8 @@ class Token extends APi\Storage\Base
return null; // not a token
}
try {
$log_passwd = substr($token, 0, strlen(Auth\Token::PREFIX)+1+strlen($matches[1]));
$log_passwd .= str_repeat('*', strlen($token)-strlen($log_passwd));
$data = self::getInstance()->read([
'token_id' => $matches[1],
'account_id' => [0, Api\Accounts::getInstance()->name2id($user)],
@ -59,12 +61,15 @@ class Token extends APi\Storage\Base
]);
if (!password_verify($matches[2], $data['token_hash']))
{
Api\Auth::log(__METHOD__."('$user', '$log_passwd', ...') returned false (no active token found)");
return false; // invalid token password
}
$limits = $data['token_limits'];
Api\Auth::log(__METHOD__."('$user', '$log_passwd', ...) returned true");
return true;
}
catch (Api\Exception\NotFound $e) {
Api\Auth::log(__METHOD__."('$user', '$log_passwd, ...) returned false: ".$e->getMessage());
return false; // token not found
}
}

View File

@ -104,28 +104,29 @@ if(@$_POST['submit'] && @$newsettings)
$GLOBALS['egw_setup']->html->show_header(lang('Configuration'),False,'config',$GLOBALS['egw_setup']->ConfigDomain . '(' . $GLOBALS['egw_domain'][$GLOBALS['egw_setup']->ConfigDomain]['db_type'] . ')');
$current_config = [];
// if we have an validation error, use the new settings made by the user and not the stored config
if($GLOBALS['error'] && is_array($newsettings))
{
$GLOBALS['current_config'] = $newsettings;
$current_config = $newsettings;
}
else
{
foreach($GLOBALS['egw_setup']->db->select($GLOBALS['egw_setup']->config_table,'*',false,__LINE__,__FILE__) as $row)
{
$GLOBALS['current_config'][$row['config_name']] = $row['config_value'];
$current_config[$row['config_name']] = $row['config_value'];
}
}
$setup_tpl->pparse('out','T_config_pre_script');
/* Now parse each of the templates we want to show here */
class phpgw
class egw
{
var $accounts;
var $applications;
var $db;
}
$GLOBALS['egw'] = new phpgw;
$GLOBALS['egw'] = new egw;
$GLOBALS['egw']->db =& $GLOBALS['egw_setup']->db;
$t = new Framework\Template(Framework\Template::get_dir('setup'));
@ -157,17 +158,18 @@ foreach($vars as $value)
}
else
{
$t->set_var($value,@$current_config[$newval]);
$t->set_var($value, $current_config[$newval]);
}
break;
case 'selected':
case 'checked':
$newvals = explode(' ',$newval);
$setting = array_pop($newvals);
$config = implode('_',$newvals);
/* echo $config . '=' . $current_config[$config]; */
if(@$current_config[$config] == $setting)
if($current_config[$config] == $setting)
{
$t->set_var($value,' selected');
$t->set_var($value,' '.$type);
}
else
{

View File

@ -15,8 +15,8 @@
%1 not allowed to create in univention. setup de Es ist nicht erlaubt unter Univention %1 anzulegen.
%1 password set in %2. setup de %1 Passwort gesetzt in %2.
%1 passwords updated, %3 errors setup de %1 Passwörter aktualisiert, %3 Fehler
%1 users and %2 groups created, %3 errors setup de %1 Benutzer und %2 Gruppen angelegt, %3 Fehler
%1 the configuration file. setup de %1 der Konfigurationsdatei.
%1 users and %2 groups created, %3 errors setup de %1 Benutzer und %2 Gruppen angelegt, %3 Fehler
'%1' is no valid domain name! setup de '%1' ist kein gültiger Domainname!
'%1' is not allowed as %2. arguments of option %3 !!! setup de '%1' ist nicht erlaubt als %2. Parameter für die Option %3 !!!
'%1' must be integer setup de %1 muß ein Integer-Wert sein.
@ -291,6 +291,7 @@ email (standard maildomain should be set) setup de email (Standard Maildomaine m
email-address setup de EMail Adresse
emailadmin profile updated: setup de EMailAdmin Profil aktualisiert:
enable for extra debug-messages setup de ankreuzen für zusätzliche Diagnosemeldungen
enable logging of authentication to files-directory setup de Aktiviere Logging der Authentifizierung in das Datei Verzeichnis
enable mcrypt setup de MCrypt einschalten
enforce ssl (allows to specify just a path above) setup de Erzwinge SSL (erlaubt darüber nur einen Pfad anzugeben)
enter some random text for app session encryption setup de Zufallstext zur Verschlüsselung der Anwendungssitzung
@ -322,6 +323,7 @@ export has been completed! setup de Export ist abgeschlossen!
failed to mount backup directory! setup de Konnte Datensicherungsverzeichnis nicht mounten!
failed updating user "%1" dn="%2"! setup de Konnte Benutzer "%1" dn="%2" nicht aktualisieren!
failed writing configuration file header.inc.php, check the permissions !!! setup de Fehler beim Schreiben der Konfigurationsdatei header.inc.php, bitte überprüfen Sie die Zugriffsrechte !!!
fallback authentication setup de Rückfall Authentifizierung
false setup de Falsch
file setup de DATEI
file type, size, version, etc. setup de Dateityp, Größe, Version usw.
@ -367,6 +369,7 @@ however, the application may still work setup de Wie auch immer, die Anwendung m
http auth types (comma-separated) to use without login-page, eg. "ntlm" setup de HTTP Authetifizierungs Typen (Komma-getrennt) die ohne Login Seite benutzt werden sollen, zB. "NTLM"
identity provider setup de Identitätsprovider
if no acl records for user or any group the user is a member of setup de Wenn es keinen ACL-Eintrag für einen Benutzer oder eine Gruppe, der er angehört gibt
if primary authentication is not successful fall back to passwords synced into account-storage setup de Wenn die primäre Authentifizierung NICHT erfolgreich ist, falle auf die synchronisierten Passwörter des Benutzer Storage zurück
if safe_mode is turned on, egw is not able to change certain settings on runtime, nor can we load any not yet loaded module. setup de Wenn safe_mode eingeschaltet ist, kann EGw verschiedene Einstellungen nicht mehr zur Laufzeit ändern, noch können wir nicht geladene Erweiterungen (php extensions) laden.
if the application has no defined tables, selecting upgrade should remedy the problem setup de Wenn die Anwendung keine definierten Tabellen hat, wählen Sie überarbeiten. Das Problem sollte damit behoben werden.
if using ads (active directory) setup de Wenn Sie ADS (Active Directory) benutzen

View File

@ -15,8 +15,8 @@
%1 not allowed to create in univention. setup en %1 not allowed to create in Univention.
%1 password set in %2. setup en %1 password set in %2.
%1 passwords updated, %3 errors setup en %1 passwords updated, %3 errors
%1 users and %2 groups created, %3 errors setup en %1 users and %2 groups created, %3 errors.
%1 the configuration file. setup en %1 the configuration file.
%1 users and %2 groups created, %3 errors setup en %1 users and %2 groups created, %3 errors.
'%1' is no valid domain name! setup en '%1' is no valid domain name!
'%1' is not allowed as %2. arguments of option %3 !!! setup en '%1' is not allowed as %2. arguments of option %3 !!!
'%1' must be integer setup en %1 must be an integer value.
@ -293,6 +293,7 @@ email-address setup en EMail-address
emailadmin mail account saved: setup en EMailAdmin mail account saved:
emailadmin profile updated: setup en eMailAdmin profile updated:
enable for extra debug-messages setup en Enable for extra debug messages
enable logging of authentication to files-directory setup en Enable logging of authentication to files-directory
enable mcrypt setup en Enable MCrypt
enforce ssl (allows to specify just a path above) setup en Enforce SSL (allows to specify just a path above)
enter some random text for app session encryption setup en Enter some random text for app session encryption
@ -324,6 +325,7 @@ export has been completed! setup en Export has been completed!
failed to mount backup directory! setup en Failed to mount Backup directory!
failed updating user "%1" dn="%2"! setup en Failed updating user "%1" dn="%2"!
failed writing configuration file header.inc.php, check the permissions !!! setup en Failed writing configuration file header.inc.php, check the permissions!
fallback authentication setup en Fallback Authentication
false setup en False
file setup en FILE
file type, size, version, etc. setup en File type, size, version, etc.
@ -370,6 +372,7 @@ however, the application may still work setup en The application may still work
http auth types (comma-separated) to use without login-page, eg. "ntlm" setup en HTTP auth types, comma-separated to use without login page, eg. "NTLM"
identity provider setup en Identity Provider
if no acl records for user or any group the user is a member of setup en If no ACL records for user or any group the user is a member of
if primary authentication is not successful fall back to passwords synced into account-storage setup en If primary authentication is NOT successful fall back to passwords synced into account-storage
if safe_mode is turned on, egw is not able to change certain settings on runtime, nor can we load any not yet loaded module. setup en If safe_mode is turned on, EGw is not able to change certain settings on runtime, nor can we load any not yet loaded module.
if the application has no defined tables, selecting upgrade should remedy the problem setup en If the application has no defined tables, selecting upgrade should remedy the problem
if using ads (active directory) setup en If using ADS (Active Directory) authentication

View File

@ -129,6 +129,8 @@
<select name="newsettings[auth_type]">
{hook_auth_types}
</select>
<label><input type="checkbox" value="True" {checked_auth_log_True} name="newsettings[auth_log]"/>
{lang_Enable_logging_of_authentication_to_files-directory}: auth.log</label>
</td>
</tr>
@ -173,6 +175,13 @@
</tr>
<tr class="row_off">
<td>{lang_Fallback_Authentication}:</td>
<td>
<label><input type="checkbox" value="True" {checked_auth_fallback_True} name="newsettings[auth_fallback]"/>
{lang_If_primary_authentication_is_NOT_successful_fall_back_to_passwords_synced_into_account-storage}</label>
</td>
</tr>
<tr class="row_on">
<td>{lang_Select_where_you_want_to_store/retrieve_user_accounts}:</td>
<td>
<select name="newsettings[account_repository]">
@ -181,14 +190,14 @@
</td>
</tr>
<tr class="row_on">
<tr class="row_off">
<td>{lang_sql_encryption_type}:</td>
<td>
<select name="newsettings[sql_encryption_type]">{hook_sql_passwdhashes}</select>
</td>
</tr>
<tr class="row_off">
<tr class="row_on">
<td>{lang_Allow_password_migration}:</td>
<td>
<select name="newsettings[pwd_migration_allowed]">
@ -198,7 +207,7 @@
</td>
</tr>
<tr class="row_on">
<tr class="row_off">
<td>{lang_Allowed_migration_types_(comma-separated)}:</td>
<td>
<input name="newsettings[pwd_migration_types]" value="{value_pwd_migration_types}" size="20" />