Protection Proxy

Step 4: Protection Proxy

A proxy can also be a gatekeeper. A protection proxy enforces access rules before letting a call through, and a denied call must never reach the real service at all.

The proxy now takes an optional allowed set of keys. If allowed is None, everything is permitted (the earlier behavior). Otherwise, a fetch for a key outside the allowed set returns None immediately, no cache lookup, no service call, no lazy construction.

  • Proxy._is_allowed(key). True when allowed is None or the key is in the allowed set

Wire the check in first: fetch returns None for a denied key before doing anything else.

Hints
Starter code
class RealService:
    def __init__(self, backend):
        self.backend = dict(backend)
        self.calls = 0

    def fetch(self, key):
        self.calls += 1
        return self.backend.get(key)


class Proxy:
    def __init__(self, backend, allowed=None):
        self._backend = dict(backend)
        self._real = None
        self._cache = {}
        self._allowed = allowed

    def _ensure_real(self):
        if self._real is None:
            self._real = RealService(self._backend)

    def _is_allowed(self, key):
        """True when access is unrestricted or the key is explicitly allowed."""
        # TODO: return self._allowed is None or key in self._allowed
        pass

    def fetch(self, key):
        if not self._is_allowed(key):
            return None
        if key in self._cache:
            return self._cache[key]
        self._ensure_real()
        value = self._real.fetch(key)
        self._cache[key] = value
        return value
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.