Skip to content

Commit 492aa33

Browse files
committed
New plugin: Persisted login
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).
1 parent b86a9b8 commit 492aa33

File tree

8 files changed

+143
-1
lines changed

8 files changed

+143
-1
lines changed

.ci/config-test.inc.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
'attachment_reminder',
3636
'markasjunk',
3737
'zipdownload',
38+
'persisted_login',
3839
];
3940

4041
$config['archive_mbox'] = 'Archive';

.tx/config

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ file_filter = plugins/password/localization/<lang>.inc
7272
source_file = plugins/password/localization/en_US.inc
7373
source_lang = en_US
7474

75+
[o:roundcube:p:roundcube-webmail:r:plugin-persisted-login]
76+
file_filter = plugins/persisted_login/localization/<lang>.inc
77+
source_file = plugins/persisted_login/localization/en_US.inc
78+
source_lang = en_US
79+
7580
[o:roundcube:p:roundcube-webmail:r:plugin-subscriptions_option]
7681
file_filter = plugins/subscriptions_option/localization/<lang>.inc
7782
source_file = plugins/subscriptions_option/localization/en_US.inc
@@ -96,4 +101,3 @@ source_lang = en_US
96101
file_filter = program/localization/<lang>/timezones.inc
97102
source_file = program/localization/en_US/timezones.inc
98103
source_lang = en_US
99-

plugins/persisted_login/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Persisted login Roundcubemail plugin
2+
====================================
3+
4+
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).
5+
6+
In effect, logins stay valid across network changes of clients, etc.
7+
8+
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.
9+
10+
Usage
11+
-----
12+
13+
Enable the plugin in your Roundcubemail's config:
14+
15+
```php
16+
$config['plugins'] = [ …, 'persisted_login'];
17+
```
18+
19+
By default logins are persisted for 7 days. That value can be changed via the config option `persisted_login_days` in the
20+
config file of this plugin. (Make sure that the config file ends in `.php` to have it used by Roundcubemail.)
21+
22+
23+
Credits
24+
-------
25+
26+
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.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
// Set the number of days a login will be valid for (if the user toggles the
4+
// switch at login).
5+
//
6+
// The maximum number of days that can be set is 365 (1 year).
7+
// The minimum value is 1.
8+
// The default value is 7. This number will be also be used if the config option is set to something else than an
9+
// integer.
10+
//
11+
// To disable the plugin, remove it from the list of plugins in Roundcube's config.inc.php.
12+
//
13+
// Technical note; This plugin will over-ride $config['session_lifetime']
14+
// (which sets the session garbage collection max lifetime in PHP) to match the
15+
// number of days set above.
16+
17+
$config['persisted_login_days'] = 7;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// When translating keep the '#' symbol in the sentence and it will be
4+
// converted to the number of days set in the config.inc.php file.
5+
6+
$labels['switch_text'] = 'Persist login for # days';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Run on 'elastic' and all derivate skins.
2+
if (window.rcmail && (window.rcmail.env.skin == 'elastic' || rcmail.env.skin_extends?.includes('elastic'))) {
3+
rcmail.addEventListener('init', () => {
4+
const days = rcmail.env.persisted_login_days;
5+
let txt = rcmail.gettext('switch_text', 'persisted_login');
6+
txt = txt.replace('#', days);
7+
8+
const elems = $('<tr>').addClass('form-group row').append(
9+
$('<td>').addClass('title').hide(),
10+
$('<td>').addClass('input input-group input-group-lg').append(
11+
$('<div>').addClass('custom-control custom-switch').css('padding', '1em 0').append(
12+
$('<input>').attr({
13+
type: 'checkbox',
14+
class: 'custom-control-input',
15+
id: '_persisted_login',
16+
name: '_persisted_login',
17+
value: '1',
18+
}),
19+
$('<label>').attr({ class: 'custom-control-label', for: '_persisted_login' }).text(txt)
20+
)
21+
)
22+
);
23+
24+
$('#login-form table tbody').append(elems);
25+
});
26+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/**
4+
* Inject a toggle switch into the login form that makes the session live for a
5+
* configured number of days (instead of only for the session).
6+
*/
7+
8+
class persisted_login extends rcube_plugin
9+
{
10+
private $rc;
11+
private $days;
12+
13+
public function onload(): void
14+
{
15+
$this->rc = rcmail::get_instance();
16+
$this->load_config();
17+
$configured_days = $this->rc->config->get('persisted_login_days');
18+
if (!is_int($configured_days)) {
19+
$configured_days = 7;
20+
}
21+
// Make sure the value is in the range 1..365.
22+
$this->days = min(max(1, $configured_days), 365);
23+
$this->rc->config->set('session_lifetime', $this->days * 24 * 60);
24+
}
25+
26+
#[\Override]
27+
public function init(): void
28+
{
29+
$this->rc->output->set_env('persisted_login_days', $this->days);
30+
$this->add_hook('template_object_loginform', [$this, 'login_page_template']);
31+
$this->add_hook('login_after', [$this, 'login_success']);
32+
}
33+
34+
public function login_page_template(array $args): array
35+
{
36+
$this->add_texts('localization', true);
37+
$this->include_script('persisted_login.js');
38+
return $args;
39+
}
40+
41+
public function login_success(array $args): array
42+
{
43+
if (empty($_POST['_persisted_login'])) {
44+
return $args;
45+
}
46+
47+
$sessCookieName = $this->rc->config->get('session_name') ?: 'roundcube_sessid';
48+
$authCookieName = $this->rc->config->get('session_auth_name') ?: 'roundcube_sessauth';
49+
$sessCookieValue = session_id();
50+
$authCookieValue = (isset($_COOKIE[$authCookieName])) ? $_COOKIE[$authCookieName] : 'Error: Auth Cookie Missing';
51+
$exp = time() + ($this->days * 24 * 60 * 60);
52+
rcube_utils::setcookie($sessCookieName, $sessCookieValue, $exp);
53+
rcube_utils::setcookie($authCookieName, $authCookieValue, $exp);
54+
return $args;
55+
}
56+
}

tests/Browser/Logon/LoginTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ public function testLogin()
3838
$browser->assertVisible('#rcmloginsubmit');
3939
$browser->assertSee($this->app->config->get('product_name'));
4040

41+
// Check for element and text from the persisted_login plugin.
42+
// The checkbox is hidden and replaced by a toggle switch via CSS, thus we check for presence, not for
43+
// visibility.
44+
$browser->assertPresent('input#_persisted_login');
45+
$browser->assertSee('Persist login for 7 days');
46+
4147
// Support link
4248
if ($url = $this->app->config->get('support_url')) {
4349
$browser->assertSeeLink('Get support');

0 commit comments

Comments
 (0)