Repository: CTFd/CTFd

Issue:
Deleting User Crashes server.
**Environment**:

- CTFd Version/Commit: https://github.com/CTFd/CTFd/commit/514ab2c8bd3b0687615307c19fa9618b09e3998a
- Operating System: Ubuntu 20
- Web Browser and Version: Chrome

**What happened?**
User registered and logged into the website.  The admin then deleted the account and next time the user refreshed, it crashed the CTFd instance.  Upon restart, if the user refreshed the browser it crashed the server again.

**What did you expect to happen?**
The user to be logged out or not able to do anything and not crash the app.

**How to reproduce your issue**

**Any associated stack traces or error logs**
```
ctfd_1   | ERROR [CTFd] Exception on / [GET]
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
ctfd_1   |     response = self.full_dispatch_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
ctfd_1   |     rv = self.handle_user_exception(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 639, in error_router
ctfd_1   |     return original_handler(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
ctfd_1   |     reraise(exc_type, exc_value, tb)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
ctfd_1   |     raise value
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1948, in full_dispatch_request
ctfd_1   |     rv = self.preprocess_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2242, in preprocess_request
ctfd_1   |     rv = func()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/initialization/__init__.py", line 242, in banned
ctfd_1   |     team = get_current_team_attrs()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/user/__init__.py", line 98, in get_current_team_attrs
ctfd_1   |     if user.team_id:
ctfd_1   | AttributeError: 'NoneType' object has no attribute 'team_id'
ctfd_1   | [2021-09-24 21:44:07 +0000] [23] [ERROR] Error handling request /
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
ctfd_1   |     response = self.full_dispatch_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
ctfd_1   |     rv = self.handle_user_exception(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 639, in error_router
ctfd_1   |     return original_handler(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
ctfd_1   |     reraise(exc_type, exc_value, tb)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
ctfd_1   |     raise value
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1948, in full_dispatch_request
ctfd_1   |     rv = self.preprocess_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2242, in preprocess_request
ctfd_1   |     rv = func()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/initialization/__init__.py", line 242, in banned
ctfd_1   |     team = get_current_team_attrs()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/user/__init__.py", line 98, in get_current_team_attrs
ctfd_1   |     if user.team_id:
ctfd_1   | AttributeError: 'NoneType' object has no attribute 'team_id'
ctfd_1   | 
ctfd_1   | During handling of the above exception, another exception occurred:
ctfd_1   | 
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base_async.py", line 55, in handle
ctfd_1   |     self.handle_request(listener_name, req, client, addr)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/ggevent.py", line 143, in handle_request
ctfd_1   |     super().handle_request(listener_name, req, sock, addr)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base_async.py", line 106, in handle_request
ctfd_1   |     respiter = self.wsgi(environ, resp.start_response)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2464, in __call__
ctfd_1   |     return self.wsgi_app(environ, start_response)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/werkzeug/middleware/proxy_fix.py", line 169, in __call__
ctfd_1   |     return self.app(environ, start_response)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2450, in wsgi_app
ctfd_1   |     response = self.handle_exception(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 639, in error_router
ctfd_1   |     return original_handler(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1879, in handle_exception
ctfd_1   |     server_error = handler(server_error)
ctfd_1   |   File "/opt/CTFd/CTFd/errors.py", line 15, in render_error
ctfd_1   |     "errors/{}.html".format(error.code), error=error.description,
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/templating.py", line 140, in render_template
ctfd_1   |     ctx.app,
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/templating.py", line 120, in _render
ctfd_1   |     rv = template.render(context)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 1090, in render
nginx_1  | 192.168.1.189 - - [24/Sep/2021:21:44:07 +0000] "GET / HTTP/1.1" 500 141 "https://ctf.digital/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
ctfd_1   |     self.environment.handle_exception()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 832, in handle_exception
ctfd_1   |     reraise(*rewrite_traceback_stack(source=source))
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/_compat.py", line 28, in reraise
ctfd_1   |     raise value.with_traceback(tb)
ctfd_1   |   File "/opt/CTFd/CTFd/themes/CTFd-theme-pixo/templates/errors/500.html", line 1, in top-level template code
ctfd_1   |     {% extends "base.html" %}
ctfd_1   |   File "/opt/CTFd/CTFd/themes/CTFd-theme-pixo/templates/base.html", line 32, in top-level template code
ctfd_1   |     {% include "components/navbar.html" %}
ctfd_1   |   File "/opt/CTFd/CTFd/themes/CTFd-theme-pixo/templates/components/navbar.html", line 48, in top-level template code
ctfd_1   |     {% if is_admin() %}
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/sandbox.py", line 462, in call
ctfd_1   |     return __context.call(__obj, *args, **kwargs)
ctfd_1   |   File "/opt/CTFd/CTFd/utils/user/__init__.py", line 129, in is_admin
ctfd_1   |     return user.type == "admin"
ctfd_1   | AttributeError: 'NoneType' object has no attribute 'type'
ctfd_1   |  * Loaded module, <module 'CTFd.plugins.challenges' from '/opt/CTFd/CTFd/plugins/challenges/__init__.py'>
ctfd_1   |  * Loaded module, <module 'CTFd.plugins.ctfd-matrix-scoreboard-plugin' from '/opt/CTFd/CTFd/plugins/ctfd-matrix-scoreboard-plugin/__init__.py'>
ctfd_1   |  * Loaded module, <module 'CTFd.plugins.dynamic_challenges' from '/opt/CTFd/CTFd/plugins/dynamic_challenges/__init__.py'>
ctfd_1   |  * Loaded module, <module 'CTFd.plugins.flags' from '/opt/CTFd/CTFd/plugins/flags/__init__.py'>
ctfd_1   | 172.25.0.3 - - [24/Sep/2021:21:44:07 +0000] "GET / HTTP/1.0" 500 0 "-" "-"
ctfd_1   | ERROR [CTFd] Exception on / [GET]
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
ctfd_1   |     response = self.full_dispatch_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
ctfd_1   |     rv = self.handle_user_exception(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 639, in error_router
ctfd_1   |     return original_handler(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
ctfd_1   |     reraise(exc_type, exc_value, tb)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
ctfd_1   |     raise value
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1948, in full_dispatch_request
ctfd_1   |     rv = self.preprocess_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2242, in preprocess_request
ctfd_1   |     rv = func()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/initialization/__init__.py", line 242, in banned
ctfd_1   |     team = get_current_team_attrs()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/user/__init__.py", line 98, in get_current_team_attrs
ctfd_1   |     if user.team_id:
ctfd_1   | AttributeError: 'NoneType' object has no attribute 'team_id'
nginx_1  | 192.168.1.189 - - [24/Sep/2021:21:44:08 +0000] "GET / HTTP/1.1" 500 141 "https://ctf.digital/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"
ctfd_1   | [2021-09-24 21:44:08 +0000] [23] [ERROR] Error handling request /
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
ctfd_1   |     response = self.full_dispatch_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
ctfd_1   |     rv = self.handle_user_exception(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 639, in error_router
ctfd_1   |     return original_handler(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
ctfd_1   |     reraise(exc_type, exc_value, tb)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
ctfd_1   |     raise value
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1948, in full_dispatch_request
ctfd_1   |     rv = self.preprocess_request()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2242, in preprocess_request
ctfd_1   |     rv = func()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/initialization/__init__.py", line 242, in banned
ctfd_1   |     team = get_current_team_attrs()
ctfd_1   |   File "/opt/CTFd/CTFd/utils/user/__init__.py", line 98, in get_current_team_attrs
ctfd_1   |     if user.team_id:
ctfd_1   | AttributeError: 'NoneType' object has no attribute 'team_id'
ctfd_1   | 
ctfd_1   | During handling of the above exception, another exception occurred:
ctfd_1   | 
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base_async.py", line 55, in handle
ctfd_1   |     self.handle_request(listener_name, req, client, addr)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/ggevent.py", line 143, in handle_request
ctfd_1   |     super().handle_request(listener_name, req, sock, addr)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base_async.py", line 106, in handle_request
ctfd_1   |     respiter = self.wsgi(environ, resp.start_response)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2464, in __call__
ctfd_1   |     return self.wsgi_app(environ, start_response)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/werkzeug/middleware/proxy_fix.py", line 169, in __call__
ctfd_1   |     return self.app(environ, start_response)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2450, in wsgi_app
ctfd_1   |     response = self.handle_exception(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 639, in error_router
ctfd_1   |     return original_handler(e)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1879, in handle_exception
ctfd_1   |     server_error = handler(server_error)
ctfd_1   |   File "/opt/CTFd/CTFd/errors.py", line 15, in render_error
ctfd_1   |     "errors/{}.html".format(error.code), error=error.description,
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/templating.py", line 140, in render_template
ctfd_1   |     ctx.app,
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/flask/templating.py", line 120, in _render
ctfd_1   |     rv = template.render(context)
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 1090, in render
ctfd_1   |     self.environment.handle_exception()
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 832, in handle_exception
ctfd_1   |     reraise(*rewrite_traceback_stack(source=source))
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/_compat.py", line 28, in reraise
ctfd_1   |     raise value.with_traceback(tb)
ctfd_1   |   File "/opt/CTFd/CTFd/themes/CTFd-theme-pixo/templates/errors/500.html", line 1, in top-level template code
ctfd_1   |     {% extends "base.html" %}
ctfd_1   |   File "/opt/CTFd/CTFd/themes/CTFd-theme-pixo/templates/base.html", line 32, in top-level template code
ctfd_1   |     {% include "components/navbar.html" %}
ctfd_1   |   File "/opt/CTFd/CTFd/themes/CTFd-theme-pixo/templates/components/navbar.html", line 48, in top-level template code
ctfd_1   |     {% if is_admin() %}
ctfd_1   |   File "/usr/local/lib/python3.7/site-packages/jinja2/sandbox.py", line 462, in call
ctfd_1   |     return __context.call(__obj, *args, **kwargs)
ctfd_1   |   File "/opt/CTFd/CTFd/utils/user/__init__.py", line 129, in is_admin
ctfd_1   |     return user.type == "admin"
ctfd_1   | AttributeError: 'NoneType' object has no attribute 'type'
ctfd_1   | 172.25.0.3 - - [24/Sep/2021:21:44:08 +0000] "GET / HTTP/1.0" 500 0 "-" "-"
```


