def register(): if request.method == "POST": name = request.form["name"].strip() email = request.form["email"] pw1 = request.form["password1"] pw2 = request.form["password2"] if len(name) < 2: flash_error("Oops, name is too short. Please enter at least 2 characters.") return make_response(render_template("register.html", title="Register", email=email)) try: email = validate_email(email)['email'] except EmailNotValidError as e: flash_error("""Oops, email '%s' is not allowed. %s""", email, str(e)) return make_response(render_template("register.html", title="Register", email=email)) if pw1 != pw2: flash_error("Oops, passwords do not match!") return make_response(render_template("register.html", title="Register", email=email)) if len(pw1) < 8: flash_error("Oops, password too short. Minimum 8 characters, please!") return make_response(render_template("register.html", title="Register", email=email)) password = pw1 if userdb.user_exists(email=email): flash_error("The email address '%s' is already registered!", email) return make_response(render_template("register.html", title="Register", email=email)) newuser = userdb.new_user(email=email, password=password,) send_confirmation_email(email) login_user(newuser, remember=True) flask.flash(Markup("Hello! Congratulations, you are a new user!")) logger.info("new user: '******' - '%s'" % (newuser.get_id(), newuser.email)) return redirect(url_for(".info")) return render_template("register.html", title="Register", email="")
def register_for_talk(seminar_id, talkid): from flask import flash talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid}) if talk is None: return abort(404, "Talk not found") # If registration isn't required just send them to the talk page # where the user will see an appropriate livestream link if talk.access_control != 4: return redirect( url_for('show_talk', seminar_id=seminar_id, talkid=talkid)) if current_user.is_anonymous or len(current_user.name) < 2: return redirect( url_for("user.info", next=url_for("register_for_talk", seminar_id=seminar_id, talkid=talkid))) if not current_user.email_confirmed: flash_error("You need to confirm your email before you can register.") return redirect( url_for('show_talk', seminar_id=seminar_id, talkid=talkid)) if not talk.live_link: return abort(404, "Livestream link for talk not found") if talk.register_user(): flash("You have been registered; enjoy the talk!") else: flash("Previous registration confirmed; enjoy the talk!") if talk.is_starting_soon(): return redirect(talk.live_link) else: return redirect( url_for('show_talk', seminar_id=seminar_id, talkid=talkid))
def parse_daterange(info, query, time=True): tz = current_user.tz date = info.get("daterange") if date: sub_query = {} if "-" not in date: # make it into a range date = date + "-" + date start, end = date.split("-") if start.strip(): try: start = tz.localize(parse(start)) sub_query["$gte"] = start if time else start.date() except Exception as e: flash_error("Could not parse start date %s. Error: " + str(e), start) if end.strip(): try: end = tz.localize(parse(end)) end = end + timedelta(hours=23, minutes=59, seconds=59) sub_query["$lte"] = end if time else end.date() except Exception as e: flash_error("Could not parse end date %s. Error: " + str(e), end) if sub_query: query["start_time" if time else "start_date"] = sub_query
def parse_recent_edit(info, query): recent = info.get("recent", "").strip() if recent: try: recent = float(recent) except Exception as e: flash_error( "Could not parse recent edit input %s. Error: " + str(e), recent) else: recent = datetime.now() - timedelta(hours=recent) query["edited_at"] = {"$gte": recent}
def reset_password_wtoken(token): try: # the users have one hour to use previous token email = read_timed_token(token, "reset password", 3600) except Exception: flash_error("The link is invalid or has expired.") return redirect(url_for(".info")) if not userdb.user_exists(email): flash_error("The link is invalid or has expired.") return redirect(url_for(".info")) if request.method == "POST": pw1 = request.form["password1"] pw2 = request.form["password2"] if pw1 != pw2: flash_error("Oops, passwords do not match!") return redirect(url_for(".reset_password_wtoken", token=token)) if len(pw1) < 8: flash_error("Oops, password too short. Minimum 8 characters, please!") return redirect(url_for(".reset_password_wtoken", token=token)) userdb.change_password(email, pw1) flask.flash(Markup("Your password has been changed. Please login with your new password.")) return redirect(url_for(".info")) return render_template("reset_password_wtoken.html", title="Reset password", token=token)
def can_edit_institution(shortname, name, new): if not allowed_shortname(shortname) or len(shortname) < 2 or len(shortname) > 32: flash_error( "The identifier must be 2 to 32 characters in length and can include only letters, numbers, hyphens and underscores." ) return redirect(url_for("list_institutions"), 302), None # We don't allow backticks so that we can use them to delimit strings in javascript if "`" in name: flash_error("The name must not include backticks") return redirect(url_for("list_institutions"), 302), None institution = db.institutions.lookup(shortname) # Check if institution exists if new != (institution is None): flash_error("Identifier %s %s" % (shortname, "already exists" if new else "does not exist")) return redirect(url_for("list_institutions"), 302), None if not new and not current_user.is_admin: # Make sure user has permission to edit if institution["admin"].lower() != current_user.email.lower(): rec = userdb.lookup(institution["admin"]) link = rec["homepage"] if rec["homepage"] else "mailto:%s" % rec["email"] owner = "%s (%s)" % (rec['name'], link) flash_error( "You do not have permission to edit %s. Contact the institution admin, %s, and ask them to fix any errors." % (institution["name"], owner) ) return redirect(url_for(".index"), 302), None institution = WebInstitution(shortname, data=None, editing=new) return None, institution
def endorse_wtoken(token): try: # tokens last forever endorser, email = read_timed_token(token, "endorser", None) except Exception: return flask.abort(404, "The link is invalid or has expired.") return redirect(url_for(".info")) if current_user.is_creator: flash_error("Account already has creator privileges.") elif current_user.email.lower() != email.lower(): flash_error("The link is not valid for this account.") else: current_user.endorser = int(endorser) # must set endorser first current_user.creator = True # this will update the db return redirect(url_for(".info"))
def send_confirmation_email(email): token = generate_confirmation_token(email) confirm_url = url_for(".confirm_email", token=token, _external=True, _scheme="https") html = render_template("confirm_email.html", confirm_url=confirm_url) subject = "Please confirm your email" try: send_email(email, subject, html) return True except: import sys flash_error( 'Unable to send email confirmation link; please contact <a href="mailto:[email protected]">[email protected]</a> directly to confirm your email.' ) app.logger.error("%s unable to send email to %s due to error: %s" % (timestamp(), email, sys.exc_info()[0])) return False
def save(self, data): data = dict(data) # copy email = data.pop("email", None) if not email: raise ValueError("data must contain email") user = self.lookup(email) if not user: raise ValueError("user does not exist") if not data: raise ValueError("no data to save") if "new_email" in data: data["email"] = data.pop("new_email") try: # standerdize email data["email"] = validate_email(data["email"])["email"] except EmailNotValidError as e: flash_error("""Oops, email '%s' is not allowed. %s""", data["email"], str(e)) return False if self.user_exists(data["email"]): flash_error( "There is already a user registered with email = %s", data["email"]) return False for key in list(data): if key not in self.search_cols: if key != "id": from .app import app app.logger.critical( "Need to update pwdmanager code to account for schema change key=%s" % key) data.pop(key) with DelayCommit(db): if "email" in data: newemail = data["email"] db.institutions.update({"admin": ilike_query(email)}, {"admin": newemail}) db.seminars.update({"owner": ilike_query(email)}, {"owner": newemail}) db.seminar_organizers.update({"email": ilike_query(email)}, {"email": newemail}) db.talks.update({"speaker_email": ilike_query(email)}, {"speaker_email": newemail}) self.update({"email": ilike_query(email)}, data, restat=False) return True
def login(**kwargs): # login and validate the user … email = request.form["email"] password = request.form["password"] if not email or not password: flash_error("Oops! Wrong username or password.") return redirect(url_for(".info")) # we always remember remember = True # if request.form["remember"] == "on" else False user = SeminarsUser(email=email) if user.email and user.check_password(password): # this is where we set current_user = user login_user(user, remember=remember) if user.name: flask.flash(Markup("Hello %s, your login was successful!" % user.name)) else: flask.flash(Markup("Hello, your login was successful!")) logger.info("login: '******' - '%s'" % (user.get_id(), user.name)) return redirect(request.form.get("next") or url_for(".info")) flash_error("Oops! Wrong username or password.") return redirect(url_for(".info"))
def show_seminar(shortname): # We need organizers to be able to see seminars with display=False seminar = seminars_lucky({"shortname": shortname}, include_pending=True) if seminar is None: return abort(404, "Seminar not found") if not seminar.visible(): # There may be a non-API version of the seminar that can be shown name = seminar.name # for flash_error seminar = seminars_lucky({"shortname": shortname}) if seminar is None or not seminar.visible(): flash_error("You do not have permission to view %s", name) return redirect(url_for("seminar_series_index"), 302) talks = seminar.talks(projection=3) now = get_now() future = [] past = [] for talk in talks: if talk.end_time >= now: future.append(talk) else: past.append(talk) future.sort(key=lambda talk: talk.start_time) past.sort(key=lambda talk: talk.start_time, reverse=True) if current_user.email in seminar.editors( ) or current_user.is_subject_admin(seminar): section = "Manage" else: section = None return render_template( "seminar.html", title="View series", future=future, past=past, seminar=seminar, section=section, subsection="view", bread=None, )
def show_talk(seminar_id, talkid): token = request.args.get( "token", "") # save the token so user can toggle between view and edit talk = talks_lucky({ "seminar_id": seminar_id, "seminar_ctr": talkid }, include_pending=True) if talk is None: return abort(404, "Talk not found") if not talk.visible(): # There may be a non-API version of the seminar that can be shown talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid}) if talk is None or not talk.visible(): flash_error("You do not have permission to view %s/%s", seminar_id, talkid) return redirect(url_for("seminar_series_index")) kwds = dict(title="View talk", talk=talk, seminar=talk.seminar, subsection="viewtalk", token=token) if token: kwds["section"] = "Manage" # Also want to override top menu from seminars.utils import top_menu menu = top_menu() menu[1] = (url_for("create.index"), "", "Manage") kwds["top_menu"] = menu elif (current_user.is_subject_admin(talk) or current_user.email_confirmed and (current_user.email in talk.seminar.editors() or current_user.email == talk.speaker_email)): kwds["section"] = "Manage" return render_template("talk.html", **kwds)
def confirm_email(token): try: # the users have 24h to confirm their email email = read_timed_token(token, "confirm email", 86400) except Exception: flash_error("The confirmation link is invalid or has expired.") else: if current_user.email.lower() != email.lower(): flash_error("The link is not valid for this account.") elif current_user.email_confirmed: flash_error("Email already confirmed.") else: current_user.email_confirmed = True current_user.save() flask.flash("Thank you for confirming your email!", "success") return redirect(url_for(".info"))
def change_password(): email = current_user.email pw_old = request.form["oldpwd"] if not current_user.check_password(pw_old): flash_error("Oops, old password is wrong!") return redirect(url_for(".info")) pw1 = request.form["password1"] pw2 = request.form["password2"] if pw1 != pw2: flash_error("Oops, new passwords do not match!") return redirect(url_for(".info")) if len(pw1) < 8: flash_error("Oops, password too short. Minimum 8 characters, please!") return redirect(url_for(".info")) userdb.change_password(email, pw1) flask.flash(Markup("Your password has been changed.")) return redirect(url_for(".info"))
def decorated_view(*args, **kwargs): logger.info("email confirmed access attempt by %s" % current_user.get_id()) if not current_user.email_confirmed: flash_error("Oops, you haven't yet confirmed your email") return redirect(url_for("user.info")) return fn(*args, **kwargs)
def _talks_index( query={}, sort=None, subsection=None, past=False, keywords="", limit=None, # this is an upper bound on desired number of talks, we might filter some extra out limitbuffer=1000, # the number of extra talks that we give ourselves to try to get the limit right asblock=False, # the number of talks returned is based on star time blocks getcounters=True, # doesn't limit the SQL search to get the full counters visible_counter=0, fully_filtered=True, ): # Eventually want some kind of cutoff on which talks are included. search_array = TalkSearchArray(past=past) info = to_dict(read_search_cookie(search_array), search_array=search_array) info.update(request.args) if keywords: info["keywords"] = keywords keywords = info.get("keywords", "") query = dict(query) parse_substring(info, query, "keywords", [ "title", "abstract", "speaker", "speaker_affiliation", "seminar_id", "comments", "speaker_homepage", "paper_link" ]) more = { } # we will be selecting talks satsifying the query and recording whether they satisfy the "more" query # Note that talks_parser ignores the "time" field at the moment; see below for workaround talks_parser(info, more) if topdomain() == "mathseminars.org": query["topics"] = {"$contains": "math"} query["display"] = True query["hidden"] = {"$or": [False, {"$exists": False}]} query["audience"] = {"$lte": DEFAULT_AUDIENCE} now = datetime.now(pytz.UTC) if past: query["end_time"] = {"$lt": now} query["seminar_ctr"] = {"$gt": 0} # don't show rescheduled talks if sort is None: sort = [("start_time", -1), "seminar_id"] else: query["end_time"] = {"$gte": now} if sort is None: sort = ["start_time", "seminar_id"] def dosearch(limit=limit, limitbuffer=limitbuffer): if limit and not getcounters: # we fetch extra talks to account for filtering talks = talks_search(query, sort=sort, seminar_dict=all_seminars(), more=more, limit=limit + limitbuffer) else: talks = talks_search(query, sort=sort, seminar_dict=all_seminars(), more=more) # Filtering on display and hidden isn't sufficient since the seminar could be private talks = [talk for talk in talks if talk.searchable()] return talks def truncateasblock(talks, retry=True): if not talks: return talks last_time = None # find enough talks such that the next talk has a different starting time for i, t in enumerate(talks): if last_time is None: last_time = t.start_time continue if t.start_time != last_time: if i > limit: return talks[:i - 1] else: last_time = t.start_time else: if retry and limit and not getcounters: # redo the search without limits talks = dosearch(limit=None) return truncateasblock(talks, retry=False) return talks def truncate(talks): if asblock and limit: return truncateasblock(talks) elif limit: return talks[:limit] else: return talks talks = dosearch() if getcounters: counters = _get_counters(talks) else: counters = _get_counters([]) if getcounters and fully_filtered: # the initial query was not limited as getcounters = True # we will first filter after figuring out the more attribute # and then truncate pass else: # we are not going to filter or the query was already limited, so we can truncate talks = truncate(talks) # While we may be able to write a query specifying inequalities on the timestamp in the user's timezone, it's not easily supported by talks_search. So we filter afterward timerange = info.get("timerange", "").strip() if timerange: tz = current_user.tz try: timerange = process_user_input(timerange, col="search", typ="daytimes") except ValueError: try: onetime = process_user_input(timerange, col="search", typ="daytime") except ValueError: flash_error("Invalid time range input: %s", timerange) else: for talk in talks: if talk.more: talkstart = adapt_datetime(talk.start_time, tz) t = date_and_daytime_to_time(talkstart.date(), onetime, tz) talk.more = (t == talkstart) else: for talk in talks: if talk.more: talkstart = adapt_datetime(talk.start_time, tz) talkend = adapt_datetime(talk.end_time, tz) t0, t1 = date_and_daytimes_to_times( talkstart.date(), timerange, tz) talk.more = (t0 <= talkstart) and (talkend <= t1) # get last_time before potential filtering last_time = int(talks[-1].start_time.timestamp()) if talks else None if fully_filtered: # first filter then truncate row_attributes, talks = _get_row_attributes(talks, visible_counter, fully_filtered) if getcounters: # we have not yet truncated the results if limit and len(talks) > limit: talks = truncate(talks) row_attributes = row_attributes[:len(talks)] last_time = int( talks[-1].start_time.timestamp()) if talks else None else: row_attributes = _get_row_attributes(talks, visible_counter) response = make_response( render_template("browse_talks.html", title="Browse past talks" if past else "Browse talks", section="Browse", info=info, subsection=subsection, talk_row_attributes=zip(talks, row_attributes), past=past, last_time=last_time, extraargs=urlencode({'keywords': keywords}), **counters)) if request.cookies.get("topics", ""): # TODO: when we move cookie data to server with ajax calls, this will need to get updated again # For now we set the max_age to 30 years response.set_cookie("topics_dict", topic_dag.port_cookie(), max_age=60 * 60 * 24 * 365 * 30) response.set_cookie("topics", "", max_age=0) # disable cache response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate' response.headers['Pragma'] = 'no-cache' return response
def get_endorsing_link(): email = request.form["email"].strip() try: email = validate_email(email)["email"] except EmailNotValidError as e: flash_error("""Oops, email '%s' is not allowed. %s""", email, str(e)) return redirect(url_for(".info")) rec = userdb.lookup(email, ["name", "creator", "email_confirmed"]) if rec is None or not rec["email_confirmed"]: # No account or email unconfirmed if db.preendorsed_users.count({'email':email}): flash_infomsg("The email address %s has already been pre-endorsed.", email) return redirect(url_for(".info")) else: db.preendorsed_users.insert_many([{"email": email, "endorser": current_user._uid}]) to_send = """Hello, I am offering you permission to add content (e.g., create a seminar) on the {topdomain} website. To accept this invitation: 1. Register at {register} using this email address. 2. Click on the link the system emails you, to confirm your email address. 3. Now any content you create will be publicly viewable. Best, {name} """.format( name = current_user.name, topdomain = topdomain(), register = url_for('.register', _external=True, _scheme='https'), ) data = { "body": to_send, "subject": "An invitation to collaborate on " + topdomain(), } endorsing_link = """ <p> The person {email} will be able to create content after registering and confirming the email address.</br> <button onClick="window.open('mailto:{email}?{msg}')"> Send email </button> to let them know. </p> """.format( email=email, msg=urlencode(data, quote_via=quote) ) flash_infomsg(""" The person %s will be able to create content after registering and confirming the email address. Click the "Send email" button below to let them know.""",email) session["endorsing link"] = endorsing_link return redirect(url_for(".info")) else: target_name = rec["name"] if rec["creator"]: flash_infomsg("%s is already able to create content.", target_name) return redirect(url_for(".info")) else: welcome = "Hello" if not target_name else ("Dear " + target_name) to_send = """{welcome},<br> <p> You have been endorsed on {topdomain} and any content you create will be publicly viewable. </p> <p> Thanks for using {topdomain}! </p> """.format( welcome = welcome, topdomain = topdomain() ) subject = "Endorsement to create content on " + topdomain() send_email(email, subject, to_send) userdb.make_creator(email, int(current_user.id)) flash_infomsg("%s is now able to create content.", target_name if target_name else email) return redirect(url_for(".info")) raise Exception("The function get_endorsing_link did not return a value")