def reset(): "Reset the password for a user account and send email." if not flask.current_app.config["MAIL_SERVER"]: utils.flash_error("Cannot reset password; no email server defined.") return flask.redirect(flask.url_for("home")) if utils.http_GET(): email = flask.request.args.get("email") or "" email = email.lower() return flask.render_template("user/reset.html", email=email) elif utils.http_POST(): try: user = get_user(email=flask.request.form["email"]) if user is None: raise KeyError if user["status"] != constants.ENABLED: raise KeyError except KeyError: pass else: with UserSaver(user) as saver: saver.set_password() send_password_code(user, "password reset") utils.get_logger().info(f"reset user {user['username']}") utils.flash_message( "An email has been sent if the user account exists.") return flask.redirect(flask.url_for("home"))
def serve(iuid): """Return dataset's information (metadata), update it, or delete it. The content of the dataset cannot be updated via this resource. """ try: dataset = get_dataset(iuid) except ValueError as error: flask.abort(http.client.NOT_FOUND) if utils.http_GET(): if not allow_view(dataset): flask.abort(http.client.FORBIDDEN) set_links(dataset) return utils.jsonify(dataset, schema=flask.url_for("api_schema.dataset", _external=True)) elif utils.http_POST(csrf=False): if not allow_edit(dataset): flask.abort(http.client.FORBIDDEN) try: data = flask.request.get_json() with DatasetSaver(dataset) as saver: try: saver.set_title(data["title"]) except KeyError: pass try: saver.set_description(data["description"]) except KeyError: pass try: saver.set_public(data["public"]) except KeyError: pass try: saver.set_vega_lite_types(data["meta"]) except KeyError: pass except ValueError as error: return str(error), http.client.BAD_REQUEST dataset = saver.doc set_links(dataset) return utils.jsonify(dataset, schema=flask.url_for("api_schema.dataset", _external=True)) elif utils.http_DELETE(): if not possible_delete(dataset): flask.abort(http.client.FORBIDDEN) if not allow_delete(dataset): flask.abort(http.client.FORBIDDEN) flask.g.db.delete(dataset) for log in utils.get_logs(dataset["_id"], cleanup=False): flask.g.db.delete(log) return "", http.client.NO_CONTENT
def stencil(): "Select a stencil for the dataset given as form argument." try: iuid = flask.request.values.get("dataset") if not iuid: raise ValueError("No dataset IUID provided.") dataset = datagraphics.dataset.get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not datagraphics.dataset.allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) if utils.http_GET(): stencils = [] for name in flask.current_app.config["STENCILS"]: header = deepcopy( flask.current_app.config["STENCILS"][name]["header"]) field_variables = [ v for v in header["variables"] if v.get("class") == "field" ] header["combinations"] = combinations(field_variables, dataset["meta"].items()) if header["combinations"]: stencils.append(header) stencils.sort(key=lambda h: (h.get("weight", 0), h["title"])) return flask.render_template("graphic/stencil.html", dataset=dataset, stencils=stencils) elif utils.http_POST(): try: spec = deepcopy(flask.current_app.config["STENCILS"]\ [flask.request.form["stencil"]]) header = spec.pop("header") setfields = SetFields(flask.request.form["combination"]) url = flask.url_for("api_dataset.content", iuid=dataset["_id"], ext="csv", _external=True) for variable in header["variables"]: if variable.get("class") == "dataset": setfields.lookup["/".join(variable["path"])] = url setfields.traverse(spec) with GraphicSaver() as saver: saver.set_dataset(dataset) saver.set_title(header["title"]) saver.set_description( f"Created from stencil {header['name']}.") saver.set_public(False) saver.set_specification(spec) except (KeyError, ValueError) as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def register(): "Register a new user account." if not flask.g.am_admin and not flask.current_app.config["USER_REGISTER"]: utils.flash_error("Only admin can register new user accounts.") return flask.redirect(flask.url_for("home")) if utils.http_GET(): return flask.render_template("user/register.html") elif utils.http_POST(): try: with UserSaver() as saver: saver.set_username(flask.request.form.get("username")) saver.set_email(flask.request.form.get("email")) saver.set_role(constants.USER) if flask.g.am_admin: saver.set_password( flask.request.form.get("password") or None) saver.set_apikey() saver.set_status(constants.ENABLED) else: saver.set_password() user = saver.doc except ValueError as error: utils.flash_error(error) return flask.redirect(flask.url_for(".register")) utils.get_logger().info(f"registered user {user['username']}") if user["status"] == constants.ENABLED: # Directly enabled and code set. Send code to the user. if user["password"][:5] == "code:": send_password_code(user, "registration") utils.get_logger().info(f"enabled user {user['username']}") utils.flash_message("User account created; check your email.") # Directly enabled and password set. No email to anyone. else: utils.get_logger().info(f"enabled user {user['username']}" " and set password") utils.flash_message("User account created and password set.") # Was set to 'pending'; send email to admins. else: admins = get_users(constants.ADMIN, status=constants.ENABLED) emails = [u["email"] for u in admins] message = flask_mail.Message("DataGraphics user account pending", recipients=emails) url = flask.url_for(".display", username=user["username"], _external=True) message.body = f"To enable the user account, go to {url}" utils.mail.send(message) utils.get_logger().info(f"pending user {user['username']}") utils.flash_message("User account created; an email will be sent" " when it has been enabled by the admin.") return flask.redirect(flask.url_for("home"))
def serve(iuid): "Return graphic information, update it, or delete it." try: graphic = get_graphic(iuid) except ValueError as error: flask.abort(http.client.NOT_FOUND) if utils.http_GET(): if not allow_view(graphic): flask.abort(http.client.FORBIDDEN) set_links(graphic) return utils.jsonify(graphic, schema=flask.url_for("api_schema.graphic", _external=True)) elif utils.http_POST(csrf=False): if not allow_edit(graphic): flask.abort(http.client.FORBIDDEN) try: data = flask.request.get_json() with GraphicSaver(graphic) as saver: try: saver.set_title(data["title"]) except KeyError: pass try: saver.set_description(data["description"]) except KeyError: pass try: saver.set_public(data["public"]) except KeyError: pass try: saver.set_specification(data["specification"]) except KeyError: pass except ValueError as error: return str(error), http.client.BAD_REQUEST graphic = saver.doc set_links(graphic) return utils.jsonify(graphic, schema=flask.url_for("api_schema.graphic", _external=True)) elif utils.http_DELETE(): if not allow_delete(graphic): flask.abort(http.client.FORBIDDEN) flask.g.db.delete(graphic) for log in utils.get_logs(graphic["_id"], cleanup=False): flask.g.db.delete(log) return "", http.client.NO_CONTENT
def password(): "Set the password for a user account, and login user." if utils.http_GET(): return flask.render_template( "user/password.html", username=flask.request.args.get("username"), code=flask.request.args.get("code")) elif utils.http_POST(): try: code = "" try: username = flask.request.form.get("username") or "" if not username: raise ValueError user = get_user(username=username) if user is None: raise ValueError if flask.g.am_admin and \ flask.g.current_user["username"] != username: pass # No check for either code or current password. elif flask.current_app.config["MAIL_SERVER"]: code = flask.request.form.get("code") or "" if user["password"] != f"code:{code}": raise ValueError else: password = flask.request.form.get("current_password") or "" if not check_password_hash(user["password"], password): raise ValueError except ValueError: if flask.current_app.config["MAIL_SERVER"]: raise ValueError("No such user or wrong code.") else: raise ValueError("No such user or wrong password.") password = flask.request.form.get("password") or "" if len(password) < flask.current_app.config["MIN_PASSWORD_LENGTH"]: raise ValueError("Too short password.") if not flask.current_app.config["MAIL_SERVER"]: if password != flask.request.form.get("confirm_password"): raise ValueError("Wrong password entered; confirm failed.") except ValueError as error: utils.flash_error(str(error)) return flask.redirect( flask.url_for(".password", username=username, code=code)) else: with UserSaver(user) as saver: saver.set_password(password) utils.get_logger().info(f"password user {user['username']}") if not flask.g.current_user: do_login(username, password) return flask.redirect(flask.url_for("home"))
def create(): "Create a new dataset, from file or URL." if utils.http_GET(): return flask.render_template("dataset/create.html") elif utils.http_POST(): try: with DatasetSaver() as saver: saver.set_title() saver.set_description() saver.set_public(False) if not saver.upload_file(): saver.get_url_data() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def edit(iuid): "Edit the dataset, or delete it." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if utils.http_GET(): if not allow_edit(dataset): utils.flash_error("Edit access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) return flask.render_template("dataset/edit.html", am_owner=am_owner(dataset), dataset=dataset) elif utils.http_POST(): if not allow_edit(dataset): utils.flash_error("Edit access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) try: with DatasetSaver(dataset) as saver: saver.set_title() if flask.g.am_admin: saver.change_owner() if am_owner(dataset): saver.set_editors() saver.set_description() saver.upload_file() saver.set_vega_lite_types() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for(".display", iuid=iuid)) elif utils.http_DELETE(): if not possible_delete(dataset): utils.flash_error("Dataset cannot be deleted; use by graphics.") return flask.redirect(flask.url_for(".display", iuid=iuid)) if not allow_delete(dataset): utils.flash_error("Delete access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) flask.g.db.delete(dataset) for log in utils.get_logs(dataset["_id"], cleanup=False): flask.g.db.delete(log) utils.flash_message("The dataset was deleted.") return flask.redirect(flask.url_for("datasets.display"))
def edit(iuid): "Edit the graphic, or delete it." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if utils.http_GET(): if not allow_edit(graphic): utils.flash_error("Edit access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) return flask.render_template("graphic/edit.html", am_owner=am_owner(graphic), graphic=graphic) elif utils.http_POST(): if not allow_edit(graphic): utils.flash_error("Edit access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) try: with GraphicSaver(graphic) as saver: saver.set_title() if flask.g.am_admin: saver.change_owner() if am_owner(graphic): saver.set_editors() saver.set_description() saver.set_specification() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"])) elif utils.http_DELETE(): if not allow_delete(graphic): utils.flash_error("Delete access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) flask.g.db.delete(graphic) for log in utils.get_logs(graphic["_id"], cleanup=False): flask.g.db.delete(log) utils.flash_message("The graphic was deleted.") return flask.redirect( flask.url_for("dataset.display", iuid=graphic["dataset"]))
def create(): "Create a new graphic for dataset given as form argument." try: iuid = flask.request.values.get("dataset") if not iuid: raise ValueError("No dataset IUID provided.") dataset = datagraphics.dataset.get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not datagraphics.dataset.allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) if utils.http_GET(): graphic = { "$schema": constants.VEGA_LITE_SCHEMA_URL, "data": { "url": flask.url_for("api_dataset.content", iuid=dataset["_id"], ext="csv", _external=True) } } return flask.render_template("graphic/create.html", dataset=dataset, graphic=graphic) elif utils.http_POST(): try: with GraphicSaver() as saver: saver.set_dataset(dataset) saver.set_title() saver.set_description() saver.set_public(False) saver.set_specification() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def login(): """Login to a user account. Creates the admin user specified in the settings.json, if not done. """ app = flask.current_app if app.config.get("ADMIN_USER"): user = get_user(username=app.config["ADMIN_USER"]["username"]) if user is None: try: with UserSaver() as saver: saver.set_username(app.config["ADMIN_USER"]["username"]) saver.set_email(app.config["ADMIN_USER"]["email"]) saver.set_role(constants.ADMIN) saver.set_status(constants.ENABLED) saver.set_password(app.config["ADMIN_USER"]["password"]) utils.get_logger().info("Created admin user " + app.config["ADMIN_USER"]["username"]) except ValueError as error: utils.get_logger().error("Could not create admin user;" " misconfiguration.") if utils.http_GET(): return flask.render_template("user/login.html", next=flask.request.args.get("next")) if utils.http_POST(): username = flask.request.form.get("username") password = flask.request.form.get("password") try: if username and password: do_login(username, password) else: raise ValueError try: next = flask.request.form["next"] except KeyError: return flask.redirect(flask.url_for("home")) else: return flask.redirect(next) except ValueError: utils.flash_error("Invalid user or password, or account disabled.") return flask.redirect(flask.url_for(".login"))
def edit(username): "Edit the user display. Or delete the user." user = get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if not am_admin_or_self(user): utils.flash_error("Access not allowed.") return flask.redirect(flask.url_for("home")) if utils.http_GET(): return flask.render_template("user/edit.html", user=user, deletable=is_empty(user)) elif utils.http_POST(): with UserSaver(user) as saver: if flask.g.am_admin: email = flask.request.form.get("email") if email != user["email"]: saver.set_email(email) if am_admin_and_not_self(user): saver.set_role(flask.request.form.get("role")) if flask.request.form.get("apikey"): saver.set_apikey() return flask.redirect( flask.url_for(".display", username=user["username"])) elif utils.http_DELETE(): if not is_empty(user): utils.flash_error("Cannot delete non-empty user account.") return flask.redirect(flask.url_for(".display", username=username)) for log in utils.get_logs(user["_id"], cleanup=False): flask.g.db.delete(log) flask.g.db.delete(user) utils.flash_message(f"Deleted user {username}.") utils.get_logger().info(f"deleted user {username}") if flask.g.am_admin: return flask.redirect(flask.url_for(".all")) else: return flask.redirect(flask.url_for("home"))