Exemplo n.º 1
0
def delete_file(filename: str):
    """Deletes a file with a given filename.
    
    This runs a check on the user's token to see if they're allowed to delete the file."""

    user = utils.check_user(token=request.headers.get("Authorization"))

    if user is None:
        return utils.respond(code=403, msg="Invalid API token.")

    file = utils.first(iterable=cache["files"],
                       condition=lambda file: file.key == filename)

    if file is None:
        return utils.respond(code=404, msg="File not found.")

    # ========================================================
    # - User isn't admin and isn't trying to delete their file
    # - Both admin but user is not superuser
    # ========================================================
    if (not user.admin and user.id != file.owner.id) \
       or (user.admin and file.owner.admin and user.id != file.owner.id and user.id != const.superuser.id):
        return utils.respond(code=403, msg="You don't own this file.")
    with postgres.cursor() as con:
        query = """DELETE FROM files
                    WHERE key = %(key)s;"""

        con.execute(query, dict(key=filename))

    os.remove(path=f"static/uploads/{filename}")
    cache["files"].remove(file)

    return utils.respond(code=200, msg="File has been deleted.")
Exemplo n.º 2
0
def delete_url(url_key: str):
    """Deletes a URL with a given key.
    
    This runs a check on the user's token to see if they're allowed to delete the file."""

    user = utils.check_user(token=request.headers.get("Authorization"))

    if user is None:
        return utils.respond(code=403, msg="Invalid API token.")

    found_url = utils.first(iterable=cache["urls"],
                            condition=lambda url: url.key == url_key)

    if found_url is None:
        return utils.respond(code=404, msg="URL not found.")

    # ========================================================
    # - User isn't admin and isn't trying to delete their file
    # - Both admin but user is not superuser
    # ========================================================
    if (not user.admin and user.id != found_url.owner.id) \
       or (user.admin and found_url.owner.admin and user.id != found_url.owner.id and user.id != const.superuser.id):
        return utils.respond(code=403, msg="You don't own this URL.")

    with postgres.cursor() as con:
        query = """DELETE FROM urls
                   WHERE key = %(key)s;"""

        con.execute(query, dict(key=url_key))

    cache["urls"].remove(found_url)

    return utils.respond(code=200, msg="URL has been deleted.")
Exemplo n.º 3
0
def shorten_url():
    """Takes a URL and shortens it. This is useful for particularly long URLs like Google Form links."""

    user = utils.check_user(token=request.headers.get("Authorization"))

    if user is None:
        return utils.respond(code=403, msg="Invalid API token.")

    if not request.is_json:
        return utils.respond(code=403, msg="Missing JSON data.")

    to_shorten = request.json.get("url")

    if to_shorten is None:
        return utils.respond(code=403, msg="Missing JSON data.")

    found_url = utils.first(iterable=cache["urls"],
                            condition=lambda url: url.url == to_shorten)

    if found_url:
        return utils.respond(
            code=409,
            msg="That URL has already been shortened.",
            link=
            f"https://{request.url_root.lstrip('http://')}u/{found_url.key}")

    if match(pattern=URL_REGEX, string=to_shorten) is None:
        return utils.respond(code=403, msg="That URL is invalid.")

    key = request.headers.get("URL-Name")

    if not (user.admin
            and config.url_shortening.custom_url.admin_only) or not key:
        key = utils.generate_key(cache_obj="urls")

    with postgres.cursor() as con:
        query = """INSERT INTO urls (owner_id, key, url, created_at)
                   VALUES (%(owner_id)s, %(key)s, %(url)s, %(created_at)s)
                   
                   RETURNING id;"""

        con.execute(
            query,
            dict(owner_id=user.id,
                 key=key,
                 url=to_shorten,
                 created_at=datetime.utcnow()))

        url_id = con.fetchone()[0]
        url_obj = URL(id=url_id,
                      key=key,
                      url=to_shorten,
                      created_at=datetime.utcnow(),
                      owner=user)

        cache["urls"].insert(0, url_obj)

    return f"https://{request.url_root.lstrip('http://')}u/{key}", 200
Exemplo n.º 4
0
def get_link(link: str):
    """Redirects a user to a shortened URL if it exists."""

    found_url = utils.first(iterable=cache["urls"],
                            condition=lambda url: url.key == link)

    if found_url is None:
        abort(status=404)

    return redirect(location=found_url.url, code=303), 303
Exemplo n.º 5
0
def authenticate():
    """Queries the provided username and password to fetch a user."""

    if not request.is_json:
        return utils.respond(code=422, msg="Missing JSON data.")

    username = request.json.get("username")
    password = request.json.get("password")

    if None in (username, password):
        return utils.respond(code=422, msg="Missing JSON data.")

    if username == const.superuser.username and password == const.superuser.password:
        return utils.respond(**dict(const.superuser))

    user = utils.first(iterable=cache["users"],
                       condition=lambda user: user.username == username and
                       user.password == password)

    if user is None:
        return utils.respond(code=404, msg="User not found.")

    return utils.respond(**dict(user))
