🕵️ Reverse EngineeringHow not to solve Jane Street's ASIC puzzle. Kinda.(17.09.2026 um 21:27 Uhr)
🔧 ProgrammierungHTMX is fine until the third stakeholder wants a modal(17.09.2026 um 21:13 Uhr)
🕵️ Reverse EngineeringHow not to solve Jane Street's ASIC puzzle. Kinda.(17.09.2026 um 21:27 Uhr)
🔧 ProgrammierungHTMX is fine until the third stakeholder wants a modal(17.09.2026 um 21:13 Uhr)
🔧 Programmierung 🕛 vor 4 Monaten 8 Min Lesezeit
0

Taking Permissions a Step Further in Node.js (The Fall of Spaghetti Code)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

So you scaffolded a blog post and handled permissions in a clean way…



Perhaps…




CODE
const isAllowedToUpdate = user.id === author.id || user.role;

const BlogPost = () => {
return (
<BlogPost>
{isAllowedToUpdate && <EditBlogPost />}
</BlogPost>
);
};






Shocker: That was a vulnerable piece of code right there.



It shows how fragile randomly handling permissions with if/else can be.



One little oversight and you're breaking a costly business logic.



So let's fix that.



Let's be more maintainable, reusable, and scalable.



That's the purpose of this article.



Let's get right in.









The Very Basics



I was recently the backend developer for a project that involved 4 roles:




  • Pharmacy

  • Customer

  • Consultant

  • Driver



For the sake of clarity, I'll reduce the resources involved to just 3:




  • Inventory

  • Medical records

  • Deliveries



So here's the basics of the relationship.



Customers can:




  • Read all inventories

  • Read only Medical records assigned to them

  • Create orders

  • Read only orders they create.



Pharmacies can:




  • Create inventories

  • Read inventories, but only ones they own

  • Update inventories, but only ones they own

  • Delete inventories, but only ones they own



Consultants can:




  • Create medical records

  • Read only medical records they own

  • Update only medical records they own

  • Delete only medical records they own



Drivers can:




  • Read deliveries assigned to them



You may also notice something in addition to all this: consultants have no business with inventory, pharmacies have no business with deliveries, and drivers have no business with medical records — and so on.









The Fast Way



In an Express project, you could quickly put something like this together:




CODE
if (user.role === "customer") {
// ...
}

if (user.role === "consultant" && medicalRecord.consultantId === user.id) {
// ...
}






And even better, with middleware.



and have something as clean as this:




CODE
authorize(["consultant", "pharmacy"]);






This restricts a particular route from being accessed by any role other than those passed in.



This solves a lot of problems — it protects routes from being accessed by other roles, and quickly narrows down the scope of concern.









The Rise of Spaghetti Code



Think about a situation where the resource in question is an "order" which a customer can only create (not update or delete), a pharmacy can only read if assigned to them, a driver can only read if assigned to them, and a consultant can only read if assigned to them.



My dear brothers and sisters…



You're ending up with:




CODE
authorize(["customer", "consultant", "driver", "pharmacy"]);






Whereas you only needed each of these roles to just read "orders" assigned to them.



In a NestJS project, at this point, there's no point calling a roles guard (the Express equivalent).



Just protect the endpoint (controller) generally and move the role logic to the service.



And my dear brothers and sisters, this is how you end up with this...




CODE
if (user.role === "consultant") {
if (record.consultantId === user.id) {
// allow

}
} else if (user.role === "pharmacy") {
// ...
} else if (user.role === "driver"






Repeated 20 different places.



This is how you have permission logic bleeding directly into the route handlers.



A spaghetti code.








CODE
import { AbilityBuilder, Ability } from '@casl/ability';
import { InventoryPolicy } from './policies/inventory.policy';
import { MedicalPolicy } from './policies/medical.policy';
// ... import other policies

export function createAbilitiesForUser(user) {
const builder = new AbilityBuilder(Ability);

// We pass the user and the builder's methods to each policy
InventoryPolicy(user, builder);
MedicalPolicy(user, builder);
// Add as many as you need...

return builder.build();
}






An ability factory is used in CASL to centralize and dynamically generate a user's permissions based on their identity or role.



It centralizes all authorization rules to live in one single file.



In our case, we had to put our policies inside.



And it eventually finds a way to build a central policy with which our app (across files) works with.



For your Express project, your middleware could then look like this:




CODE
const checkPermission = (action, subject) => {
return (req, res, next) => {
const ability = defineAbilitiesFor(req.user);

if (ability.can(action, subject)) {
return next();
}

return res.status(403).json({ message: "Forbidden" });
};
};






You see.



Apt.



And our route becomes as simple as:




CODE
router.post('/inventory', checkPermission('create', 'Inventory'), (req, res) => { ... });






Beautiful.



Independent of whatever goes on with our policy, what we change, and what we do.



All it knows to check is "who has the ability" to create inventory based on the created policy.



This allows for flexibility and reusability.



For instance, if we introduce an admin role tomorrow, all we have to do is add it to our policy — and everything still works perfectly.



Now, route protection alone isn't enough.



Let's also reuse this in our service:




CODE
// In your Service
async function updateInventory(user, inventoryId, updateData) {
const inventory = await db.inventory.find(inventoryId);
const ability = defineAbilitiesFor(user);

// ABAC logic happens here, away from the if/else mess
if (ability.cannot('update', inventory)) {
throw new ForbiddenException("You do not own this inventory record.");
}

return db.inventory.update(inventoryId, updateData);
}






And voila.



This solves every edge case.



For instance, you may notice in our inventory policy, we had something like this...




CODE
if (user.role === 'pharmacy') {
can('manage', 'Inventory', { pharmacyId: user.id });
}






This means pharmacies can manage inventory as long as they own it.



So when we do this...




CODE
router.post('/inventory', checkPermission('create', 'Inventory'), (req, res) => { ... });






...we're filtering the forbidden users at the route level.



Immediately.



They never get to the service.









The Upsides





  • Policies live in one place. Add a role, update the policy file. Done.


  • Controllers stay dumb. They declare what permission is needed, not how to evaluate it.


  • Services stay clean. They check ability against the actual object, not the user's role string.


  • Business logic stays consistent. Because it only exists once.



This is also similarly beautiful for NestJS projects.



If there are requests, I could make a NestJS implementation for this.






I'm a solo developer who's currently a free agent — openly looking for software engineering roles. My portfolio is at www.me.soapnotes.doctor.



Thank you so much!

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Microsoft gibt Fehler zu – Vorsicht! Windows-Update sperrt Nutzer vom PC aus - Heute.at
1 Quelle
How not to solve Jane Street's ASIC puzzle. Kinda.
1 Quelle
Revolut-Hacker fordern 6.000 Monero nach Datendiebstahl - Kryptorevolution
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Taking Permissions a Step Further in Node.js (The Fall of Spaghetti Code)

Thematisch verwandte Begriffe: Taking, Permissions, Step, Further · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...