Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add impersonation feature for admins to webmail #3163

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/admin/mailu/internal/views/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ def user_authentication():
if (not flask_login.current_user.is_anonymous
and flask_login.current_user.enabled):
response = flask.Response()
email = flask_login.current_user.get_id()
original_email = flask_login.current_user.get_id()
email = flask.session.get('webmail_user_email', original_email)
response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, email, "")
response.headers["X-User-Token"] = utils.gen_temp_token(email, flask.session)
response.headers["X-Original-User"] = models.IdnaEmail.process_bind_param(flask_login, original_email, "")
return response
return flask.abort(403)

Expand Down
2 changes: 1 addition & 1 deletion core/admin/mailu/ui/templates/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
<li class="nav-header text-uppercase text-primary" role="none">{% trans %}Go to{% endtrans %}</li>
{%- if config["WEBMAIL"] != "none" and current_user.is_authenticated %}
<li class="nav-item" role="none">
<a href="{{ config["WEB_WEBMAIL"] }}" class="nav-link" role="menuitem">
<a href="{{ url_for('.user_webmail') }}" class="nav-link" role="menuitem">
<i class="nav-icon far fa-envelope"></i>
<p>{% trans %}Webmail{% endtrans %}</p>
</a>
Expand Down
4 changes: 4 additions & 0 deletions core/admin/mailu/ui/templates/user/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@
</td>
<td>
<a href="{{ url_for('.user_settings', user_email=user.email) }}" title="{% trans %}Settings{% endtrans %}"><i class="fa fa-wrench"></i></a>&nbsp;
<a href="{{ url_for('.token_list', user_email=user.email) }}" title="{% trans %}Authentication tokens{% endtrans %}"><i class="fa fa-ticket-alt"></i></a>&nbsp;
<a href="{{ url_for('.user_reply', user_email=user.email) }}" title="{% trans %}Auto-reply{% endtrans %}"><i class="fa fa-plane"></i></a>&nbsp;
{%- if config["FETCHMAIL_ENABLED"] -%}
<a href="{{ url_for('.fetch_list', user_email=user.email) }}" title="{% trans %}Fetched accounts{% endtrans %}"><i class="fa fa-download"></i></a>&nbsp;
{%- endif -%}
{%- if config["WEBMAIL"] != "none" -%}
<a href="{{ url_for('.user_webmail', user_email=user.email) }}" title="{% trans %}Webmail{% endtrans %}"><i class="fa fa-envelope"></i></a>&nbsp;
{%- endif -%}
</td>
<td>{{ user }}</td>
<td data-sort="{{ user.allow_spoofing*4 + user.enable_imap*2 + user.enable_pop }}">
Expand Down
15 changes: 15 additions & 0 deletions core/admin/mailu/ui/templates/webmail_impersonation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{%- extends "base.html" %}

{%- block title %}
{% trans %}Webmail{% endtrans %}
{%- endblock %}

{%- block subtitle %}
{% trans %}Access to Webmail mailbox of{% endtrans %} {{ user_email }}.
{%- endblock %}

{%- block content %}
<div class="embed-responsive embed-responsive-1by1">
<iframe class="embed-responsive-item" src="{{ config["WEB_WEBMAIL"] }}/sso.php"></iframe>
</div>
{%- endblock %}
14 changes: 14 additions & 0 deletions core/admin/mailu/ui/views/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ def user_reply(user_email):
flask.url_for('.user_list', domain_name=user.domain.name))
return flask.render_template('user/reply.html', form=form, user=user)

@ui.route('/user/webmail', methods=['GET'], defaults={'user_email': None})
@ui.route('/user/webmail/<path:user_email>', methods=['GET'])
@access.owner(models.User, 'user_email')
def user_webmail(user_email):
user_email_or_current = user_email or flask_login.current_user.email
user = models.User.query.get(user_email_or_current) or flask.abort(404)

flask.session['webmail_user_email'] = user.get_id()

if (user_email):
return flask.render_template('webmail_impersonation.html', user_email=user_email)

return flask.redirect(f'{app.config["WEB_WEBMAIL"]}/sso.php')


