We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Can't add new accounts
We can't assign new accounts (for editing knowls) because that has to be done on beta, not www.

I think this just involves changing line 28 of lmfdb/users/main.py
from
base_url = "http://www.l-functions.org"
to
base_url = "http://beta.lmfdb.org"

But I didn't want to do that in case it has a bad side-effect.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lmfdb/users/main.py`
Content:
```
1 # -*- encoding: utf-8 -*-
2 # this holds all the flask-login specific logic (+ url mapping an rendering templates)
3 # for the user management
4 # author: harald schilly <harald.schilly@univie.ac.at>
5 
6 import pymongo
7 ASC = pymongo.ASCENDING
8 import flask
9 from functools import wraps
10 from lmfdb.base import app, getDBConnection
11 from flask import render_template, request, abort, Blueprint, url_for, make_response
12 from flask.ext.login import login_required, login_user, current_user, logout_user
13 
14 login_page = Blueprint("users", __name__, template_folder='templates')
15 import lmfdb.utils
16 logger = lmfdb.utils.make_logger(login_page)
17 
18 import re
19 allowed_usernames = re.compile("^[a-zA-Z0-9._-]+$")
20 
21 from flask.ext.login import LoginManager
22 login_manager = LoginManager()
23 
24 import pwdmanager
25 from pwdmanager import LmfdbUser, LmfdbAnonymousUser
26 
27 # TODO update this url, needed for the user login token
28 base_url = "http://www.l-functions.org"
29 # TODO: Not sure this should be changed from l-functions -> lmfdb, because
30 # I don't really understand how this is used. Paul
31 
32 
33 @login_manager.user_loader
34 def load_user(userid):
35     from pwdmanager import LmfdbUser
36     return LmfdbUser(userid)
37 
38 login_manager.login_view = "users.info"
39 
40 # this anonymous user has the is_admin() method
41 login_manager.anonymous_user = LmfdbAnonymousUser
42 
43 
44 def get_username(uid):
45     """returns the name of user @uid"""
46     return LmfdbUser(uid).name
47 
48 # globally define user properties and username
49 
50 
51 @app.context_processor
52 def ctx_proc_userdata():
53     userdata = {}
54     userdata['userid'] = 'anon' if current_user.is_anonymous() else current_user._uid
55     userdata['username'] = 'Anonymous' if current_user.is_anonymous() else current_user.name
56     userdata['user_is_authenticated'] = current_user.is_authenticated()
57     userdata['user_is_admin'] = current_user.is_admin()
58     userdata['get_username'] = get_username # this is a function
59     return userdata
60 
61 # blueprint specific definition of the body_class variable
62 
63 
64 @login_page.context_processor
65 def body_class():
66     return {'body_class': 'login'}
67 
68 # the following doesn't work as it should, also depends on blinker python lib
69 # flask signal when a user logs in. we record the last logins in the user's data
70 # http://flask.pocoo.org/docs/signals/
71 # def log_login_callback(cur_app, user = None):
72 #  cur_user = user or current_user
73 #  logger.info(">> curr_app: %s   user: %s" % (cur_app, cur_user))
74 #
75 # from flask.ext.login import user_logged_in, user_login_confirmed
76 # user_logged_in.connect(log_login_callback)
77 # user_login_confirmed.connect(log_login_callback)
78 
79 
80 def base_bread():
81     return [('Users', url_for(".list"))]
82 
83 
84 @login_page.route("/")
85 @login_required
86 def list():
87     import pwdmanager
88     users = pwdmanager.get_user_list()
89     # trying to be smart and sorting by last name
90     users = sorted(users, key=lambda x: x[1].split(" ")[-1].lower())
91     bread = base_bread()
92     return render_template("user-list.html", title="All Users",
93                            users=users, bread=bread)
94 
95 
96 @login_page.route("/myself")
97 def info():
98     info = {}
99     info['login'] = url_for(".login")
100     info['logout'] = url_for(".logout")
101     info['user'] = current_user
102     info['next'] = request.referrer
103     return render_template("user-info.html",
104                            info=info, title="Userinfo",
105                            bread=base_bread() + [("Myself", url_for(".info"))])
106 
107 # ./info again, but for POST!
108 
109 
110 @login_page.route("/info", methods=['POST'])
111 @login_required
112 def set_info():
113     for k, v in request.form.iteritems():
114         setattr(current_user, k, v)
115     current_user.save()
116     flask.flash("Thank you for updating your details!")
117     return flask.redirect(url_for(".info"))
118 
119 
120 @login_page.route("/profile/<userid>")
121 @login_required
122 def profile(userid):
123     # See issue #1169
124     #try:
125     #    getDBConnection().knowledge.knowls.ensure_index('title')
126     #except pymongo.errors.OperationFailure:
127     #    pass
128     user = LmfdbUser(userid)
129     bread = base_bread() + [(user.name, url_for('.profile', userid=user.get_id()))]
130     userknowls = getDBConnection(
131     ).knowledge.knowls.find({'authors': userid}, ['title']).sort([('title', ASC)])
132     userfiles = getDBConnection(
133     ).upload.fs.files.find({'metadata.uploader_id': userid, 'metadata.status': 'approved'})
134     userfilesmod = getDBConnection(
135     ).upload.fs.files.find({'metadata.uploader_id': userid, 'metadata.status': 'unmoderated'})
136     return render_template("user-detail.html", user=user,
137                            title="%s" % user.name, bread=bread, userknowls=userknowls, userfiles=userfiles, userfilesmod=userfilesmod)
138 
139 
140 @login_page.route("/login", methods=["POST"])
141 def login(**kwargs):
142     bread = base_bread() + [('Login', url_for('.login'))]
143     # login and validate the user …
144     # remember = True sets a cookie to remmeber the user
145     name = request.form["name"]
146     password = request.form["password"]
147     next = request.form["next"]
148     remember = True if request.form["remember"] == "on" else False
149     user = LmfdbUser(name)
150     if user and user.authenticate(password):
151         login_user(user, remember=remember)
152         flask.flash("Hello %s, your login was successful!" % user.name)
153         logger.info("login: '%s' - '%s'" % (user.get_id(), user.name))
154         return flask.redirect(next or url_for(".info"))
155     flask.flash("Oops! Wrong username or password.", "error")
156     return flask.redirect(url_for(".info"))
157 
158 
159 def admin_required(fn):
160     """
161     wrap this around those entry points where you need to be an admin.
162     """
163     @wraps(fn)
164     @login_required
165     def decorated_view(*args, **kwargs):
166         logger.info("admin access attempt by %s" % current_user.get_id())
167         if not current_user.is_admin():
168             return flask.abort(403)  # access denied
169         return fn(*args, **kwargs)
170     return decorated_view
171 
172 
173 def housekeeping(fn):
174     """
175     wrap this around maintenance calls, they are only accessible for
176     admins and for localhost
177     """
178     @wraps(fn)
179     def decorated_view(*args, **kwargs):
180         logger.info("housekeeping access attempt by %s" % request.remote_addr)
181         if request.remote_addr in ["127.0.0.1", "localhost"]:
182             return fn(*args, **kwargs)
183         return admin_required(fn)(*args, **kwargs)
184     return decorated_view
185 
186 
187 def get_user_token_coll():
188     return getDBConnection().userdb.tokens
189 
190 
191 @login_page.route("/register")
192 def register_new():
193     return ""
194     # q_admins = getDBConnection().userdb.users.find({'admin' : True})
195     # admins =', '.join((_['full_name'] or _['_id'] for _ in q_admins))
196     # return "You have to contact one of the Admins: %s" % admins
197 
198 
199 @login_page.route("/register/new")
200 @login_page.route("/register/new/<int:N>")
201 @admin_required
202 def register(N=10):
203     N = 100 if N > 100 else N
204     from datetime import datetime, timedelta
205     now = datetime.utcnow()
206     tdelta = timedelta(days=1)
207     exp = now + tdelta
208     import random
209     tokens = [str(random.randrange(1e20, 1e21)) for _ in range(N)]
210     for t in tokens:
211         get_user_token_coll().save({'_id': t, 'expire': exp})
212     urls = ["%s%s" % (base_url, url_for(".register_token", token=_)) for _ in tokens]
213     resp = make_response('\n'.join(urls))
214     resp.headers['Content-type'] = 'text/plain'
215     return resp
216 
217 
218 def delete_old_tokens():
219     from datetime import datetime, timedelta
220     now = datetime.utcnow()
221     tdelta = timedelta(days=8)
222     exp = now + tdelta
223     get_user_token_coll().remove({'expire': {'$gt': exp}})
224 
225 
226 @login_page.route("/register/<token>", methods=['GET', 'POST'])
227 def register_token(token):
228     delete_old_tokens()
229     token_exists = get_user_token_coll().find({'_id': token}).count() == 1
230     if not token_exists:
231         flask.abort(401)
232     bread = base_bread() + [('Register', url_for(".register_new"))]
233     if request.method == "GET":
234         return render_template("register.html", title="Register", bread=bread, next=request.referrer or "/", token=token)
235     elif request.method == 'POST':
236         name = request.form['name']
237         if not allowed_usernames.match(name):
238             flask.flash("""Oops, usename '%s' is not allowed.
239                   It must consist of lower/uppercase characters,
240                   no spaces, numbers or '.', '_' and '-'.""" % name, "error")
241             return flask.redirect(url_for(".register_new"))
242 
243         pw1 = request.form['password1']
244         pw2 = request.form['password2']
245         if pw1 != pw2:
246             flask.flash("Oops, passwords do not match!", "error")
247             return flask.redirect(url_for(".register_new"))
248 
249         if len(pw1) <= 3:
250             flask.flash("Oops, password too short. Minimum 4 characters please!", "error")
251             return flask.redirect(url_for(".register_new"))
252 
253         full_name = request.form['full_name']
254         email = request.form['email']
255         next = request.form["next"]
256 
257         if pwdmanager.user_exists(name):
258             flask.flash("Sorry, user ID '%s' already exists!" % name, "error")
259             return flask.redirect(url_for(".register_new"))
260 
261         newuser = pwdmanager.new_user(name, pw1)
262         newuser.full_name = full_name
263         newuser.email = email
264         newuser.save()
265         login_user(newuser, remember=True)
266         flask.flash("Hello %s! Congratulations, you are a new user!" % newuser.name)
267         get_user_token_coll().remove({'_id': token})
268         logger.debug("removed login token '%s'" % token)
269         logger.info("new user: '%s' - '%s'" % (newuser.get_id(), newuser.name))
270         return flask.redirect(next or url_for(".info"))
271 
272 
273 @login_page.route("/change_password", methods=['POST'])
274 @login_required
275 def change_password():
276     uid = current_user.get_id()
277     pw_old = request.form['oldpwd']
278     if not current_user.authenticate(pw_old):
279         flask.flash("Ooops, old password is wrong!", "error")
280         return flask.redirect(url_for(".info"))
281 
282     pw1 = request.form['password1']
283     pw2 = request.form['password2']
284     if pw1 != pw2:
285         flask.flash("Oops, new passwords do not match!", "error")
286         return flask.redirect(url_for(".info"))
287 
288     pwdmanager.change_password(uid, pw1)
289     flask.flash("Your password has been changed.")
290     return flask.redirect(url_for(".info"))
291 
292 
293 @login_page.route("/logout")
294 @login_required
295 def logout():
296     bread = base_bread() + [('Login', url_for('.logout'))]
297     logout_user()
298     flask.flash("You are logged out now. Have a nice day!")
299     return flask.redirect(request.args.get("next") or request.referrer or url_for('.info'))
300 
301 
302 @login_page.route("/admin")
303 @login_required
304 @admin_required
305 def admin():
306     return "success: only admins can read this!"
307
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lmfdb/users/main.py b/lmfdb/users/main.py
--- a/lmfdb/users/main.py
+++ b/lmfdb/users/main.py
@@ -24,11 +24,7 @@
 import pwdmanager
 from pwdmanager import LmfdbUser, LmfdbAnonymousUser
 
-# TODO update this url, needed for the user login token
-base_url = "http://www.l-functions.org"
-# TODO: Not sure this should be changed from l-functions -> lmfdb, because
-# I don't really understand how this is used. Paul
-
+base_url = "http://beta.lmfdb.org"
 
 @login_manager.user_loader
 def load_user(userid):

{"golden_diff": "diff --git a/lmfdb/users/main.py b/lmfdb/users/main.py\n--- a/lmfdb/users/main.py\n+++ b/lmfdb/users/main.py\n@@ -24,11 +24,7 @@\n import pwdmanager\n from pwdmanager import LmfdbUser, LmfdbAnonymousUser\n \n-# TODO update this url, needed for the user login token\n-base_url = \"http://www.l-functions.org\"\n-# TODO: Not sure this should be changed from l-functions -> lmfdb, because\n-# I don't really understand how this is used. Paul\n-\n+base_url = \"http://beta.lmfdb.org\"\n \n @login_manager.user_loader\n def load_user(userid):\n", "issue": "Can't add new accounts\nWe can't assign new accounts (for editing knowls) because that has to be done on beta, not www.\n\nI think this just involves changing line 28 of lmfdb/users/main.py\nfrom\nbase_url = \"http://www.l-functions.org\"\nto\nbase_url = \"http://beta.lmfdb.org\"\n\nBut I didn't want to do that in case it has a bad side-effect.\n\n", "before_files": [{"content": "# -*- encoding: utf-8 -*-\n# this holds all the flask-login specific logic (+ url mapping an rendering templates)\n# for the user management\n# author: harald schilly <harald.schilly@univie.ac.at>\n\nimport pymongo\nASC = pymongo.ASCENDING\nimport flask\nfrom functools import wraps\nfrom lmfdb.base import app, getDBConnection\nfrom flask import render_template, request, abort, Blueprint, url_for, make_response\nfrom flask.ext.login import login_required, login_user, current_user, logout_user\n\nlogin_page = Blueprint(\"users\", __name__, template_folder='templates')\nimport lmfdb.utils\nlogger = lmfdb.utils.make_logger(login_page)\n\nimport re\nallowed_usernames = re.compile(\"^[a-zA-Z0-9._-]+$\")\n\nfrom flask.ext.login import LoginManager\nlogin_manager = LoginManager()\n\nimport pwdmanager\nfrom pwdmanager import LmfdbUser, LmfdbAnonymousUser\n\n# TODO update this url, needed for the user login token\nbase_url = \"http://www.l-functions.org\"\n# TODO: Not sure this should be changed from l-functions -> lmfdb, because\n# I don't really understand how this is used. Paul\n\n\n@login_manager.user_loader\ndef load_user(userid):\n    from pwdmanager import LmfdbUser\n    return LmfdbUser(userid)\n\nlogin_manager.login_view = \"users.info\"\n\n# this anonymous user has the is_admin() method\nlogin_manager.anonymous_user = LmfdbAnonymousUser\n\n\ndef get_username(uid):\n    \"\"\"returns the name of user @uid\"\"\"\n    return LmfdbUser(uid).name\n\n# globally define user properties and username\n\n\n@app.context_processor\ndef ctx_proc_userdata():\n    userdata = {}\n    userdata['userid'] = 'anon' if current_user.is_anonymous() else current_user._uid\n    userdata['username'] = 'Anonymous' if current_user.is_anonymous() else current_user.name\n    userdata['user_is_authenticated'] = current_user.is_authenticated()\n    userdata['user_is_admin'] = current_user.is_admin()\n    userdata['get_username'] = get_username # this is a function\n    return userdata\n\n# blueprint specific definition of the body_class variable\n\n\n@login_page.context_processor\ndef body_class():\n    return {'body_class': 'login'}\n\n# the following doesn't work as it should, also depends on blinker python lib\n# flask signal when a user logs in. we record the last logins in the user's data\n# http://flask.pocoo.org/docs/signals/\n# def log_login_callback(cur_app, user = None):\n#  cur_user = user or current_user\n#  logger.info(\">> curr_app: %s   user: %s\" % (cur_app, cur_user))\n#\n# from flask.ext.login import user_logged_in, user_login_confirmed\n# user_logged_in.connect(log_login_callback)\n# user_login_confirmed.connect(log_login_callback)\n\n\ndef base_bread():\n    return [('Users', url_for(\".list\"))]\n\n\n@login_page.route(\"/\")\n@login_required\ndef list():\n    import pwdmanager\n    users = pwdmanager.get_user_list()\n    # trying to be smart and sorting by last name\n    users = sorted(users, key=lambda x: x[1].split(\" \")[-1].lower())\n    bread = base_bread()\n    return render_template(\"user-list.html\", title=\"All Users\",\n                           users=users, bread=bread)\n\n\n@login_page.route(\"/myself\")\ndef info():\n    info = {}\n    info['login'] = url_for(\".login\")\n    info['logout'] = url_for(\".logout\")\n    info['user'] = current_user\n    info['next'] = request.referrer\n    return render_template(\"user-info.html\",\n                           info=info, title=\"Userinfo\",\n                           bread=base_bread() + [(\"Myself\", url_for(\".info\"))])\n\n# ./info again, but for POST!\n\n\n@login_page.route(\"/info\", methods=['POST'])\n@login_required\ndef set_info():\n    for k, v in request.form.iteritems():\n        setattr(current_user, k, v)\n    current_user.save()\n    flask.flash(\"Thank you for updating your details!\")\n    return flask.redirect(url_for(\".info\"))\n\n\n@login_page.route(\"/profile/<userid>\")\n@login_required\ndef profile(userid):\n    # See issue #1169\n    #try:\n    #    getDBConnection().knowledge.knowls.ensure_index('title')\n    #except pymongo.errors.OperationFailure:\n    #    pass\n    user = LmfdbUser(userid)\n    bread = base_bread() + [(user.name, url_for('.profile', userid=user.get_id()))]\n    userknowls = getDBConnection(\n    ).knowledge.knowls.find({'authors': userid}, ['title']).sort([('title', ASC)])\n    userfiles = getDBConnection(\n    ).upload.fs.files.find({'metadata.uploader_id': userid, 'metadata.status': 'approved'})\n    userfilesmod = getDBConnection(\n    ).upload.fs.files.find({'metadata.uploader_id': userid, 'metadata.status': 'unmoderated'})\n    return render_template(\"user-detail.html\", user=user,\n                           title=\"%s\" % user.name, bread=bread, userknowls=userknowls, userfiles=userfiles, userfilesmod=userfilesmod)\n\n\n@login_page.route(\"/login\", methods=[\"POST\"])\ndef login(**kwargs):\n    bread = base_bread() + [('Login', url_for('.login'))]\n    # login and validate the user \u2026\n    # remember = True sets a cookie to remmeber the user\n    name = request.form[\"name\"]\n    password = request.form[\"password\"]\n    next = request.form[\"next\"]\n    remember = True if request.form[\"remember\"] == \"on\" else False\n    user = LmfdbUser(name)\n    if user and user.authenticate(password):\n        login_user(user, remember=remember)\n        flask.flash(\"Hello %s, your login was successful!\" % user.name)\n        logger.info(\"login: '%s' - '%s'\" % (user.get_id(), user.name))\n        return flask.redirect(next or url_for(\".info\"))\n    flask.flash(\"Oops! Wrong username or password.\", \"error\")\n    return flask.redirect(url_for(\".info\"))\n\n\ndef admin_required(fn):\n    \"\"\"\n    wrap this around those entry points where you need to be an admin.\n    \"\"\"\n    @wraps(fn)\n    @login_required\n    def decorated_view(*args, **kwargs):\n        logger.info(\"admin access attempt by %s\" % current_user.get_id())\n        if not current_user.is_admin():\n            return flask.abort(403)  # access denied\n        return fn(*args, **kwargs)\n    return decorated_view\n\n\ndef housekeeping(fn):\n    \"\"\"\n    wrap this around maintenance calls, they are only accessible for\n    admins and for localhost\n    \"\"\"\n    @wraps(fn)\n    def decorated_view(*args, **kwargs):\n        logger.info(\"housekeeping access attempt by %s\" % request.remote_addr)\n        if request.remote_addr in [\"127.0.0.1\", \"localhost\"]:\n            return fn(*args, **kwargs)\n        return admin_required(fn)(*args, **kwargs)\n    return decorated_view\n\n\ndef get_user_token_coll():\n    return getDBConnection().userdb.tokens\n\n\n@login_page.route(\"/register\")\ndef register_new():\n    return \"\"\n    # q_admins = getDBConnection().userdb.users.find({'admin' : True})\n    # admins =', '.join((_['full_name'] or _['_id'] for _ in q_admins))\n    # return \"You have to contact one of the Admins: %s\" % admins\n\n\n@login_page.route(\"/register/new\")\n@login_page.route(\"/register/new/<int:N>\")\n@admin_required\ndef register(N=10):\n    N = 100 if N > 100 else N\n    from datetime import datetime, timedelta\n    now = datetime.utcnow()\n    tdelta = timedelta(days=1)\n    exp = now + tdelta\n    import random\n    tokens = [str(random.randrange(1e20, 1e21)) for _ in range(N)]\n    for t in tokens:\n        get_user_token_coll().save({'_id': t, 'expire': exp})\n    urls = [\"%s%s\" % (base_url, url_for(\".register_token\", token=_)) for _ in tokens]\n    resp = make_response('\\n'.join(urls))\n    resp.headers['Content-type'] = 'text/plain'\n    return resp\n\n\ndef delete_old_tokens():\n    from datetime import datetime, timedelta\n    now = datetime.utcnow()\n    tdelta = timedelta(days=8)\n    exp = now + tdelta\n    get_user_token_coll().remove({'expire': {'$gt': exp}})\n\n\n@login_page.route(\"/register/<token>\", methods=['GET', 'POST'])\ndef register_token(token):\n    delete_old_tokens()\n    token_exists = get_user_token_coll().find({'_id': token}).count() == 1\n    if not token_exists:\n        flask.abort(401)\n    bread = base_bread() + [('Register', url_for(\".register_new\"))]\n    if request.method == \"GET\":\n        return render_template(\"register.html\", title=\"Register\", bread=bread, next=request.referrer or \"/\", token=token)\n    elif request.method == 'POST':\n        name = request.form['name']\n        if not allowed_usernames.match(name):\n            flask.flash(\"\"\"Oops, usename '%s' is not allowed.\n                  It must consist of lower/uppercase characters,\n                  no spaces, numbers or '.', '_' and '-'.\"\"\" % name, \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        pw1 = request.form['password1']\n        pw2 = request.form['password2']\n        if pw1 != pw2:\n            flask.flash(\"Oops, passwords do not match!\", \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        if len(pw1) <= 3:\n            flask.flash(\"Oops, password too short. Minimum 4 characters please!\", \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        full_name = request.form['full_name']\n        email = request.form['email']\n        next = request.form[\"next\"]\n\n        if pwdmanager.user_exists(name):\n            flask.flash(\"Sorry, user ID '%s' already exists!\" % name, \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        newuser = pwdmanager.new_user(name, pw1)\n        newuser.full_name = full_name\n        newuser.email = email\n        newuser.save()\n        login_user(newuser, remember=True)\n        flask.flash(\"Hello %s! Congratulations, you are a new user!\" % newuser.name)\n        get_user_token_coll().remove({'_id': token})\n        logger.debug(\"removed login token '%s'\" % token)\n        logger.info(\"new user: '%s' - '%s'\" % (newuser.get_id(), newuser.name))\n        return flask.redirect(next or url_for(\".info\"))\n\n\n@login_page.route(\"/change_password\", methods=['POST'])\n@login_required\ndef change_password():\n    uid = current_user.get_id()\n    pw_old = request.form['oldpwd']\n    if not current_user.authenticate(pw_old):\n        flask.flash(\"Ooops, old password is wrong!\", \"error\")\n        return flask.redirect(url_for(\".info\"))\n\n    pw1 = request.form['password1']\n    pw2 = request.form['password2']\n    if pw1 != pw2:\n        flask.flash(\"Oops, new passwords do not match!\", \"error\")\n        return flask.redirect(url_for(\".info\"))\n\n    pwdmanager.change_password(uid, pw1)\n    flask.flash(\"Your password has been changed.\")\n    return flask.redirect(url_for(\".info\"))\n\n\n@login_page.route(\"/logout\")\n@login_required\ndef logout():\n    bread = base_bread() + [('Login', url_for('.logout'))]\n    logout_user()\n    flask.flash(\"You are logged out now. Have a nice day!\")\n    return flask.redirect(request.args.get(\"next\") or request.referrer or url_for('.info'))\n\n\n@login_page.route(\"/admin\")\n@login_required\n@admin_required\ndef admin():\n    return \"success: only admins can read this!\"\n", "path": "lmfdb/users/main.py"}], "after_files": [{"content": "# -*- encoding: utf-8 -*-\n# this holds all the flask-login specific logic (+ url mapping an rendering templates)\n# for the user management\n# author: harald schilly <harald.schilly@univie.ac.at>\n\nimport pymongo\nASC = pymongo.ASCENDING\nimport flask\nfrom functools import wraps\nfrom lmfdb.base import app, getDBConnection\nfrom flask import render_template, request, abort, Blueprint, url_for, make_response\nfrom flask.ext.login import login_required, login_user, current_user, logout_user\n\nlogin_page = Blueprint(\"users\", __name__, template_folder='templates')\nimport lmfdb.utils\nlogger = lmfdb.utils.make_logger(login_page)\n\nimport re\nallowed_usernames = re.compile(\"^[a-zA-Z0-9._-]+$\")\n\nfrom flask.ext.login import LoginManager\nlogin_manager = LoginManager()\n\nimport pwdmanager\nfrom pwdmanager import LmfdbUser, LmfdbAnonymousUser\n\nbase_url = \"http://beta.lmfdb.org\"\n\n@login_manager.user_loader\ndef load_user(userid):\n    from pwdmanager import LmfdbUser\n    return LmfdbUser(userid)\n\nlogin_manager.login_view = \"users.info\"\n\n# this anonymous user has the is_admin() method\nlogin_manager.anonymous_user = LmfdbAnonymousUser\n\n\ndef get_username(uid):\n    \"\"\"returns the name of user @uid\"\"\"\n    return LmfdbUser(uid).name\n\n# globally define user properties and username\n\n\n@app.context_processor\ndef ctx_proc_userdata():\n    userdata = {}\n    userdata['userid'] = 'anon' if current_user.is_anonymous() else current_user._uid\n    userdata['username'] = 'Anonymous' if current_user.is_anonymous() else current_user.name\n    userdata['user_is_authenticated'] = current_user.is_authenticated()\n    userdata['user_is_admin'] = current_user.is_admin()\n    userdata['get_username'] = get_username # this is a function\n    return userdata\n\n# blueprint specific definition of the body_class variable\n\n\n@login_page.context_processor\ndef body_class():\n    return {'body_class': 'login'}\n\n# the following doesn't work as it should, also depends on blinker python lib\n# flask signal when a user logs in. we record the last logins in the user's data\n# http://flask.pocoo.org/docs/signals/\n# def log_login_callback(cur_app, user = None):\n#  cur_user = user or current_user\n#  logger.info(\">> curr_app: %s   user: %s\" % (cur_app, cur_user))\n#\n# from flask.ext.login import user_logged_in, user_login_confirmed\n# user_logged_in.connect(log_login_callback)\n# user_login_confirmed.connect(log_login_callback)\n\n\ndef base_bread():\n    return [('Users', url_for(\".list\"))]\n\n\n@login_page.route(\"/\")\n@login_required\ndef list():\n    import pwdmanager\n    users = pwdmanager.get_user_list()\n    # trying to be smart and sorting by last name\n    users = sorted(users, key=lambda x: x[1].split(\" \")[-1].lower())\n    bread = base_bread()\n    return render_template(\"user-list.html\", title=\"All Users\",\n                           users=users, bread=bread)\n\n\n@login_page.route(\"/myself\")\ndef info():\n    info = {}\n    info['login'] = url_for(\".login\")\n    info['logout'] = url_for(\".logout\")\n    info['user'] = current_user\n    info['next'] = request.referrer\n    return render_template(\"user-info.html\",\n                           info=info, title=\"Userinfo\",\n                           bread=base_bread() + [(\"Myself\", url_for(\".info\"))])\n\n# ./info again, but for POST!\n\n\n@login_page.route(\"/info\", methods=['POST'])\n@login_required\ndef set_info():\n    for k, v in request.form.iteritems():\n        setattr(current_user, k, v)\n    current_user.save()\n    flask.flash(\"Thank you for updating your details!\")\n    return flask.redirect(url_for(\".info\"))\n\n\n@login_page.route(\"/profile/<userid>\")\n@login_required\ndef profile(userid):\n    # See issue #1169\n    #try:\n    #    getDBConnection().knowledge.knowls.ensure_index('title')\n    #except pymongo.errors.OperationFailure:\n    #    pass\n    user = LmfdbUser(userid)\n    bread = base_bread() + [(user.name, url_for('.profile', userid=user.get_id()))]\n    userknowls = getDBConnection(\n    ).knowledge.knowls.find({'authors': userid}, ['title']).sort([('title', ASC)])\n    userfiles = getDBConnection(\n    ).upload.fs.files.find({'metadata.uploader_id': userid, 'metadata.status': 'approved'})\n    userfilesmod = getDBConnection(\n    ).upload.fs.files.find({'metadata.uploader_id': userid, 'metadata.status': 'unmoderated'})\n    return render_template(\"user-detail.html\", user=user,\n                           title=\"%s\" % user.name, bread=bread, userknowls=userknowls, userfiles=userfiles, userfilesmod=userfilesmod)\n\n\n@login_page.route(\"/login\", methods=[\"POST\"])\ndef login(**kwargs):\n    bread = base_bread() + [('Login', url_for('.login'))]\n    # login and validate the user \u2026\n    # remember = True sets a cookie to remmeber the user\n    name = request.form[\"name\"]\n    password = request.form[\"password\"]\n    next = request.form[\"next\"]\n    remember = True if request.form[\"remember\"] == \"on\" else False\n    user = LmfdbUser(name)\n    if user and user.authenticate(password):\n        login_user(user, remember=remember)\n        flask.flash(\"Hello %s, your login was successful!\" % user.name)\n        logger.info(\"login: '%s' - '%s'\" % (user.get_id(), user.name))\n        return flask.redirect(next or url_for(\".info\"))\n    flask.flash(\"Oops! Wrong username or password.\", \"error\")\n    return flask.redirect(url_for(\".info\"))\n\n\ndef admin_required(fn):\n    \"\"\"\n    wrap this around those entry points where you need to be an admin.\n    \"\"\"\n    @wraps(fn)\n    @login_required\n    def decorated_view(*args, **kwargs):\n        logger.info(\"admin access attempt by %s\" % current_user.get_id())\n        if not current_user.is_admin():\n            return flask.abort(403)  # access denied\n        return fn(*args, **kwargs)\n    return decorated_view\n\n\ndef housekeeping(fn):\n    \"\"\"\n    wrap this around maintenance calls, they are only accessible for\n    admins and for localhost\n    \"\"\"\n    @wraps(fn)\n    def decorated_view(*args, **kwargs):\n        logger.info(\"housekeeping access attempt by %s\" % request.remote_addr)\n        if request.remote_addr in [\"127.0.0.1\", \"localhost\"]:\n            return fn(*args, **kwargs)\n        return admin_required(fn)(*args, **kwargs)\n    return decorated_view\n\n\ndef get_user_token_coll():\n    return getDBConnection().userdb.tokens\n\n\n@login_page.route(\"/register\")\ndef register_new():\n    return \"\"\n    # q_admins = getDBConnection().userdb.users.find({'admin' : True})\n    # admins =', '.join((_['full_name'] or _['_id'] for _ in q_admins))\n    # return \"You have to contact one of the Admins: %s\" % admins\n\n\n@login_page.route(\"/register/new\")\n@login_page.route(\"/register/new/<int:N>\")\n@admin_required\ndef register(N=10):\n    N = 100 if N > 100 else N\n    from datetime import datetime, timedelta\n    now = datetime.utcnow()\n    tdelta = timedelta(days=1)\n    exp = now + tdelta\n    import random\n    tokens = [str(random.randrange(1e20, 1e21)) for _ in range(N)]\n    for t in tokens:\n        get_user_token_coll().save({'_id': t, 'expire': exp})\n    urls = [\"%s%s\" % (base_url, url_for(\".register_token\", token=_)) for _ in tokens]\n    resp = make_response('\\n'.join(urls))\n    resp.headers['Content-type'] = 'text/plain'\n    return resp\n\n\ndef delete_old_tokens():\n    from datetime import datetime, timedelta\n    now = datetime.utcnow()\n    tdelta = timedelta(days=8)\n    exp = now + tdelta\n    get_user_token_coll().remove({'expire': {'$gt': exp}})\n\n\n@login_page.route(\"/register/<token>\", methods=['GET', 'POST'])\ndef register_token(token):\n    delete_old_tokens()\n    token_exists = get_user_token_coll().find({'_id': token}).count() == 1\n    if not token_exists:\n        flask.abort(401)\n    bread = base_bread() + [('Register', url_for(\".register_new\"))]\n    if request.method == \"GET\":\n        return render_template(\"register.html\", title=\"Register\", bread=bread, next=request.referrer or \"/\", token=token)\n    elif request.method == 'POST':\n        name = request.form['name']\n        if not allowed_usernames.match(name):\n            flask.flash(\"\"\"Oops, usename '%s' is not allowed.\n                  It must consist of lower/uppercase characters,\n                  no spaces, numbers or '.', '_' and '-'.\"\"\" % name, \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        pw1 = request.form['password1']\n        pw2 = request.form['password2']\n        if pw1 != pw2:\n            flask.flash(\"Oops, passwords do not match!\", \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        if len(pw1) <= 3:\n            flask.flash(\"Oops, password too short. Minimum 4 characters please!\", \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        full_name = request.form['full_name']\n        email = request.form['email']\n        next = request.form[\"next\"]\n\n        if pwdmanager.user_exists(name):\n            flask.flash(\"Sorry, user ID '%s' already exists!\" % name, \"error\")\n            return flask.redirect(url_for(\".register_new\"))\n\n        newuser = pwdmanager.new_user(name, pw1)\n        newuser.full_name = full_name\n        newuser.email = email\n        newuser.save()\n        login_user(newuser, remember=True)\n        flask.flash(\"Hello %s! Congratulations, you are a new user!\" % newuser.name)\n        get_user_token_coll().remove({'_id': token})\n        logger.debug(\"removed login token '%s'\" % token)\n        logger.info(\"new user: '%s' - '%s'\" % (newuser.get_id(), newuser.name))\n        return flask.redirect(next or url_for(\".info\"))\n\n\n@login_page.route(\"/change_password\", methods=['POST'])\n@login_required\ndef change_password():\n    uid = current_user.get_id()\n    pw_old = request.form['oldpwd']\n    if not current_user.authenticate(pw_old):\n        flask.flash(\"Ooops, old password is wrong!\", \"error\")\n        return flask.redirect(url_for(\".info\"))\n\n    pw1 = request.form['password1']\n    pw2 = request.form['password2']\n    if pw1 != pw2:\n        flask.flash(\"Oops, new passwords do not match!\", \"error\")\n        return flask.redirect(url_for(\".info\"))\n\n    pwdmanager.change_password(uid, pw1)\n    flask.flash(\"Your password has been changed.\")\n    return flask.redirect(url_for(\".info\"))\n\n\n@login_page.route(\"/logout\")\n@login_required\ndef logout():\n    bread = base_bread() + [('Login', url_for('.logout'))]\n    logout_user()\n    flask.flash(\"You are logged out now. Have a nice day!\")\n    return flask.redirect(request.args.get(\"next\") or request.referrer or url_for('.info'))\n\n\n@login_page.route(\"/admin\")\n@login_required\n@admin_required\ndef admin():\n    return \"success: only admins can read this!\"\n", "path": "lmfdb/users/main.py"}]}