Managing access control in a CRM system might seem straightforward at first glance: sales reps see their opportunities, managers see their teams' opportunities, and executives see everything. However, once you start dealing with real-world organizational structures and business requirements, the complexity grows exponentially. Let's explore why this happens and how modern authorization tools like Oso can help tame this complexity.
Sales organizations typically follow hierarchical structures that extend beyond simple manager-report relationships. Consider these common patterns:
- Geographic hierarchies (Global → Region → Country → State)
- Account hierarchies (Enterprise Accounts → Subsidiary Companies)
- Sales team hierarchies (Chief Revenue Officer → Regional VP → District Manager → Account Executive)
Each of these hierarchies influences who should have access to which opportunities in your CRM. For example, a Regional VP for EMEA needs access to all opportunities in their region, regardless of which individual sales rep owns them.
Traditional Approaches and Their Limitations
The conventional approach to handling authorization logic often involves directly manipulating SQL queries, such as:
SELECT * FROM opportunities WHERE
owner_id = $<userId> OR
territory_id IN (SELECT territory_id FROM territory_assignments WHERE user_id = $<userId>) OR
account_id IN (SELECT account_id FROM account_assignments WHERE user_id = $<userId>)
This approach quickly becomes unwieldy as you add more conditions and hierarchical relationships. It also creates maintenance headaches when business rules change and is prone to security holes when new access patterns are introduced.
Enter Recursive Authorization with Oso
Modern authorization tools like Oso provide a more elegant solution through declarative rules that support recursive relationships. Here's how you might express the same logic:
actor User {}
resource Account {
roles = ["assigned"];
}
resource Territory {
roles = ["assigned"];
relations = {
ancestor: Territory
};
"assigned" if "assigned" on "ancestor";
}
resource Opportunity {
permissions = ["read"];
relations = {
owner: User,
territory: Territory,
account: Account,
};
"read" if "owner";
"read" if "assigned" on "territory";
"read" if "assigned" on "account";
}
In your application, you can then easily retrieve all Opportunity
entries that a User
has the "read"
permission on using list filtering.
While this requires more lines of code, it offers several advantages over embedding authorization in SQL queries.
Natural Expression of Business Logic
The rules read almost like English sentences, making them easier to understand and maintain. Business rules are expressed directly rather than being buried in complex SQL queries or nested if-statements.
Hierarchical Relationships Made Simple
The recursive nature of the Territory
resources elegantly handles nested hierarchies of any depth. When a sales leader is responsible for EMEA, they automatically get access to all opportunities in all subregions (Western Europe, UK, London, etc.) without explicitly listing each one.
Separation of Concerns
Authorization logic lives in its own domain, separate from your application code. This makes it easier to audit, modify, and extend without touching your core business logic.
For example, if you want to introduce new features such as temporary assignments to accounts or territories to grant the "read"
permission on Opportunity
entries, you can modify the policy, and your current endpoints will automatically support the new cases.
Full-Featured Example
For those interested in a more complex and detailed implementation, Oso provides a CRM reference application using PostgreSQL and Node.js + React.
This example showcases a powerful new Oso feature that isn't yet generally available: recursion in local authorization queries.
While you won't be able to run the application immediately, you can join our beta program to try it out. Simply reach out to us in our community Slack.
With that caveat out of the way, you can view the code on GitHub.
Conclusion
Complex authorization requirements don't have to result in complex code. By leveraging modern authorization tools that support recursive relationships, you can build maintainable, secure, and performant access control systems that accurately reflect your organization's structure and needs.
The key is to choose tools and approaches that embrace the inherently hierarchical nature of sales organizations rather than fighting against it.
Tools like Oso provide the building blocks needed to express these relationships naturally while maintaining security and supporting future feature development.
Have questions about building complex CRM apps with Oso? Schedule a call with one of our team members or join our community Slack.