Exemplo n.º 6
0
def get_file(filename: str):
    """Gets and returns an file if it exists."""

    path = f"static/uploads/{filename}"

    if not os.path.exists(path):
        abort(status=404)

    file_type = utils.filetype(filename=filename)

    if file_type == "gif":
        file_type = "image"

    if file_type == "download":
        return send_file(filename_or_fp=path, as_attachment=True)

    if file_type == "text":
        with open(file=path) as file:
            content = file.read()

        return render_template(
            template_name_or_list="files/text.html",
            file=utils.first(iterable=cache.files,
                             condition=lambda f: f.key == filename),
            config=config.meta,
            content=content,
            size=utils.bytes_4_humans(count=os.path.getsize(filename=path)))

    if file_type == "code":
        with open(file=path) as file:
            content = file.read()

        file_ext = utils.filext(filename=filename)
        lang = {
            "py": "python",
            "js": "javascript",
            "go": "go",
            "ts": "typescript",
            "cpp": "cpp",
            "html": "html",
            "css": "css",
            "json": "json"
        }.get(file_ext)

        return render_template(
            template_name_or_list="files/code.html",
            file=utils.first(iterable=cache.files,
                             condition=lambda f: f.key == filename),
            config=config.meta,
            content=markdown(f"```{lang}\n{content}\n```"),
            size=utils.bytes_4_humans(count=os.path.getsize(filename=path)),
            lang=lang,
            lang_ext=file_ext)

    if file_type == "markdown":
        with open(file=path) as file:
            content = file.read()

        return render_template(
            template_name_or_list="files/markdown.html",
            file=utils.first(iterable=cache.files,
                             condition=lambda f: f.key == filename),
            config=config.meta,
            content=markdown(content),
            size=utils.bytes_4_humans(count=os.path.getsize(filename=path)))

    return render_template(
        template_name_or_list=f"files/{file_type}.html",
        file=utils.first(iterable=cache.files,
                         condition=lambda f: f.key == filename),
        config=config.meta,
        size=utils.bytes_4_humans(count=os.path.getsize(filename=path)))
Exemplo n.º 7
0
    def postgres_init(self):
        """Initialises the connection to the PostgreSQL server."""

        constants.postgres = connect(
            dsn=
            "user={pg.user} password={pg.password} host={pg.host} port={pg.port} dbname={pg.database}"
            .format(pg=config.postgres))
        console.info(
            text=
            "Connected to Postgres server at: {pg.user}@{pg.host}/{pg.database}"
            .format(pg=config.postgres))
        constants.postgres.set_session(autocommit=True)

        # =================================
        # Create the required schema tables
        # =================================
        with constants.postgres.cursor() as con:
            queries = (
                """CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, username TEXT UNIQUE, password TEXT, admin BOOLEAN, token TEXT, created_at TIMESTAMP);""",
                """CREATE TABLE IF NOT EXISTS files (id SERIAL PRIMARY KEY, owner_id INT, key TEXT UNIQUE, deleted BOOLEAN, created_at TIMESTAMP);""",
                """CREATE TABLE IF NOT EXISTS urls (id SERIAL PRIMARY KEY, owner_id INT, key TEXT UNIQUE, url TEXT, created_at TIMESTAMP);"""
            )

            for query in queries:
                con.execute(query)
                constants.postgres.commit()

            # =================================
            # Populate file, url and user cache
            # =================================
            console.verbose(text="Beginning cache population...")
            console.verbose(text="Populating user cache...")
            query = """SELECT id, username, password, admin, token, created_at
                       FROM users
                       ORDER BY id ASC;"""

            con.execute(query)

            for user in con.fetchall():
                cache["users"].append(
                    User(id=user[0],
                         username=user[1],
                         password=user[2],
                         admin=user[3],
                         token=user[4],
                         created_at=user[5]))
                console.verbose(
                    text=f"Populated cache for user {user[0]} ({user[1]}).")

            console.verbose(text="Populating file cache...")
            query = """SELECT id, owner_id, key, deleted, created_at
                       FROM files
                       ORDER BY created_at DESC;"""

            con.execute(query)

            for file in con.fetchall():
                cache["files"].append(
                    File(id=file[0],
                         key=file[2],
                         deleted=file[3],
                         created_at=file[4],
                         owner=utils.first(
                             iterable=cache["users"],
                             condition=lambda user: user.id == file[1])))
                console.verbose(
                    text=f"Populated cache for file {file[0]} ({file[2]}).")

            console.verbose(text="Populating URL cache...")
            query = """SELECT id, owner_id, key, url, created_at
                       FROM urls
                       ORDER BY created_at DESC;"""

            con.execute(query)

            for url in con.fetchall():
                cache["urls"].append(
                    URL(id=url[0],
                        key=url[2],
                        url=url[3],
                        created_at=url[4],
                        owner=utils.first(
                            iterable=cache["users"],
                            condition=lambda user: user.id == url[1])))
                console.verbose(
                    text=f"Populated cache for url {url[0]} ({url[2]}).")