Merge branch 'master' into refactor-config

This commit is contained in:
kaiyou
2018-11-08 21:43:05 +01:00
111 changed files with 2696 additions and 1098 deletions

View File

@@ -1,17 +1,21 @@
FROM python:3-alpine
FROM alpine:3.8
# python3 shared with most images
RUN apk add --no-cache \
python3 py3-pip \
&& pip3 install --upgrade pip
# Image specific layers under this line
RUN mkdir -p /app
WORKDIR /app
COPY requirements-prod.txt requirements.txt
RUN apk add --no-cache openssl \
&& apk add --no-cache --virtual build-dep openssl-dev libffi-dev python-dev build-base \
&& pip install -r requirements.txt \
RUN apk add --no-cache openssl curl \
&& apk add --no-cache --virtual build-dep openssl-dev libffi-dev python3-dev build-base \
&& pip3 install -r requirements.txt \
&& apk del --no-cache build-dep
COPY mailu ./mailu
COPY migrations ./migrations
COPY start.sh /start.sh
COPY start.py /start.py
RUN pybabel compile -d mailu/translations
@@ -19,4 +23,6 @@ EXPOSE 80/tcp
VOLUME ["/data"]
ENV FLASK_APP mailu
CMD ["/start.sh"]
CMD /start.py
HEALTHCHECK CMD curl -f -L http://localhost/ui || exit 1

View File

@@ -54,3 +54,4 @@ def create_app():
"""
config = configuration.ConfigManager()
return create_app_from_config(config)

View File

@@ -32,9 +32,6 @@ if exists "X-Virus" {
stop;
}
{% if user.reply_enabled %}
if currentdate :value "le" "date" "{{ user.reply_enddate }}"
{
vacation :days 1 :subject "{{ user.reply_subject }}" "{{ user.reply_body }}";
}
{% if user.reply_active %}
vacation :days 1 :subject "{{ user.reply_subject }}" "{{ user.reply_body }}";
{% endif %}

View File

@@ -3,13 +3,24 @@ from mailu.internal import internal
from flask import current_app as app
import flask
import socket
import os
@internal.route("/dovecot/passdb/<user_email>")
def dovecot_passdb_dict(user_email):
user = models.User.query.get(user_email) or flask.abort(404)
allow_nets = []
allow_nets.append(
app.config.get("POD_ADDRESS_RANGE") or
socket.gethostbyname(app.config["HOST_FRONT"])
)
if os.environ["WEBMAIL"] != "none":
allow_nets.append(socket.gethostbyname(app.config["HOST_WEBMAIL"]))
print(allow_nets)
return flask.jsonify({
"password": user.password,
"password": None,
"nopassword": "Y",
"allow_nets": ",".join(allow_nets)
})

View File

@@ -40,11 +40,14 @@ class IdnaEmail(db.TypeDecorator):
impl = db.String(255, collation="NOCASE")
def process_bind_param(self, value, dialect):
localpart, domain_name = value.split('@')
return "{0}@{1}".format(
localpart,
idna.encode(domain_name).decode('ascii'),
)
try:
localpart, domain_name = value.split('@')
return "{0}@{1}".format(
localpart,
idna.encode(domain_name).decode('ascii'),
)
except ValueError:
pass
def process_result_value(self, value, dialect):
localpart, domain_name = value.split('@')
@@ -276,6 +279,8 @@ class User(Base, Email):
reply_enabled = db.Column(db.Boolean(), nullable=False, default=False)
reply_subject = db.Column(db.String(255), nullable=True, default=None)
reply_body = db.Column(db.Text(), nullable=True, default=None)
reply_startdate = db.Column(db.Date, nullable=False,
default=date(1900, 1, 1))
reply_enddate = db.Column(db.Date, nullable=False,
default=date(2999, 12, 31))
@@ -295,14 +300,24 @@ class User(Base, Email):
@property
def destination(self):
if self.forward_enabled:
result = self.self.forward_destination
result = self.forward_destination
if self.forward_keep:
result += ',' + self.email
return result
else:
return self.email
scheme_dict = {'BLF-CRYPT': "bcrypt",
@property
def reply_active(self):
now = date.today()
return (
self.reply_enabled and
self.reply_startdate < now and
self.reply_enddate > now
)
scheme_dict = {'PBKDF2': "pbkdf2_sha512",
'BLF-CRYPT': "bcrypt",
'SHA512-CRYPT': "sha512_crypt",
'SHA256-CRYPT': "sha256_crypt",
'MD5-CRYPT': "md5_crypt",
@@ -315,8 +330,14 @@ class User(Base, Email):
)
def check_password(self, password):
context = User.pw_context
reference = re.match('({[^}]+})?(.*)', self.password).group(2)
return self.get_password_context().verify(password, reference)
result = context.verify(password, reference)
if result and context.identify(reference) != context.default_scheme():
self.set_password(password)
db.session.add(self)
db.session.commit()
return result
def set_password(self, password, hash_scheme=None, raw=False):
"""Set password for user with specified encryption scheme

View File

@@ -117,6 +117,7 @@ class UserReplyForm(flask_wtf.FlaskForm):
reply_subject = fields.StringField(_('Reply subject'))
reply_body = fields.StringField(_('Reply body'),
widget=widgets.TextArea())
reply_startdate = fields.html5.DateField(_('Start of vacation'))
reply_enddate = fields.html5.DateField(_('End of vacation'))
submit = fields.SubmitField(_('Update'))

View File

@@ -13,14 +13,17 @@
<form class="form" method="post" role="form">
{{ form.hidden_tag() }}
{{ macros.form_field(form.reply_enabled,
onchange="if(this.checked){$('#reply_subject,#reply_body,#reply_enddate').removeAttr('readonly')}
onchange="if(this.checked){$('#reply_subject,#reply_body,#reply_enddate,#reply_startdate').removeAttr('readonly')}
else{$('#reply_subject,#reply_body,#reply_enddate').attr('readonly', '')}") }}
{{ macros.form_field(form.reply_subject,
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
{{ macros.form_field(form.reply_body, rows=10,
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
{{ macros.form_field(form.reply_enddate,
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
{{ macros.form_field(form.reply_startdate,
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
{{ macros.form_field(form.submit) }}
</form>
{% endcall %}

View File

@@ -0,0 +1,24 @@
""" Add a start day for vacations
Revision ID: 3b281286c7bd
Revises: 25fd6c7bcb4a
Create Date: 2018-09-27 22:20:08.158553
"""
revision = '3b281286c7bd'
down_revision = '25fd6c7bcb4a'
from alembic import op
import sqlalchemy as sa
def upgrade():
with op.batch_alter_table('user') as batch:
batch.add_column(sa.Column('reply_startdate', sa.Date(), nullable=False,
server_default="1900-01-01"))
def downgrade():
with op.batch_alter_table('user') as batch:
batch.drop_column('reply_startdate')

7
core/admin/start.py Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/python3
import os
os.system("flask mailu advertise")
os.system("flask db upgrade")
os.system("gunicorn -w 4 -b :80 --access-logfile - --error-logfile - --preload 'mailu:create_app()'")

View File

@@ -1,6 +0,0 @@
#!/bin/sh
flask mailu advertise
flask db upgrade
gunicorn -w 4 -b :80 --access-logfile - --error-logfile - --preload "$FLASK_APP:create_app()"