def disable(username):
    "Disable the given user account."
    user = get_user(username=username)
    if user is None:
        return utils.error("No such user.")
    if user["username"].lower() == flask.g.current_user["username"].lower():
        return utils.error("You cannot disable yourself.")
    with UserSaver(user) as saver:
        saver.set_status(constants.DISABLED)
    utils.get_logger().info(f"disabled user {username}")
    return flask.redirect(flask.url_for(".display", username=username))
def do_login(username, password):
    """Set the session cookie if successful login.
    Raise ValueError if some problem.
    """
    user = get_user(username=username)
    if user is None: raise ValueError
    if not check_password_hash(user["password"], password):
        raise ValueError
    if user["status"] != constants.ENABLED:
        raise ValueError
    flask.session["username"] = user["username"]
    flask.session.permanent = True
    utils.get_logger().info(f"logged in {user['username']}")
def enable(username):
    "Enable the given user account."
    user = get_user(username=username)
    if user is None:
        return utils.error("No such user.")
    if user["username"].lower() == flask.g.current_user["username"].lower():
        return utils.error("You cannot enable yourself.")
    with UserSaver(user) as saver:
        saver.set_status(constants.ENABLED)
    if user["password"][:5] == "code:" and \
       flask.current_app.config["MAIL_SERVER"]:
        send_password_code(user, "enabled")
    utils.get_logger().info(f"enabled user {username}")
    return flask.redirect(flask.url_for(".display", username=username))
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 am_admin_and_not_self(user):
                    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:
            return utils.error(error, flask.url_for(".password",
                                                    username=username,
                                                    code=code))
        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 edit(username):
    "Edit the user display. Or delete the user."
    user = get_user(username=username)
    if user is None:
        return utils.error("No such user.")
    if not am_admin_or_self(user):
        return utils.error("Access not allowed.")

    if utils.http_GET():
        deletable = am_admin_and_not_self(user) and is_empty(user)
        return flask.render_template("user/edit.html",
                                     user=user,
                                     change_role=am_admin_and_not_self(user),
                                     deletable=deletable)

    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):
            return utils.error("Cannot delete non-empty user account.")
        with flask.g.db:
            flask.g.db.execute("DELETE FROM logs WHERE docid=?",(user["iuid"],))
            flask.g.db.execute("DELETE FROM users "
                               " WHERE username=? COLLATE NOCASE",
                               (username,))
        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"))
def reset():
    "Reset the password for a user account and send email."
    if not flask.current_app.config["MAIL_SERVER"]:
        return utils.error("Cannot reset password; no email server defined.")
        
    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 register():
    "Register a new user account."
    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:
                    password = flask.request.form.get("password") or None
                    if password:
                        confirm = flask.request.form.get("confirm_password")
                        if password != confirm:
                            raise ValueError("Password differs from"
                                             " confirmed password.")
                    saver.set_password(password)
                    saver.set_status(constants.ENABLED)
                elif not flask.current_app.config["MAIL_SERVER"]:
                    password = flask.request.form.get("password") or None
                    if password:
                        confirm = flask.request.form.get("confirm_password")
                        if password != confirm:
                            raise ValueError("Password an confirmed password"
                                             " not the same.")
                    saver.set_password(password)
                else:
                    saver.set_password()
            user = saver.doc
        except ValueError as error:
            return utils.error(error)
        utils.get_logger().info(f"registered user {user['username']}")
        # Directly enabled.
        if user["status"] == constants.ENABLED:
            if user["password"][:5] == "code:":
                utils.get_logger().info(f"enabled user {user['username']}")
                # Send code by email to user.
                if flask.current_app.config["MAIL_SERVER"]:
                    send_password_code(user, "registration")
                    utils.flash_message("User account created; check your email.")
                # No email server: must contact admin.
                else:
                    utils.flash_message("User account created; contact"
                                        " the site admin to get the password"
                                        " setting code.")
            # 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 if email server defined.
        elif flask.current_app.config["MAIL_SERVER"]:
            admins = get_users(constants.ADMIN, status=constants.ENABLED)
            emails = [u["email"] for u in admins]
            site = flask.current_app.config["SITE_NAME"]
            message = flask_mail.Message(f"{site} user account pending",
                                         recipients=emails)
            url = utils.url_for(".display", username=user["username"])
            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.")
        else:
            utils.get_logger().info(f"pending user {user['username']}")
            utils.flash_message("User account created; admin will enable it"
                                " at some point. Try login later.")
        return flask.redirect(flask.url_for("home"))
def logout():
    "Logout from the user account."
    username = flask.session.pop("username", None)
    if username:
        utils.get_logger().info(f"logged out {username}")
    return flask.redirect(flask.url_for("home"))