Repository: Mailu/Mailu

Issue:
Admin Error 500: 'MailuSessionConfig' object has no attribute 'app'
Thank you for opening an issue with Mailu. Please understand that issues are meant for bugs and enhancement-requests.
For **user-support questions**, reach out to us  on [matrix](https://matrix.to/#/#mailu:tedomum.net).

To be able to help you best, we need some more information.

## Before you open your issue
- [ ] Check if no issue or pull-request for this already exists.
- [ ] Check [documentation](https://mailu.io/master/) and [FAQ](https://mailu.io/master/faq.html). (Tip, use the search function on the documentation page)
- [X] You understand `Mailu` is made by volunteers in their **free time** — be conscise, civil and accept that delays can occur.
- [X] The title of the issue should be short and simple. It should contain specific terms related to the actual issue. Be specific while writing the title.

## Environment & Versions
### Environment
 - [X] docker-compose
 - [ ] kubernetes
 - [ ] docker swarm

### Versions
master

## Description
Admin and Webmail show 500 Internal Server Error.

The login page can be restored by deleting cookies, but nothing beyond the login page works.

## Replication Steps
Access `/admin` (and log in)

## Expected behaviour
Works normally.

## Logs

```
mailu-admin-1      | [2021-12-22 16:16:26,927] ERROR in app: Exception on /admin [GET]
mailu-admin-1      | Traceback (most recent call last):
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask/app.py", line 2072, in wsgi_app
mailu-admin-1      |     ctx.push()
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask/ctx.py", line 404, in push
mailu-admin-1      |     self.session = session_interface.open_session(self.app, self.request)
mailu-admin-1      |   File "/app/mailu/utils.py", line 379, in open_session
mailu-admin-1      |     return MailuSession(request.cookies.get(app.config['SESSION_COOKIE_NAME'], None), app)
mailu-admin-1      |   File "/app/mailu/utils.py", line 195, in __init__
mailu-admin-1      |     if parsed := self.app.session_config.parse_key(key, self.app):
mailu-admin-1      |   File "/app/mailu/utils.py", line 360, in parse_key
mailu-admin-1      |     if not created <= now <= created + self.app.config['PERMANENT_SESSION_LIFETIME']:
mailu-admin-1      | AttributeError: 'MailuSessionConfig' object has no attribute 'app'
mailu-admin-1      | [2021-12-22 16:16:26,927] ERROR in app: Request finalizing failed with an error while handling an error
mailu-admin-1      | Traceback (most recent call last):
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask/app.py", line 2072, in wsgi_app
mailu-admin-1      |     ctx.push()
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask/ctx.py", line 404, in push
mailu-admin-1      |     self.session = session_interface.open_session(self.app, self.request)
mailu-admin-1      |   File "/app/mailu/utils.py", line 379, in open_session
mailu-admin-1      |     return MailuSession(request.cookies.get(app.config['SESSION_COOKIE_NAME'], None), app)
mailu-admin-1      |   File "/app/mailu/utils.py", line 195, in __init__
mailu-admin-1      |     if parsed := self.app.session_config.parse_key(key, self.app):
mailu-admin-1      |   File "/app/mailu/utils.py", line 360, in parse_key
mailu-admin-1      |     if not created <= now <= created + self.app.config['PERMANENT_SESSION_LIFETIME']:
mailu-admin-1      | AttributeError: 'MailuSessionConfig' object has no attribute 'app'
mailu-admin-1      | 
mailu-admin-1      | During handling of the above exception, another exception occurred:
mailu-admin-1      | 
mailu-admin-1      | Traceback (most recent call last):
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask/app.py", line 1540, in finalize_request
mailu-admin-1      |     response = self.process_response(response)
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask/app.py", line 1885, in process_response
mailu-admin-1      |     response = self.ensure_sync(func)(response)
mailu-admin-1      |   File "/usr/lib/python3.9/site-packages/flask_login/login_manager.py", line 399, in _update_remember_cookie
mailu-admin-1      |     if '_remember' not in session and \
mailu-admin-1      | TypeError: argument of type 'NoneType' is not iterable
```



Discussion:

Resolving patch:
diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py
--- a/core/admin/mailu/utils.py
+++ b/core/admin/mailu/utils.py
@@ -300,7 +300,7 @@ class MailuSessionConfig:
     # default size of session key parts
     uid_bits = 64 # default if SESSION_KEY_BITS is not set in config
     sid_bits = 128 # for now. must be multiple of 8!
-    time_bits = 32 # for now. must be multiple of 8!
+    time_bits = 32 # for now. must be multiple of 8!  
 
     def __init__(self, app=None):
 
@@ -341,6 +341,9 @@ def gen_created(self, now=None):
     def parse_key(self, key, app=None, now=None):
         """ Split key into sid, uid and creation time. """
 
+        if app is None:
+            app = flask.current_app
+
         if not (isinstance(key, bytes) and self._key_min <= len(key) <= self._key_max):
             return None
 
@@ -357,7 +360,7 @@ def parse_key(self, key, app=None, now=None):
         if now is None:
             now = int(time.time())
         created = int.from_bytes(created, byteorder='big')
-        if not created <= now <= created + self.app.config['PERMANENT_SESSION_LIFETIME']:
+        if not created <= now <= created + app.config['PERMANENT_SESSION_LIFETIME']:
             return None
 
         return (uid, sid, crt)
@@ -422,8 +425,8 @@ def cleanup_sessions(app=None):
 
         count = 0
         for key in app.session_store.list():
-            if key.startswith('token-'):
-                if sessid := app.session_store.get(token):
+            if key.startswith(b'token-'):
+                if sessid := app.session_store.get(key):
                     if not app.session_config.parse_key(sessid, app, now=now):
                         app.session_store.delete(sessid)
                         app.session_store.delete(key)
@@ -451,7 +454,7 @@ def prune_sessions(uid=None, keep=None, app=None):
 
         count = 0
         for key in app.session_store.list(prefix):
-            if key not in keep and not key.startswith('token-'):
+            if key not in keep and not key.startswith(b'token-'):
                 app.session_store.delete(key)
                 count += 1
 


Test patch: