Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ci/config-test.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
'attachment_reminder',
'markasjunk',
'zipdownload',
'persisted_login',
];

$config['archive_mbox'] = 'Archive';
Expand Down
6 changes: 5 additions & 1 deletion .tx/config
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ file_filter = plugins/password/localization/<lang>.inc
source_file = plugins/password/localization/en_US.inc
source_lang = en_US

[o:roundcube:p:roundcube-webmail:r:plugin-persisted-login]
file_filter = plugins/persisted_login/localization/<lang>.inc
source_file = plugins/persisted_login/localization/en_US.inc
source_lang = en_US

[o:roundcube:p:roundcube-webmail:r:plugin-subscriptions_option]
file_filter = plugins/subscriptions_option/localization/<lang>.inc
source_file = plugins/subscriptions_option/localization/en_US.inc
Expand All @@ -96,4 +101,3 @@ source_lang = en_US
file_filter = program/localization/<lang>/timezones.inc
source_file = program/localization/en_US/timezones.inc
source_lang = en_US

26 changes: 26 additions & 0 deletions plugins/persisted_login/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Persisted login Roundcubemail plugin
====================================

This plugin adds a toggle switch into the login form of Roundcubemail's "elastic" skin, that makes the session live for a configured number of days (instead of only for the session).

In effect, logins stay valid across network changes of clients, etc.

From a technical point of view this plugin (if enabled) overrides `$config['session_lifetime']` (which sets the session garbage collection max lifetime in PHP) to match the number of days set in its own config.

Usage
-----

Enable the plugin in your Roundcubemail's config:

```php
$config['plugins'] = [ …, 'persisted_login'];
```

By default logins are persisted for 7 days. That value can be changed via the config option `persisted_login_days` in the
config file of this plugin. (Make sure that the config file ends in `.php` to have it used by Roundcubemail.)


Credits
-------

Most of this code was actually written by [Github-Citizen](https://github.com/Github-Citizen) for https://github.com/roundcube/roundcubemail/pull/8689, which fell through due to styling issues, and only cleaned up and renamed for this plugin.
17 changes: 17 additions & 0 deletions plugins/persisted_login/config.inc.php.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

// Set the number of days a login will be valid for (if the user toggles the
// switch at login).
//
// The maximum number of days that can be set is 365 (1 year).
// The minimum value is 1.
// The default value is 7. This number will be also be used if the config option is set to something else than an
// integer.
//
// To disable the plugin, remove it from the list of plugins in Roundcube's config.inc.php.
//
// Technical note; This plugin will over-ride $config['session_lifetime']
// (which sets the session garbage collection max lifetime in PHP) to match the
// number of days set above.

$config['persisted_login_days'] = 7;
6 changes: 6 additions & 0 deletions plugins/persisted_login/localization/en_US.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

// When translating keep the '#' symbol in the sentence and it will be
// converted to the number of days set in the config.inc.php file.

$labels['switch_text'] = 'Persist login for # days';
26 changes: 26 additions & 0 deletions plugins/persisted_login/persisted_login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Run on 'elastic' and all derivate skins.
if (window.rcmail && (window.rcmail.env.skin == 'elastic' || rcmail.env.skin_extends?.includes('elastic'))) {
rcmail.addEventListener('init', () => {
const days = rcmail.env.persisted_login_days;
let txt = rcmail.gettext('switch_text', 'persisted_login');
txt = txt.replace('#', days);

const elems = $('<tr>').addClass('form-group row').append(
$('<td>').addClass('title').hide(),
$('<td>').addClass('input input-group input-group-lg').append(
$('<div>').addClass('custom-control custom-switch').css('padding', '1em 0').append(
$('<input>').attr({
type: 'checkbox',
class: 'custom-control-input',
id: '_persisted_login',
name: '_persisted_login',
value: '1',
}),
$('<label>').attr({ class: 'custom-control-label', for: '_persisted_login' }).text(txt)
)
)
);

$('#login-form table tbody').append(elems);
});
}
56 changes: 56 additions & 0 deletions plugins/persisted_login/persisted_login.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/**
* Inject a toggle switch into the login form that makes the session live for a
* configured number of days (instead of only for the session).
*/

class persisted_login extends rcube_plugin
{
private $rc;
private $days;

public function onload(): void
{
$this->rc = rcmail::get_instance();
$this->load_config();
$configured_days = $this->rc->config->get('persisted_login_days');
if (!is_int($configured_days)) {
$configured_days = 7;
}
// Make sure the value is in the range 1..365.
$this->days = min(max(1, $configured_days), 365);
$this->rc->config->set('session_lifetime', $this->days * 24 * 60);
}

#[\Override]
public function init(): void
{
$this->rc->output->set_env('persisted_login_days', $this->days);
$this->add_hook('template_object_loginform', [$this, 'login_page_template']);
$this->add_hook('login_after', [$this, 'login_success']);
}

public function login_page_template(array $args): array
{
$this->add_texts('localization', true);
$this->include_script('persisted_login.js');
return $args;
}

public function login_success(array $args): array
{
if (empty($_POST['_persisted_login'])) {
return $args;
}

$sessCookieName = $this->rc->config->get('session_name') ?: 'roundcube_sessid';
$authCookieName = $this->rc->config->get('session_auth_name') ?: 'roundcube_sessauth';
$sessCookieValue = session_id();
$authCookieValue = (isset($_COOKIE[$authCookieName])) ? $_COOKIE[$authCookieName] : 'Error: Auth Cookie Missing';
$exp = time() + ($this->days * 24 * 60 * 60);
rcube_utils::setcookie($sessCookieName, $sessCookieValue, $exp);
rcube_utils::setcookie($authCookieName, $authCookieValue, $exp);
return $args;
}
}
6 changes: 6 additions & 0 deletions tests/Browser/Logon/LoginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public function testLogin()
$browser->assertVisible('#rcmloginsubmit');
$browser->assertSee($this->app->config->get('product_name'));

// Check for element and text from the persisted_login plugin.
// The checkbox is hidden and replaced by a toggle switch via CSS, thus we check for presence, not for
// visibility.
$browser->assertPresent('input#_persisted_login');
$browser->assertSee('Persist login for 7 days');

// Support link
if ($url = $this->app->config->get('support_url')) {
$browser->assertSeeLink('Get support');
Expand Down
Loading