Discussion:

Resolving patch:
diff --git a/CTFd/cache/__init__.py b/CTFd/cache/__init__.py
--- a/CTFd/cache/__init__.py
+++ b/CTFd/cache/__init__.py
@@ -78,24 +78,44 @@ def clear_user_recent_ips(user_id):
 
 
 def clear_user_session(user_id):
-    from CTFd.utils.user import get_user_attrs
+    from CTFd.utils.user import (
+        get_user_attrs,
+        get_user_place,
+        get_user_score,
+        get_user_recent_ips,
+    )
 
     cache.delete_memoized(get_user_attrs, user_id=user_id)
+    cache.delete_memoized(get_user_place, user_id=user_id)
+    cache.delete_memoized(get_user_score, user_id=user_id)
+    cache.delete_memoized(get_user_recent_ips, user_id=user_id)
 
 
 def clear_all_user_sessions():
-    from CTFd.utils.user import get_user_attrs
+    from CTFd.utils.user import (
+        get_user_attrs,
+        get_user_place,
+        get_user_score,
+        get_user_recent_ips,
+    )
 
     cache.delete_memoized(get_user_attrs)
+    cache.delete_memoized(get_user_place)
+    cache.delete_memoized(get_user_score)
+    cache.delete_memoized(get_user_recent_ips)
 
 
 def clear_team_session(team_id):
-    from CTFd.utils.user import get_team_attrs
+    from CTFd.utils.user import get_team_attrs, get_team_place, get_team_score
 
     cache.delete_memoized(get_team_attrs, team_id=team_id)
+    cache.delete_memoized(get_team_place, team_id=team_id)
+    cache.delete_memoized(get_team_score, team_id=team_id)
 
 
 def clear_all_team_sessions():
-    from CTFd.utils.user import get_team_attrs
+    from CTFd.utils.user import get_team_attrs, get_team_place, get_team_score
 
     cache.delete_memoized(get_team_attrs)
+    cache.delete_memoized(get_team_place)
+    cache.delete_memoized(get_team_score)
diff --git a/CTFd/utils/user/__init__.py b/CTFd/utils/user/__init__.py
--- a/CTFd/utils/user/__init__.py
+++ b/CTFd/utils/user/__init__.py
@@ -95,7 +95,7 @@ def get_current_team():
 def get_current_team_attrs():
     if authed():
         user = get_user_attrs(user_id=session["id"])
-        if user.team_id:
+        if user and user.team_id:
             return get_team_attrs(team_id=user.team_id)
     return None
 


Test patch: