The Hidden Complexity of CRM Authorization

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.

Want us to remind you?
We'll email you before the event with a friendly reminder.

Write your first policy