-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #48 from MITLibraries/tco-30-authorization-adr
Add ADR to select an authorization gem
- Loading branch information
Showing
1 changed file
with
106 additions
and
0 deletions.
There are no files selected for viewing
106 changes: 106 additions & 0 deletions
106
docs/architecture-decisions/0005-use-cancancan-for-authorization.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# 5. Use CanCanCan for authorization | ||
|
||
Date: 2024-06-25 | ||
|
||
## Status | ||
|
||
Accepted | ||
|
||
## Context | ||
|
||
We will need authorization in TACOS to support the staff user interface. Specifically, we will need to manage access | ||
to the following features: | ||
|
||
* The categorization interface. This is one of the core features of TACOS, in which staff will match logged search | ||
terms to categories. | ||
* The 'Hints' system. TACOS will provide a dashboard to manage Hints, which are currently maintained in a Google | ||
spreadsheet. | ||
* Reporting dashboard. Initially, TACOS will provide monthly statistics on matched algorithms. We will likely add | ||
more reporting functionality in the future. | ||
|
||
The authorization gem we select should also be able to support features beyond those listed above, as we are not sure | ||
what TACOS might become in the future. | ||
|
||
## Options considered | ||
|
||
For the purpose of this ADR, I considered three popular authorization gems. These are not the only available options, | ||
but they are widely adopted and well supported. | ||
|
||
### CanCanCan | ||
|
||
[CanCanCan](https://github.com/CanCanCommunity/cancancan) is the gem we are familiar with, having most recently used it | ||
in [ETD](https://github.com/MITLibraries/thing/blob/main/app/models/ability.rb). It differs from the other two gems | ||
in its use of roles rather than policies, and a single `Ability` model to manage all roles. | ||
|
||
This approach, while less object-oriented, makes for a simpler file structure. I also find it easier to understand | ||
authorization when defined as roles, particularly if I can see all roles alongside each other in the same model. I have | ||
a bias here due to my familiarity with CanCanCan, but all of us in EngX share that familiarity. | ||
|
||
However, there is a risk of code smell in CanCanCan if we end up with complex authorization logic. The consolidation of | ||
authorization logic can lead to an expansive `Ability` model, which can make for difficult readability and | ||
maintainability. | ||
|
||
### Pundit | ||
|
||
[Pundit](https://github.com/varvet/pundit) is probably the most popular alternative to CanCanCan. In Pundit, we create | ||
policy classes, each of which usually corresponds to a model. Within a given policy are several query methods that | ||
correspond to different controller methods. | ||
|
||
For example, the `HintPolicy` class might have a `update?` method, which would define the conditions under which a user | ||
could update a hint record. We would then invoke this authorization in the Hint controller by adding `authorize @hint` | ||
to `HintController#update`. (See the [Pundit readme](https://github.com/varvet/pundit?tab=readme-ov-file#policies) | ||
for a more detailed example.) | ||
|
||
Pundit also allows for more granular access control via | ||
[scopes](https://github.com/varvet/pundit?tab=readme-ov-file#scopes). This feature is the most compelling reason to | ||
choose Pundit over CanCanCan, but it's unclear whether we would need it in this application. | ||
|
||
Because it requires a separate policy class per model, Pundit can require some repetitive code if authorization | ||
policies are similar across multiple models. We might be able to mitigate this by abstracting common logic to the | ||
`ApplicationPolicy` from which all policy classes inherit. | ||
|
||
### Action Policy | ||
|
||
[Action Policy](https://github.com/palkan/action_policy) is the newest of the gems considered here, but still | ||
well-established (its first release was in 2018). It is based on Pundit and uses very similar conventions. The main | ||
differences are that Action Policy offers better performance, largely via | ||
[caching](https://actionpolicy.evilmartians.io/#/./caching). | ||
|
||
Besides that, it seems to be a more customizable version of Pundit. That customizability is likely helpful in an | ||
application that requires very complex authorization logic, but I don't expect TACOS to have that need. | ||
|
||
## Ease of documentation | ||
|
||
Because we are considering generated documentation in TACOS, an additional criterion is whether any of these gems is | ||
better suited to this than the others. I am not familiar with RDoc or YARD, but it seems like the object-oriented nature | ||
of Pundit and Action Policy would yield better generated docs. However, descriptive comments and role names in a | ||
CanCanCan `Ability` model may also be adequate. | ||
|
||
## Decision | ||
|
||
We will use CanCanCan for authorization. | ||
|
||
Action Policy seems to have more features than are necessary for TACOS. Unless our authorization logic becomes far more | ||
complex than I anticipate, or we have performance issues related to authorization, it is probably not the best choice | ||
for this application. It does seem like an intriguing option, though, and something we should consider for future use | ||
cases. | ||
|
||
This leaves Pundit as the alternative. The key advantages of Pundit over CanCanCan are its OO design and its use of | ||
scopes to provide more granular control. It does seem to be more Ruby-ish, and if we were starting from zero with no | ||
previous experience in authorization, it would probably be my choice. | ||
|
||
In this case, though, we are already familiar with CanCanCan. This familiarity, combined with its relative simplicity, | ||
gives it the edge in an application that should have fairly straightforward authorization. | ||
|
||
## Consequences | ||
|
||
If our authorization logic becomes more complex than expected, then we risk a bloated `Ability` model. This could cause | ||
readability and maintenance challenges. | ||
|
||
We may also find, after developing TACOS further, that scoped authorization would be useful, | ||
or even necessary. In that case, we might consider migrating our authorization logic to either Pundit or Action Policy. | ||
While CanCanCan does offer a | ||
[similar feature](https://github.com/CanCanCommunity/cancancan/blob/develop/docs/define_abilities_with_blocks.md#block-conditions-with-activerecord-scopes), it's a bit clunky and not as intuitive (to my eyes) as Pundit's convention. | ||
|
||
By not selecting Action Policy, we are forgoing its performance gains. This feels like an acceptable risk, as I am | ||
unaware of any performance issues we've encountered with CanCanCan. |