Skip to content

Tutorial: Adding a blocklist

bovine claims to be modular. This means that it can be configured by adding code. In this tutorial, we will take a look at how to add a domain level block list to bovine store. This will lead all signed requests coming from an actor belonging to these servers to fail. In practice this means, that users on these instances can neither fetch content from bovine_store nor posts to the endpoints defined later.

The blocklist class

The following class defines our blocklist. It contains a list of domains to block. The check_retriever coroutine extracts the domain from the retriever and if container in the list, returns an unauthorized response.

from quart import g
from urllib.parse import urlparse

import logging

logger = logging.getLogger(__name__)

class Blocklist:
    def __init__(self):
        self.entries = ["mastodon.social"]

    async def check_retriever(self):
        if g.retriever:
            domain = urlparse(g.retriever).netloc

            if domain in self.entries:
                logger.warning("Blocked %s", g.retriever)
                return {"status": "unauthorized"}, 401

In practice, one will not want to hard code the blocklist. Either one can load one from a simple text file, containing a list of domains. Another option would to have a table in the database that contains records for known domains. These can then have a blocked status.

Similarly, one has access to the entire request here. So by changing the imports to

from quart import g, request

one can access the request headers with request.headers and so on.

Adding the blocklist

Adding the blocklist now to a bovine application with bovine_herd would look as follows

from quart import Quart

from bovine_herd import BovineHerd
from bovine_herd.server.authorize import add_authorization

from .blocklist import Blocklist

blocklist = Blocklist()

async def authorize():
    result = await add_authorization()
    if result:
        return result
    return await blocklist.check_retriever()

app = Quart(__name__)
BovineHerd(app, authorization_adder=authorize)

This is a somewhat minimal fully functional example.