Esempio n. 1
0
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"))
Esempio n. 2
0
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
Esempio n. 3
0
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"]))
Esempio n. 4
0
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"))
Esempio n. 5
0
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
Esempio n. 6
0
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"))
Esempio n. 7
0
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"]))
Esempio n. 8
0
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"))
Esempio n. 9
0
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"]))
Esempio n. 10
0
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"]))
Esempio n. 11
0
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"))
Esempio n. 12
0
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"))
Esempio n. 13
0
def content(iuid, ext):
    "Fetch or update the content of the dataset as JSON or CSV file."
    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)
        if not dataset.get("_attachments", None):
            return "", http.client.NO_CONTENT
        if ext == "json":
            outfile = flask.g.db.get_attachment(dataset, "data.json")
            response = flask.make_response(outfile.read())
            response.headers.set("Content-Type", constants.JSON_MIMETYPE)
        elif ext == "csv":
            outfile = flask.g.db.get_attachment(dataset, "data.csv")
            response = flask.make_response(outfile.read())
            response.headers.set("Content-Type", constants.CSV_MIMETYPE)
        else:
            flask.abort(http.client.NOT_FOUND)
        return response

    elif utils.http_PUT():
        if not allow_edit(dataset):
            flask.abort(http.client.FORBIDDEN)
        if ext == "json":
            content_type = constants.JSON_MIMETYPE
        elif ext == "csv":
            content_type = constants.CSV_MIMETYPE
        else:
            flask.abort(http.client.NOT_FOUND)
        try:
            with DatasetSaver(dataset) as saver:
                saver.set_data(io.BytesIO(flask.request.data), content_type)
        except ValueError as error:
            return str(error), http.client.BAD_REQUEST
        return "", http.client.NO_CONTENT