Repository: CTFd/CTFd

Issue:
override_template throwing a 500 error
**Environment**:

  - CTFd Version/Commit: 2.5.0
  - Operating System: Mac OS Mojave
  - Web Browser and Version: Google Chrome Version 83.0.4103.106

**What happened?**
Bug/issue using the "override_template" function, inside the __init.py__ file for a plugin, from CTFD.plugins methods. Upon configuring the new template it initially seems to work. A page route will render the new html template. Once you refresh the page the server throws a 500 error and errors appear in the stack trace. This not only happens when i tired implementing this myself but also when I tried using the ctfd-matrix-scoreboard-plugin  that also uses this method to change the original scoreboard html.

**What did you expect to happen?**
What i expected to happen is what happens upon when you first compose the container. It renders the new html file you have defined in your plugin. But upon reloading the page this is when it break with a 500.

**How to reproduce your issue**

Create a named folder within the plugins folder. I named mine cfc.  
In the __init__.py file i have the following code:

************************************** __init__.py ***************************************
from CTFd.plugins import override_template
import os


def load(app):
    dir_path = os.path.dirname(os.path.realpath(__file__))
    template_path = os.path.join(dir_path, 'usersplug.html')
    override_template('users/users.html', open(template_path).read())
******************************************************************************************

I then created an html file with the name usersplug.html also in the route of my cfc plugin folder. Inside this html file is just have an exact copy of the users.html from the core themes apart from i changed the h1 tag to make sure i can see its pulling the right file .... <h1>Users-plugin</h1>

When starting the container and upon initial load of the application it renders the new new file as is to be expected but then on reload i get a 500 error.  This sequence also happens when I use the 
ctfd-matrix-scoreboard-plugin  and also outputs the same stack trace as shown below. 
**Any associated stack traces or error logs**

   | ERROR [CTFd] Exception on /users [GET]
   | Traceback (most recent call last):
   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
   |     response = self.full_dispatch_request()
   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
   |     rv = self.handle_user_exception(e)
   |   File "/usr/local/lib/python3.7/site-packages/flask_restx/api.py", line 600, in error_router
   |     return original_handler(e)
   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
   |     reraise(exc_type, exc_value, tb)
   |   File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
   |     raise value
   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
   |     rv = self.dispatch_request()
   |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
   |     return self.view_functions[rule.endpoint](**req.view_args)
   |   File "/opt/CTFd/CTFd/utils/decorators/visibility.py", line 73, in _check_account_visibility
   |     return f(*args, **kwargs)
   |   File "/opt/CTFd/CTFd/users.py", line 31, in listing
   |     return render_template("users/users.html", users=users, pages=pages, curr_page=page)
   |   File "/usr/local/lib/python3.7/site-packages/flask/templating.py", line 138, in render_template
   |     ctx.app.jinja_env.get_or_select_template(template_name_or_list),
   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 930, in get_or_select_template
   |     return self.get_template(template_name_or_list, parent, globals)
   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 883, in get_template
   |     return self._load_template(name, self.make_globals(globals))
   |   File "/opt/CTFd/CTFd/__init__.py", line 94, in _load_template
   |     not self.auto_reload or template.is_up_to_date
   |   File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 1205, in is_up_to_date
   |     return self._uptodate()
   | TypeError: 'bool' object is not callable




Discussion:
the issue seems to be originated here at https://github.com/CTFd/CTFd/blob/aad3d215683b919e9e4accce6d2db0882fd487f7/CTFd/__init__.py#L110-L124

line 113, "True" should be callable, patching it into `lambda: True` works

code from jinja (FileSystemLoader):
```python
def get_source(self, environment, template):
    pieces = split_template_path(template)
    for searchpath in self.searchpath:
        ...
        mtime = path.getmtime(filename)
        def uptodate():
            try:
                return path.getmtime(filename) == mtime
            except OSError:
                return False
        return contents, filename, uptodate
    raise TemplateNotFound(template)
```

jinja docs:
https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.BaseLoader.get_source
why the hell does it work before???
@frankli0324  Thanks very much for your help!!. Now looking at the "get_source" function it does expect a call back function to be declared so your solution makes total sense!  And I can report back that this override method is now working as it is intended to with this fix. 

Your comment to how "why the hell does it work before???" I totally get you. If the code has always just defined a boolean value instead a callback i'm not sure how it was working for people before...

I'm not too sure if you are part of the CTFD dev team?. Can I leave it with you to create the PR to push the change into the upstream ?

Thank you!
I'm not with the CTFd devs, but sure, I'll open a PR.

Resolving patch:
diff --git a/CTFd/__init__.py b/CTFd/__init__.py
--- a/CTFd/__init__.py
+++ b/CTFd/__init__.py
@@ -110,7 +110,7 @@ def __init__(self, searchpath, encoding="utf-8", followlinks=False):
     def get_source(self, environment, template):
         # Check if the template has been overriden
         if template in self.overriden_templates:
-            return self.overriden_templates[template], template, True
+            return self.overriden_templates[template], template, lambda: True
 
         # Check if the template requested is for the admin panel
         if template.startswith("admin/"):


Test patch: