It is helpful to distinguish two types of access control:
- Vertical - functions that some users can access and some users cannot. For example, anyone can view the home page, but only admin can ban a user.
- Horizonal - functions that multiple users can access, but the data is segregated. For example, everyone can access "inbox" - but they see only their own messages, not other people's.
Vertical access control is almost always enforced at the controller layer.
Most applications also enforce horizontal access control at the controller layer. This works, but there is a disadvantage: access checks need to be repeated in multiple places, which invites mistakes. Complex applications need a lot of checks to properly enforce horizontal access control.
But there is an alternative: perform horizontal access control in the model. This tends to reduce the number of checks needed - e.g. you have an access control check on the Email object, rather that 15 separate checks in controller methods that access the object. One criticism of this approach is that is requires the model to be identity-aware, which could be unwanted coupling between model and controller. However, I think it's worth it. I've written more about this topic on my website.