Back in 2023, the CMMS (Computerized Maintenance Management System[^1]) I was working on needed a new system to manage its users' documents. The inherited legacy DMS (Document Management System) was getting exponentially slower. Meanwhile, the implementation was getting both harder to maintain and tweak to the needs of our fast-growing user base. An upcoming tender and the accompanying feature requests had put the matter at the top of the pile. Having a couple of weeks to make things happen — migration included — saved us from 'doing it the way it's supposed to be done'. We had to focus on solving actual problems over complying with vague general specs. Three years later, the implementation hasn't changed, even though the system, while not perfect, is under a continuously growing load, and registers north of 2 million documents. One such problem was access control. Access to documents — both visibility and editing rights — needed to be restricted. 1. A user can see the documents their company is allowed to 2. A user can see the documents their role permits 3. A company can share its documents with another Such fine-grained access control is traditionally solved using attribute-based access control (ABAC) patterns. While this is a fun problem to ponder in your spare time (who doesn't like to think about formal grammars?), it wasn't realistic for us to come up with a solution 'for all access control needs a company could ever have', given (1) our restricted needs and (2) our limited time. After asking around a bit, my SWAG[^2] was it was more than reasonable to expect permission granularity to stay at the role level (as opposed to the company or user level). Hence, we were able to solve the problem by introducing a single `viewer_ids` key on our document model.[^3] ```json { id: 1, viewer_ids: [] // <-- Behold ! } ``` It holds a list of all companies allowed to view the document, represented here by their unique identifiers. This means that isolating companies was now made possible by adding a simple filter on top of our requests to the database[^4]. It's like asking your company's archives to access all the documents you're authorized to view, all the documents contained in a specific folder that you're authorized to view, or all the documents matching a set of arbitrary criteria... that you're authorized to view. This is both trivial and crucial; this ensures the responsibility of maintaining the list falls onto the service using the DMS, not on the DMS itself. This way, ~~if~~ when a module changes its requirements for accessing the documents it stores, it is in charge of modifying its own use of the DMS, as opposed to applying changes to the DMS itself and piling them up on top of one another, much like a Jenga tower. Three years later, the logic to maintain the list of companies authorized to access documents has indeed seen drastic changes across almost all modules. The DMS, however, remains untouched to this day. The second issue related to access control that needed tackling was that of editing rights. This was probably one of the biggest business logic messes I had ever seen (emphasis on the past tense). Furthermore, no one could agree on a viable set of rules[^5]. I took the decision for the group, because I felt no amount of debating was going to move our understanding forward. Quite the contrary[^6]. My bet was that if something was wrong with the logic, it would be reported back to me promptly, and it would be on me to fix it anyway. Better that than solving the imaginary problems made up during each meeting. This might sound naïve or egotistic; however note I was not betting on being right, quite the opposite. I just felt the only way we could refine our understanding was to ship something, then fix it as we were obtaining new real-world information. I find that in practice, most of what you think of as problematic or important turns out not to be; and what is was rarely anticipated. So as a heuristic, I like to avoid solving problems I don't have, and I try not to put myself in a position where I can't solve the problems I will have. This translates as (1) betting on being wrong, not right, (2) keeping as much room to maneuver as possible, in order to (3) learn from reality as soon as I can. The simplest set of rules I could think of was: 1. Users should be able to edit the documents they uploaded themselves 2. Users with editing rights should be able to edit their company's documents That's it. And guess what? This set of rules hasn't changed one iota either. I'm glad we didn't solve all the problems we didn't have[^7]. Some implementation details could have been more polished, of course. In practice, though, it does the job to this day, we got the tender, and I continue to be surprised at the thought such a simple solution turned out to be so robust and durable. A bit of luck, a bit of thought, and a lack of options, I suppose. [^1]: A ticketing application for the maintenance industry. [^2]: Scientific Wild-Ass Guess [^3]: There are two difficult things in computer science: Cache invalidation, naming things, and off-by-one-errors. Translation: Don't judge my 2am self. [^4]: To ensure isolation, the system relies on the information encoded in the JWT used for authentication. [^5]: The business logic had been lost to the wheel of time (and its author that went MIA) [^6]: This reminds me of [[Meno's Paradox]]. If you could state what you're looking for before finding it, you wouldn't be looking for it in the first place. [^7]: Especially since I was in charge of actually solving them, cashing in on the checks the team was signing.