diff --git a/core/admin/Dockerfile b/core/admin/Dockerfile index 8adf2ca5..5c1227e5 100644 --- a/core/admin/Dockerfile +++ b/core/admin/Dockerfile @@ -39,7 +39,7 @@ RUN set -eu \ && pip install -r requirements.txt \ && apk del --no-cache build-dep -COPY --from=assets static ./mailu/ui/static +COPY --from=assets static ./mailu/static COPY mailu ./mailu COPY migrations ./migrations COPY start.py /start.py @@ -53,4 +53,4 @@ ENV FLASK_APP mailu CMD /start.py -HEALTHCHECK CMD curl -f -L http://localhost/ui/login?next=ui.index || exit 1 +HEALTHCHECK CMD curl -f -L http://localhost/sso/login?next=ui.index || exit 1 diff --git a/core/admin/assets/app.js b/core/admin/assets/app.js index 5df8052c..54602d1f 100644 --- a/core/admin/assets/app.js +++ b/core/admin/assets/app.js @@ -43,7 +43,7 @@ $('document').ready(function() { var infinity = $(this).data('infinity'); var step = $(this).attr('step'); $(this).on('input', function() { - value_element.text((infinity && this.value == 0) ? '∞' : this.value/step); + value_element.text((infinity && this.value == 0) ? '∞' : (this.value/step).toFixed(2)); }).trigger('input'); } }); diff --git a/core/admin/mailu/__init__.py b/core/admin/mailu/__init__.py index 7fb04380..e4024e47 100644 --- a/core/admin/mailu/__init__.py +++ b/core/admin/mailu/__init__.py @@ -11,7 +11,7 @@ import hmac def create_app_from_config(config): """ Create a new application based on the given configuration """ - app = flask.Flask(__name__) + app = flask.Flask(__name__, static_folder='static', static_url_path='/static') app.cli.add_command(manage.mailu) # Bootstrap is used for error display and flash messages @@ -58,10 +58,10 @@ def create_app_from_config(config): ) # Import views - from mailu import ui, internal - app.register_blueprint(ui.ui, url_prefix='/ui') + from mailu import ui, internal, sso + app.register_blueprint(ui.ui, url_prefix=app.config['WEB_ADMIN']) app.register_blueprint(internal.internal, url_prefix='/internal') - + app.register_blueprint(sso.sso, url_prefix='/sso') return app @@ -70,3 +70,4 @@ def create_app(): """ config = configuration.ConfigManager() return create_app_from_config(config) + diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index d33efa12..9829f798 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -58,6 +58,7 @@ DEFAULT_CONFIG = { # Web settings 'SITENAME': 'Mailu', 'WEBSITE': 'https://mailu.io', + 'ADMIN' : 'none', 'WEB_ADMIN': '/admin', 'WEB_WEBMAIL': '/webmail', 'WEBMAIL': 'none', diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index 04d6d94e..027db935 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -78,12 +78,6 @@ def handle_authentication(headers): # Authenticated user elif method == "plain": is_valid_user = False - if headers["Auth-Port"] == '25': - return { - "Auth-Status": "AUTH not supported", - "Auth-Error-Code": "502 5.5.1", - "Auth-Wait": 0 - } # According to RFC2616 section 3.7.1 and PEP 3333, HTTP headers should # be ASCII and are generally considered ISO8859-1. However when passing # the password, nginx does not transcode the input UTF string, thus diff --git a/core/admin/mailu/internal/templates/default.sieve b/core/admin/mailu/internal/templates/default.sieve index 5e995611..a29e7aba 100644 --- a/core/admin/mailu/internal/templates/default.sieve +++ b/core/admin/mailu/internal/templates/default.sieve @@ -19,7 +19,7 @@ if header :index 2 :matches "Received" "from * by * for <*>; *" } {% if user.spam_enabled %} -if spamtest :percent :value "gt" :comparator "i;ascii-numeric" "{{ user.spam_threshold }}" +if spamtest :percent :value "gt" :comparator "i;ascii-numeric" "{{ user.spam_threshold }}" { setflag "\\seen"; fileinto :create "Junk"; @@ -32,6 +32,6 @@ if exists "X-Virus" { stop; } -{% if user.reply_active %} +{% if user.reply_active %} vacation :days 1 {% if user.displayed_name != "" %}:from "{{ user.displayed_name }} <{{ user.email }}>"{% endif %} :subject "{{ user.reply_subject }}" "{{ user.reply_body }}"; {% endif %} diff --git a/core/admin/mailu/internal/views/auth.py b/core/admin/mailu/internal/views/auth.py index c5cd9e28..344be78b 100644 --- a/core/admin/mailu/internal/views/auth.py +++ b/core/admin/mailu/internal/views/auth.py @@ -11,6 +11,13 @@ def nginx_authentication(): """ Main authentication endpoint for Nginx email server """ client_ip = flask.request.headers["Client-Ip"] + headers = flask.request.headers + if headers["Auth-Port"] == '25' and headers['Auth-Method'] == 'plain': + response = flask.Response() + response.headers['Auth-Status'] = 'AUTH not supported' + response.headers['Auth-Error-Code'] = '502 5.5.1' + utils.limiter.rate_limit_ip(client_ip) + return response if utils.limiter.should_rate_limit_ip(client_ip): status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit') response = flask.Response() @@ -41,7 +48,7 @@ def nginx_authentication(): elif is_valid_user: utils.limiter.rate_limit_user(username, client_ip) else: - rate_limit_ip(client_ip) + utils.limiter.rate_limit_ip(client_ip) return response @internal.route("/auth/admin") diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py index 54f4b826..937c9f49 100644 --- a/core/admin/mailu/manage.py +++ b/core/admin/mailu/manage.py @@ -119,7 +119,7 @@ def password(localpart, domain_name, password): """ Change the password of an user """ email = f'{localpart}@{domain_name}' - user = models.User.query.get(email) + user = models.User.query.get(email) if user: user.set_password(password) else: diff --git a/core/admin/mailu/sso/__init__.py b/core/admin/mailu/sso/__init__.py new file mode 100644 index 00000000..98b5abd0 --- /dev/null +++ b/core/admin/mailu/sso/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +sso = Blueprint('sso', __name__, static_folder=None, template_folder='templates') + +from mailu.sso.views import * diff --git a/core/admin/mailu/sso/forms.py b/core/admin/mailu/sso/forms.py new file mode 100644 index 00000000..c190b8bc --- /dev/null +++ b/core/admin/mailu/sso/forms.py @@ -0,0 +1,11 @@ +from wtforms import validators, fields +from flask_babel import lazy_gettext as _ +import flask_wtf + +class LoginForm(flask_wtf.FlaskForm): + class Meta: + csrf = False + email = fields.StringField(_('E-mail'), [validators.Email(), validators.DataRequired()]) + pw = fields.PasswordField(_('Password'), [validators.DataRequired()]) + submitAdmin = fields.SubmitField(_('Sign in')) + submitWebmail = fields.SubmitField(_('Sign in')) diff --git a/core/admin/mailu/sso/templates/base_sso.html b/core/admin/mailu/sso/templates/base_sso.html new file mode 100644 index 00000000..9dfb25a5 --- /dev/null +++ b/core/admin/mailu/sso/templates/base_sso.html @@ -0,0 +1,86 @@ +{%- import "macros.html" as macros %} +{%- import "bootstrap/utils.html" as utils %} + + +
+ + + + + MX
dla swojej "
+"Jeśli nie wiesz, jak skonfigurować rekord MX
dla swojej "
"strefy DNS,\n"
"skontaktuj się z dostawcą DNS lub administratorem. Proszę również "
"poczekać\n"
-"kilka minut po ustawieniu MX
, żeby pamięć podręczna "
+"kilka minut po ustawieniu MX
, żeby pamięć podręczna "
"serwera lokalnego wygasła."
#: mailu/ui/templates/fetch/create.html:4
diff --git a/core/admin/mailu/ui/__init__.py b/core/admin/mailu/ui/__init__.py
index ec3601a1..49338cd1 100644
--- a/core/admin/mailu/ui/__init__.py
+++ b/core/admin/mailu/ui/__init__.py
@@ -1,6 +1,6 @@
from flask import Blueprint
-ui = Blueprint('ui', __name__, static_folder='static', template_folder='templates')
+ui = Blueprint('ui', __name__, static_folder=None, template_folder='templates')
from mailu.ui.views import *
diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py
index 60be1699..24d6f899 100644
--- a/core/admin/mailu/ui/forms.py
+++ b/core/admin/mailu/ui/forms.py
@@ -44,15 +44,6 @@ class MultipleEmailAddressesVerify(object):
class ConfirmationForm(flask_wtf.FlaskForm):
submit = fields.SubmitField(_('Confirm'))
-
-class LoginForm(flask_wtf.FlaskForm):
- class Meta:
- csrf = False
- email = fields.StringField(_('E-mail'), [validators.Email()])
- pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
- submit = fields.SubmitField(_('Sign in'))
-
-
class DomainForm(flask_wtf.FlaskForm):
name = fields.StringField(_('Domain name'), [validators.DataRequired()])
max_users = fields_.IntegerField(_('Maximum user count'), [validators.NumberRange(min=-1)], default=10)
diff --git a/core/admin/mailu/ui/templates/base.html b/core/admin/mailu/ui/templates/base.html
index fc27d12b..e646e579 100644
--- a/core/admin/mailu/ui/templates/base.html
+++ b/core/admin/mailu/ui/templates/base.html
@@ -1,15 +1,15 @@
{%- import "macros.html" as macros %}
{%- import "bootstrap/utils.html" as utils %}
-
+