@ui.route('/user/signup', methods=['GET', 'POST'])
@ui.route('/user/signup/<domain_name>', methods=['GET', 'POST'])
Expand Down
2 changes: 1 addition & 1 deletion core/admin/mailu/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def verify_temp_token(email, token):
if token.startswith('token-'):
if sessid := app.session_store.get(token):
session = MailuSession(sessid, app)
if session.get('_user_id', '') == email:
if session.get('webmail_user_email', session.get('_user_id', '')) == email:
return True
except:
pass
Expand Down
2 changes: 2 additions & 0 deletions core/nginx/conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,10 @@ http {
auth_request /internal/auth/user;
auth_request_set $user $upstream_http_x_user;
auth_request_set $token $upstream_http_x_user_token;
auth_request_set $original_user $upstream_http_x_original_user;
proxy_set_header X-Remote-User $user;
proxy_set_header X-Remote-User-Token $token;
proxy_set_header X-Remote-Original-User $original_user;
error_page 403 @sso_login;
proxy_pass http://$webmail;
}
Expand Down
6 changes: 6 additions & 0 deletions docs/webadministration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ Click the submit button to apply settings. With the default polling interval, fe
Make sure ``FETCHMAIL_ENABLED`` is set to ``true`` in ``mailu.env`` to enable fetching and showing fetchmail in the admin interface.


.. _webadministration_authentication_tokens:

Authentication tokens
---------------------

Expand Down Expand Up @@ -320,10 +322,14 @@ This page is also accessible for domain managers. On the users page new users ca

* Settings. Access the settings page of the user. See :ref:`the settings page <webadministration_settings>` for more information.

* Authentication tokens. Access the authentication tokens page of the user. See :ref:`the authentication tokens page <webadministration_authentication_tokens>` for more information.

* Auto-reply. Access the auto-reply page of the user. See the :ref:`auto-reply page <webadministration_auto-reply>` for more information.

* Fetched accounts. Access the fetched accounts page of the user. See the :ref:`fetched accounts page <webadministration_fetched_accounts>` for more information.

* Webmail. Login to webmail as the respective user to access the user's mailbox.

This page also shows an overview of the following settings of an user:

* Email. The email address of the user.
Expand Down
1 change: 1 addition & 0 deletions towncrier/newsfragments/3106.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add impersonation feature for admins to webmail
3 changes: 3 additions & 0 deletions webmails/roundcube/login/localization/en_US.inc
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php
$labels = [];
$labels['mailu'] = 'Mailu';

$messages['impersonationwarning'] = 'You are currently logged in as <strong>$email</strong>. Click $link.';
$messages['impersonationwarninglink'] = 'here to return to <strong>$email</strong>';
?>
33 changes: 32 additions & 1 deletion webmails/roundcube/login/mailu.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,38 @@ function startup($args)
);
}
// sso
if (empty($_SESSION['user_id'])) {
if (empty($_SESSION['username']) || (!empty($_SERVER['HTTP_X_REMOTE_USER']) && $_SESSION['username'] !== $_SERVER['HTTP_X_REMOTE_USER'])) {
$args['task'] = 'login';
$args['action'] = 'login';
}

if (!$rcmail->output->framed && !empty($_SESSION['mailu_original_username']) && $_SESSION['mailu_original_username'] !== $_SESSION['username']) {
$currentUser = $_SESSION['username'];
$originalUser = $_SESSION['mailu_original_username'];

$link = html::a(
sprintf('%s/user/webmail', $rcmail->config->get('support_url')),
$this->gettext([
'name' => 'impersonationwarninglink',
'vars' => [
'email' => html::quote($originalUser),
],
]),
);

$this->api->output->add_header(html::tag(
'div',
'boxwarning',
$this->gettext([
'name' => 'impersonationwarning',
'vars' => [
'email' => html::quote($currentUser),
'link' => $link,
],
]),
));
}

return $args;
}

Expand All @@ -55,6 +84,8 @@ function authenticate($args)
$args['user'] = $_SERVER['HTTP_X_REMOTE_USER'];
$args['pass'] = $_SERVER['HTTP_X_REMOTE_USER_TOKEN'];

$_SESSION['mailu_original_username'] = $_SERVER['HTTP_X_REMOTE_ORIGINAL_USER'];

$args['cookiecheck'] = false;
$args['valid'] = true;

Expand Down