mirror of
https://github.com/Mailu/Mailu.git
synced 2025-03-05 14:55:20 +02:00
Fix 2080
Ensure that webmail tokens are in sync with sessions
This commit is contained in:
parent
c6aefd8e04
commit
fe18cf9743
@ -1,4 +1,4 @@
|
|||||||
from mailu import models
|
from mailu import models, utils
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
|
|
||||||
import re
|
import re
|
||||||
@ -32,8 +32,8 @@ def check_credentials(user, password, ip, protocol=None, auth_port=None):
|
|||||||
return False
|
return False
|
||||||
is_ok = False
|
is_ok = False
|
||||||
# webmails
|
# webmails
|
||||||
if len(password) == 64 and auth_port in ['10143', '10025']:
|
if auth_port in ['10143', '10025'] and password.startswith('token-'):
|
||||||
if user.verify_temp_token(password):
|
if utils.verify_temp_token(user.get_id(), password):
|
||||||
is_ok = True
|
is_ok = True
|
||||||
# All tokens are 32 characters hex lowercase
|
# All tokens are 32 characters hex lowercase
|
||||||
if not is_ok and len(password) == 32:
|
if not is_ok and len(password) == 32:
|
||||||
|
@ -68,8 +68,9 @@ def user_authentication():
|
|||||||
if (not flask_login.current_user.is_anonymous
|
if (not flask_login.current_user.is_anonymous
|
||||||
and flask_login.current_user.enabled):
|
and flask_login.current_user.enabled):
|
||||||
response = flask.Response()
|
response = flask.Response()
|
||||||
response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, flask_login.current_user.get_id(), "")
|
email = flask_login.current_user.get_id()
|
||||||
response.headers["X-User-Token"] = models.User.get_temp_token(flask_login.current_user.get_id())
|
response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, email, "")
|
||||||
|
response.headers["X-User-Token"] = utils.gen_temp_token(email, flask.session)
|
||||||
return response
|
return response
|
||||||
return flask.abort(403)
|
return flask.abort(403)
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import passlib.hash
|
|||||||
import passlib.registry
|
import passlib.registry
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import hmac
|
|
||||||
import smtplib
|
import smtplib
|
||||||
import idna
|
import idna
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
@ -645,15 +644,6 @@ in clear-text regardless of the presence of the cache.
|
|||||||
user = cls.query.get(email)
|
user = cls.query.get(email)
|
||||||
return user if (user and user.enabled and user.check_password(password)) else None
|
return user if (user and user.enabled and user.check_password(password)) else None
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_temp_token(cls, email):
|
|
||||||
user = cls.query.get(email)
|
|
||||||
return hmac.new(app.temp_token_key, bytearray("{}|{}".format(time.strftime('%Y%m%d'), email), 'utf-8'), 'sha256').hexdigest() if (user and user.enabled) else None
|
|
||||||
|
|
||||||
def verify_temp_token(self, token):
|
|
||||||
return hmac.compare_digest(self.get_temp_token(self.email), token)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Alias(Base, Email):
|
class Alias(Base, Email):
|
||||||
""" An alias is an email address that redirects to some destination.
|
""" An alias is an email address that redirects to some destination.
|
||||||
|
@ -121,7 +121,6 @@ proxy = PrefixMiddleware()
|
|||||||
# Data migrate
|
# Data migrate
|
||||||
migrate = flask_migrate.Migrate()
|
migrate = flask_migrate.Migrate()
|
||||||
|
|
||||||
|
|
||||||
# session store (inspired by https://github.com/mbr/flask-kvsession)
|
# session store (inspired by https://github.com/mbr/flask-kvsession)
|
||||||
class RedisStore:
|
class RedisStore:
|
||||||
""" Stores session data in a redis db. """
|
""" Stores session data in a redis db. """
|
||||||
@ -232,7 +231,8 @@ class MailuSession(CallbackDict, SessionMixin):
|
|||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
""" destroy session for security reasons. """
|
""" destroy session for security reasons. """
|
||||||
|
if 'webmail_token' in self:
|
||||||
|
self.app.session_store.delete(self['webmail_token'])
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
||||||
self._uid = None
|
self._uid = None
|
||||||
@ -273,6 +273,11 @@ class MailuSession(CallbackDict, SessionMixin):
|
|||||||
if self._sid is None:
|
if self._sid is None:
|
||||||
self._sid = self.app.session_config.gen_sid()
|
self._sid = self.app.session_config.gen_sid()
|
||||||
set_cookie = True
|
set_cookie = True
|
||||||
|
if 'webmail_token' in self:
|
||||||
|
app.session_store.put(self['webmail_token'],
|
||||||
|
self.sid,
|
||||||
|
int(app.config['PERMANENT_SESSION_LIFETIME']),
|
||||||
|
)
|
||||||
|
|
||||||
# get new session key
|
# get new session key
|
||||||
key = self.sid
|
key = self.sid
|
||||||
@ -477,3 +482,24 @@ class MailuSessionExtension:
|
|||||||
|
|
||||||
cleaned = Value('i', False)
|
cleaned = Value('i', False)
|
||||||
session = MailuSessionExtension()
|
session = MailuSessionExtension()
|
||||||
|
|
||||||
|
# this is used by the webmail to authenticate IMAP/SMTP
|
||||||
|
def verify_temp_token(email, token):
|
||||||
|
try:
|
||||||
|
if token.startswith('token-'):
|
||||||
|
sessid = app.session_store.get(token)
|
||||||
|
if sessid:
|
||||||
|
session = MailuSession(sessid, app)
|
||||||
|
if session.get('_user_id', '') == email:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def gen_temp_token(email, session):
|
||||||
|
token = session.get('webmail_token', 'token-'+secrets.token_urlsafe())
|
||||||
|
session['webmail_token'] = token
|
||||||
|
app.session_store.put(token,
|
||||||
|
session.sid,
|
||||||
|
int(app.config['PERMANENT_SESSION_LIFETIME']),
|
||||||
|
)
|
||||||
|
return token
|
||||||
|
Loading…
x
Reference in New Issue
Block a user