# Who Gets In

> When the roster changes, does access follow?

Canonical URL: <https://datadriven.io/problems/who_gets_in>

Domain: Python · Difficulty: medium · Seniority: L5

## Problem

A permission service replays a stream of access changes: `grant` and `revoke` add or drop a single permission for one user, `check` asks whether a user currently holds a given permission, and `update_config` hands over a whole user-to-permissions mapping that replaces every permission on record. Most operations name an action, a user, and a permission, but `update_config` carries only the action and the mapping. Return the boolean answer to each `check` in order, and treat a user you have never seen as holding nothing.

## Worked solution and explanation

### What this problem really is

This is variable-arity command dispatch hiding a state-consistency trap. Everyone can maintain a dict of permission sets and branch on grant/revoke/check. What separates candidates is noticing that `update_config` is only two elements long, so a loop that reaches for `op[2]` on every operation crashes with IndexError the moment the first config push arrives. Dispatch on the action first, unpack second.

---

### Break down the requirements

#### Step 1: Maintain a set of permissions per user

Keep a dict mapping each user to a set of their current permissions. Sets make grant idempotent, revoke idempotent, and check an O(1) membership test.

#### Step 2: Dispatch on the action before unpacking

Operations are not all the same length, so read op[0] FIRST. The 'update_config' op has only 2 elements (action plus a dict), so reading op[2] on it would raise IndexError. Handle it before touching user/permission fields.

#### Step 3: Process each operation in order

grant adds to the set, revoke discards from the set (never remove, which throws on a second revoke), check tests membership (False if the user is unknown), and update_config rebuilds the entire permissions dict from the config, converting each permission list to a set.

#### Step 4: Collect check results

Append the boolean from every check to a results list and return it at the end, in operation order.

---

### The solution

**Set-based permission tracking with arity-aware dispatch**

```python
def permissions_manager(operations):
    permissions = {}
    results = []
    for op in operations:
        action = op[0]
        if action == 'update_config':
            config = op[1]
            permissions = {u: set(perms) for u, perms in config.items()}
            continue
        user = op[1]
        perm = op[2]
        if action == 'grant':
            permissions.setdefault(user, set()).add(perm)
        elif action == 'revoke':
            permissions.setdefault(user, set()).discard(perm)
        elif action == 'check':
            results.append(perm in permissions.get(user, set()))
    return results
```

> **Time and Space Complexity**
>
> **Time:** O(n + k) where n is the number of operations and k is the total size of any config payloads. Each grant/revoke/check is O(1) average.
> 
> **Space:** O(u * p) where u is the number of users and p is the average number of permissions per user.

> **Interviewers Watch For**
>
> Whether you branch on the action BEFORE indexing op[1]/op[2]. The 'update_config' op has only 2 elements, so a loop that blindly reads op[2] crashes with IndexError. Strong candidates dispatch first, then unpack.

> **Common Pitfall**
>
> Assuming every operation is a 3-tuple and reading op[2] unconditionally. The 2-element update_config op then raises IndexError. Also: using set.remove() for revoke (raises KeyError on a double-revoke) instead of the idempotent set.discard().

---

## Common follow-up questions

- What if permissions should support hierarchical inheritance? _(Tests building a permission tree where granting 'admin' implies 'read' and 'write'.)_
- How would you change update_config to merge rather than replace? _(Tests merging the config into existing permissions instead of replacing wholesale.)_
- What if operations arrive concurrently from multiple threads? _(Tests knowledge of thread-safe data structures or locking strategies.)_
- How would you audit the permission history? _(Tests appending each operation to an audit log alongside the state change.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/who_gets_in)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). DataDriven is the data engineering interview community. Live code execution in SQL, Python, and Spark sandboxes. Every feature is open to every member.