def before_request() -> None: """ Runs before each request. """ try: vote = cube.load_file("vote") except: #sometimes fails, not sure why app.logger.warning("Error in parsing JSON file") else: if time.time() > cube.short_date(vote["ends_at"]): vote["vote_active"] = False cube.dump_file(vote, "vote") # record number of times each page has been visted path = flask.request.path if path.split("/")[1] != "static" and path.split("/")[1] != "_uploads": vists = cube.load_file("vists") date = cube.unix_to_date(time.time()) vists[path] = vists.get(path, {}) vists[path][date] = vists[path].get(date, 0) + 1 # more than one day if time.time() - vists["time"] > 60 * 60 * 24: cube.graph_vists() vists["time"] = time.time() cube.dump_file(vists, "vists")
def GLOBALS() -> dict: """ Returns all the global variables passed to every template. """ user = cube.load_file("users").get(flask.session.get("account", None), {}) vars = { "vote_active": cube.load_file("vote")["vote_active"], "user": user, "btnform": forms.FlaskForm(), "searchForm": forms.SearchForm(), } return cube.add_dict(GLOBAL, vars)
def settings() -> dict: """ Adjusts a user's profile. """ if "account" not in flask.session: return flask.redirect("profile") users = cube.load_file("users") user = users[flask.session["account"]] gpgForm = forms.GPGForm() photoForm = forms.PhotoForm() secret = "" if gpgForm.validate_on_submit(): key = cube.gpg.import_keys(gpgForm.gpgkey.data) user["keys"] += cube.gpg.list_keys(keys=[key.fingerprints[0]]) curr = user["keys"][-1] curr["fuids"] = ", ".join( [uid.split()[-1][1:-1] for uid in curr["uids"]]) cube.dump_file(users, "users") return alert("GPG key added.", "success", "meta") elif photoForm.validate_on_submit(): users = cube.load_file("users") delete_photo() #old profile picture not necessary anymore filename = forms.photos.save(photoForm.photo.data) users[flask.session["account"]]["pfp"] = forms.photos.url(filename) users[flask.session["account"]]["pfpfilename"] = filename cube.dump_file(users, "users") return alert("Profile photo changed.", "success", "profile") if flask.request.method == "POST": if "remove" in flask.request.form: delete_photo() return alert("Profile picture removed.", "success", "profile") if "delete" in flask.request.form: del user["keys"][int(flask.request.form["delete"])] cube.dump_file(users, "users") if "enable_2fa" in flask.request.form: secret = cube.pyotp.random_base32() user["2fa"] = secret cube.dump_file(users, "users") if "disable_2fa" in flask.request.form: del user["2fa"] cube.dump_file(users, "users") if "disable_yubi" in flask.request.form: del user["yubi"] cube.dump_file(users, "users") return {"gpgForm": gpgForm, "photoForm": photoForm, "secret": secret}
def email() -> Response: """ After requesting to be added to the email list, see if nonce matches. """ emails = cube.load_file("emails") requests = emails["requests"] nonce = flask.request.args.get("nonce", None) # subscribing if nonce in requests: email = requests[nonce] # Remove previous attempts emails["requests"] = { nonce: value for nonce, value in requests.items() if value != email } cube.dump_file(emails, "emails") cube.register_email(email) return alert("You have been added to the email list.", "success") requests = emails["unsubscribe-requests"] # unsubscribing if nonce in requests: email = requests[nonce] # Remove previous attempts emails["unsubscribe-requests"] = { nonce: value for nonce, value in requests.items() if value != email } cube.dump_file(emails, "emails") cube.remove_email(email) return alert("You have been removed from the email list.", "success") return alert("Wrong nonce. Try registering again?", "danger")
def records() -> dict: """ Displays TJ's all time bests. """ records = cube.load_file("records") times, people = records["records"], records["people"] refresh = False if "wca_token" in flask.session and "ion_token" in flask.session: me = cube.api_call("wca", "me")["me"] year = cube.api_call("ion", "profile")["graduation_year"] if [me["url"], me["name"], year] not in people: records["people"].append([me["url"], me["name"], year]) # New person added refresh = True cube.dump_file(records, "records") if refresh or time.time() - records["time"] > cube.CONFIG["time"]: cube.update_records() (sing_rank, avg_rank), kinch_rank = cube.get_ranks() cube.dump_file( { "sor_single": sing_rank, "sor_average": avg_rank, "kinch": kinch_rank }, "wca/ranks") return { "times": times, "events": cube.EVENTS, "icons": cube.ICONS, "DNF": statistics.DNF, "ranks": cube.RANKS }
def wca_stats() -> dict: """ Computes WCA summary statistics for TJ. """ ranks = cube.load_file("wca/ranks") return { "events": [cube.ICONS[event][6:] for event in cube.EVENTS], "sor": cube.get_sor(), "sor_rank": (ranks["sor_single"], ranks["sor_average"]), "kinch": cube.get_kinch(), "kinch_rank": ranks["kinch"] }
def cookie() -> dict: """ Returns the user's cookies. """ data = dict(flask.session) users = cube.load_file("users") if "account" in flask.session: user = users[flask.session["account"]] if len(user["keys"]) > 0: return str( cube.gpg.encrypt(flask.json.dumps(data), [k["fingerprint"] for k in user["keys"]], always_trust=True)) return data
def competitions() -> dict: """ Gets the cached competitions, but will refresh if not updated recently. """ c = cube.load_file("comps") last, comps = c["time"], c["comps"] if time.time() - last > cube.CONFIG["time"]: comps = cube.get_comps() return { "comps": comps, "last": cube.unix_to_human(last), "icons": cube.ICONS }
def delete_photo() -> None: """ Deletes a photo from the server. """ users = cube.load_file("users") user = users[flask.session["account"]] try: del user["pfp"] except KeyError: pass try: os.remove(forms.photos.path(user["pfpfilename"])) del user["pfpfilename"] except (KeyError, FileNotFoundError): pass cube.dump_file(users, "users")
def vote_requirements(): """ Checks if the user can view the vote/run pages. """ if not cube.test_token("ion"): flask.session["action"] = flask.request.path return flask.redirect(flask.url_for("ion_login")) vote = cube.load_file("vote") if not cube.valid_voter(): return alert( "You do not fullfill the requirements to be able to vote.", "warning") if not vote["vote_active"]: return alert("Stop trying to subvert democracy!", "danger") return vote
def register_complete(): data = cbor.decode(flask.request.get_data()) client_data = ClientData(data["clientDataJSON"]) att_obj = AttestationObject(data["attestationObject"]) # print("clientData", client_data) # print("AttestationObject:", att_obj) auth_data = server.register_complete(flask.session["state"], client_data, att_obj) flask.session["credentials"].append(auth_data.credential_data) encoded = websafe_encode(auth_data.credential_data) users = cube.load_file("users") users[flask.session["account"]]["yubi"] = encoded cube.dump_file(users, "users") # print("REGISTERED CREDENTIAL:", auth_data.credential_data) return cbor.encode({"status": "OK"})
def check(club_id): """ Checks the signups for a user on ION. """ # may be subject to change, check: https://ion.tjhsst.edu/api/activities # 152 - Rubik's cube club, 198 - Computer vision, 204 - SCT if not cube.test_token("ion"): flask.session["action"] = flask.request.path return flask.redirect(flask.url_for("ion_login")) club_name = cube.api_call("ion", f"activities/{club_id}")["name"] fname = f"signups_{club_id}" record = cube.load_file(fname) username = cube.api_call("ion", "profile")["ion_username"] signups = cube.get_signups(club_id) number = cube.count_meetings(signups) record[username] = number cube.dump_file(record, fname) return alert( f"Recorded for {username} {number} attendances at {club_name}")
def rankings() -> dict: """ Shows the rankings of all TJ students signed up. """ return { "records": cube.load_file("records")["records"], "events": cube.EVENTS }
csp = { "default-src": "'self'", } Talisman(app, content_security_policy=None) # explicit CSRF protection for all forms CSRFProtect(app) # generate sitemap # ext = Sitemap(app=app) flask_uploads.configure_uploads(app, (forms.times, forms.photos)) # Strange glitch with TJ servers - if you look at the source code this *should* happen already... app.register_blueprint(flask_uploads.uploads_mod) rp = PublicKeyCredentialRpEntity( cube.load_file("site")["domain"], "2fa server") server = Fido2Server(rp) def alert(msg: str, category: str = "success", loc: str = "index") -> Response: """ Redirects the user with an alert. """ flask.flash(msg, category) dest = {"self": flask.request.path, "meta": flask.request.full_path} return flask.redirect(flask.url_for(dest.get(loc, loc))) @app.before_request def before_request() -> None: """ Runs before each request. """ try: vote = cube.load_file("vote")
def profile() -> dict: """ Allows the user to login as well as register a new account. """ loginForm = forms.LoginForm() codeForm = forms.TFAForm() signupForm = forms.SignupForm() mailForm = forms.MailForm() httpForm = forms.HTTPForm() ionForm, wcaForm = forms.APIForm(prefix="ion"), forms.APIForm(prefix="wca") rtn = { "loginForm": loginForm, "codeForm": codeForm, "signupForm": signupForm, "mailForm": mailForm, "httpForm": httpForm, "ionForm": ionForm, "wcaForm": wcaForm } users = cube.load_file("users") if "account" in flask.session: tabs = [["overview", "API"], ["email", "refresh", "develop"], ["edit"]] scopes = {"default": 0, "privileged": 1, "admin": 2} tab = flask.request.args.get('tab', 'overview') scope = scopes[flask.session["scope"]] i = None for j, group in enumerate(tabs): if tab in group: i = j if i is None: return alert("Invalid tab!", "info", "self") if i > scope: return alert( "User does not have the valid scope. This incident will be logged.", "danger", "self") rtn = cube.add_dict( { "tabs": tabs, "scopes": scopes, "clubmailpassword": cube.load_file("secrets")["clubmailpassword"], "emails": cube.load_file("emails")["emails"], }, rtn) else: scope = 0 if scope >= 0: if "confirm" in flask.request.form and signupForm.validate_on_submit(): username, password = signupForm.username.data, signupForm.password.data if username in cube.load_file("users"): return alert("Username is taken.", "info", "meta") if password != signupForm.confirm.data: return alert("Passwords do not match.", "info", "meta") cube.register(username, password) return alert("Account registered!", "success", "self") elif "login" in flask.request.form and loginForm.validate_on_submit(): username, password = loginForm.username.data, loginForm.password.data if not cube.check(username, password): return alert("Username or password is incorrect.", "info", "self") # Save login to cookies if 2fa is not enabled, otherwise don't if "2fa" not in users[username] and "yubi" not in users[username]: flask.session["account"] = username flask.session["scope"] = users[username]["scope"] else: if "2fa" in users[username]: flask.session["2fa"] = True flask.session["username"] = username # load U2F challenge if it exists if "yubi" in users[username]: flask.session["yubi"] = websafe_decode(users[username]["yubi"]) return flask.redirect(flask.url_for("profile")) elif "yubi_check" in flask.session or ( "login_2fa" in flask.request.form and codeForm.validate_on_submit()): username = flask.session["username"] if "yubi_check" not in flask.session and not cube.check_2fa( username, str(codeForm.code.data)): return alert("2FA code is incorrect.", "info", "self") if "yubi_check" in flask.session: del flask.session["yubi_check"] # actually login flask.session["account"] = username flask.session["scope"] = users[username]["scope"] return flask.redirect(flask.url_for("profile")) elif "cancel_2fa" in flask.request.form and "2fa" in flask.session: del flask.session["2fa"] elif "cancel_yubi" in flask.request.form and "yubi" in flask.session: del flask.session["yubi"] elif ionForm.validate_on_submit(): rtn = cube.add_dict( {"data": cube.api_call("ion", ionForm.call.data)}, rtn) elif wcaForm.validate_on_submit(): rtn = cube.add_dict( {"data": cube.api_call("wca", wcaForm.call.data)}, rtn) if scope >= 1: if mailForm.validate_on_submit(): recipients = mailForm.recipients.data.split( ", ") if mailForm.recipients.data != "" else cube.load_file( "emails")["emails"] # add footer with unsubscribe information body = mailForm.email.data.replace("\n", "") + \ f""" -- If you're tired of seeing these emails, unsubscribe [here]({cube.TJ}). """ body = cube.markdown2.markdown(body) cube.send_email(recipients, mailForm.subject.data, body) if mailForm.log.data: cube.save_email(mailForm.subject.data, mailForm.email.data) return alert("Mail sent.", "success", "meta") elif httpForm.validate_on_submit(): flask.abort(int(httpForm.http.data)) if scope >= 2: pass if flask.request.method == "POST": if "logout" in flask.request.form: del flask.session["account"] if "2fa" in flask.session: del flask.session["2fa"] if "yubi" in flask.session: del flask.session["yubi"] if "delete" in flask.request.form: del users[flask.session["account"]] del flask.session["account"] cube.dump_file(users, "users") return alert("Account deleted!", "success", "self") if "clear" in flask.request.form: # Save CSRF token csrf = flask.session["csrf_token"] flask.session.clear() flask.session["csrf_token"] = csrf if "fb" in flask.request.form: cube.get_pfps(cube.CONFIG["officers"]) alert("Updated the profile pictures!") if "comps" in flask.request.form: cube.get_comps() alert("Updated the competitions!") if "records" in flask.request.form: cube.update_records() alert("Updated the records!") if "history" in flask.request.form: cube.save_club_history() cube.graph_capacity() cube.graph_blocks("by_x") cube.graph_blocks("by_y") alert("Updated the club history!") if "heatmap" in flask.request.form: cube.graph_vists() alert("Updated the heatmap!") return rtn
def result() -> dict: """ Displays the result of the election. """ return {"result": cube.get_winner(), "vote": cube.load_file("vote")}
def profile() -> dict: """ Allows the user to login as well as register a new account. """ loginForm = forms.LoginForm() signupForm = forms.SignupForm() mailForm = forms.MailForm() httpForm = forms.HTTPForm() ionForm, wcaForm = forms.APIForm(prefix="ion"), forms.APIForm(prefix="wca") rtn = { "loginForm": loginForm, "signupForm": signupForm, "mailForm": mailForm, "httpForm": httpForm, "ionForm": ionForm, "wcaForm": wcaForm } users = cube.load_file("users") if "account" in flask.session: tabs = [["overview", "API"], ["email", "refresh", "develop"], ["edit"]] scopes = {"default": 0, "privileged": 1, "admin": 2} tab = flask.request.args.get('tab', 'overview') scope = scopes[flask.session["scope"]] i = None for j, group in enumerate(tabs): if tab in group: i = j if i is None: return alert("Invalid tab!", "info", "self") if i > scope: return alert( "User does not have the valid scope. This incident will be logged.", "danger", "self") rtn = cube.add_dict( { "tabs": tabs, "scopes": scopes, "clubmailpassword": cube.load_file("secrets")["clubmailpassword"], "emails": cube.load_file("emails")["emails"], }, rtn) else: scope = 0 if scope >= 0: if "confirm" in flask.request.form and signupForm.validate_on_submit(): username, password = signupForm.username.data, signupForm.password.data if username in cube.load_file("users"): return alert("Username is taken.", "info", "meta") if password != signupForm.confirm.data: return alert("Passwords do not match.", "info", "meta") cube.register(username, password) return alert("Account registered!", "success", "self") elif "login" in flask.request.form and loginForm.validate_on_submit(): username, password = loginForm.username.data, loginForm.password.data if not cube.check(username, password): return alert("Username or password is incorrect.", "info", "self") # Save login to cookies flask.session["account"] = username flask.session["scope"] = users[username]["scope"] return flask.redirect(flask.url_for("profile")) elif ionForm.validate_on_submit(): rtn = cube.add_dict( {"data": cube.api_call("ion", ionForm.call.data)}, rtn) elif wcaForm.validate_on_submit(): rtn = cube.add_dict( {"data": cube.api_call("wca", wcaForm.call.data)}, rtn) if scope >= 1: if mailForm.validate_on_submit(): recipients = mailForm.recipients.data.split( ", ") if mailForm.recipients.data != "" else cube.load_file( "emails")["emails"] body = cube.markdown2.markdown(mailForm.email.data).replace( "\n", "") cube.send_email(recipients, mailForm.subject.data, body) if mailForm.log.data: cube.save_email(mailForm.subject.data, mailForm.email.data) return alert("Mail sent.", "success", "meta") elif httpForm.validate_on_submit(): flask.abort(int(httpForm.http.data)) if scope >= 2: pass if flask.request.method == "POST": if "logout" in flask.request.form: del flask.session["account"] if "delete" in flask.request.form: del users[flask.session["account"]] del flask.session["account"] cube.dump_file(users, "users") return alert("Account deleted!", "success", "self") if "clear" in flask.request.form: # Save CSRF token csrf = flask.session["csrf_token"] flask.session.clear() flask.session["csrf_token"] = csrf if "fb" in flask.request.form: cube.get_pfps(cube.CONFIG["officers"]) if "comps" in flask.request.form: cube.get_comps() if "records" in flask.request.form: cube.update_records() return rtn
import time, os from flask_frozen import Freezer import cube OUT = "rendered_templates" #Change URL format to end with a tailing slash site = cube.load_file("site") site["tailing_slash"] = True cube.dump_file(site, "site") from app import app app.config['FREEZER_IGNORE_MIMETYPE_WARNINGS'] = True app.config['FREEZER_REDIRECT_POLICY'] = 'ignore' freezer = Freezer(app) # if __name__ == '__main__': # freezer.freeze() #Undo changes site["tailing_slash"] = False cube.dump_file(site, "site") try: os.mkdir(OUT) except FileExistsError: pass
def convert(order: list, prefix: str = "", new: list = []) -> list: """ Converts an order specification into a parsable (path, name) format by the nav header. """ for name in order: if isinstance(name, list): addition, sublist = name new.append( ("*" + addition, convert(sublist, "{}".format(prefix), []))) else: new.append( ("{}{}".format(prefix, name if name != "" else "index"), NAMES[name])) return new params = cube.load_file("site") TSLASH = "/" if params["tailing_slash"] else "" NAMES = params["names"] NAV = convert(params["order"]) PAGES = { "": (index, ["POST", "GET"]), "competitions": competitions, "algorithms": None, "weekly": { "lectures": lectures, "inhouse": (in_house, ["POST", "GET"]), }, "contact": lambda: { "fb": cube.load_file("fb") },