In the process of turning most web projects into browser applications, many questions arise. And one of the most significant of them is the processing of access rights without unnecessary code. Reflections on this topic led us to a big problem: there is simply no comfortable way to implement protection at the field level of a model for ActiveRecord (
Egor , hello!;). CanCan adds limitations at the controller level, but this is too high to solve all the problems.
After giving up a little, we wrote two lovely hemes. Meet
Heimdallr (Heimdal) and its extension
Heimdallr :: Resource . They will bring peace and security to your models.
Heimdallr
Let's first look at the problem more deeply. A huge part of projects really equates security with access control of REST controllers. Large projects often go down to models, so as not to duplicate the code. And so that the number of actions in the controllers does not become unbearably large, they sometimes go down to control access to the fields.

')
For many RESTful applications, the 1st and 2nd levels are identical. Therefore, in the bottom line with us:
- Access to models
- Access to model fields
At the same time, the importance of controlling access to fields is growing rapidly with the growth of the project. And the recent Github defamation example is a vivid example of the consequences of the “Fields? But who cares! ”
Here is an example of how Heimdallr can help with this:
class Article < ActiveRecord::Base include Heimdallr::Model belongs_to :owner, :class_name => 'User' restrict do |user, record| if user.admin?
Using a simple DSL inside the models, we declare both access restrictions to the models themselves and their fields. Heimdallr extends the models that use it with the
.restrict
method. Calling this method will wrap the model class in a proxy wrapper, which can be used completely transparently.
Article.restrict(current_user).where(:typical => true)
Note that for
Class.restrict
calls, the second block parameter will be nil. Therefore, all checks that depend on the state of the fields of the current object should be wrapped in
.try(:field)
.
These restrictions can be used anywhere in the project, not only in controllers. And this is important. If you try to read a protected field - an exception. This behavior is predictable, but it is not very convenient for the design of views.
To solve a problem with views, Heimdallr implements two strategies, explicit and implicit. By default, Heimdallr will follow an explicit behavior pattern. But the alternative behavior:
article = Article.restrict(current_user).first @article = article.implicit @article.protected_thing
OK. At the beginning of the article I mentioned CanCan. But doesn't he solve the problem in a fundamentally different way?
CanCan
For many Rails projects, the term “Security” is a synonym for the CanCan gem. CanCan really was a whole epoch and still works great. But he has a number of problems:
- CanCan was conceived as a tool that does not work with models. It offers an architecture in which REST controllers are protected, and the attacker simply will not reach the models. Sometimes this strategy is good, sometimes not. But the fact is that you cannot get to the fields, no matter how hard you try. CanCan simply does not know and can not know anything about the fields.
- Branch 1.x is dead and not supported. There are several unpleasant bugs in it that prevent you from working with namespacs in difficult cases. A branch 2.x developed prohibitively long.
We began to develop Heimdallr as a model control tool, but in practice it turned out that we have enough data to limit controllers. Therefore, we took and wrote Heimdallr :: Resource.
This part of Heimdallr is mimicking CanCan as much as possible. You have the same
load_and_authorize
filter and this is how it works:
- If for the current context the create: loop is not declared (and therefore you cannot create entities), then you cannot be in new and create
- If you do not have a scop: update, you can not edit and update.
- A similar approach for scoop: destroy
- In actions, you immediately get a protected entity, and therefore you can not forget to manually call
restrict
It looks like this:
class ArticlesController < ApplicationController include Heimdallr::Resource load_and_authorize_resource
REST-API Providers
At the beginning of the story, I talked about the root of the idea, the synchronization of access rights between the client application and the server REST-API. And here are the conventions we finally came to.
Imagine that you have a simple CRUD interface with roles that you need to implement as a client JS application. At the same time on the server you have REST with index / create / update / destroy. Permissions ask the following questions:
- What entities can I get through the index?
- Which ones can I change?
- Which ones can I remove?
- Can I create a new entity?
- What fields can I set when updating?
- What fields can I set when creating?
The first question is solved by Heimdallr by nature. You just determined the desired skoup and all. No one just sees anything extra. Regarding the rest. In my last article, I described how we render JSON representations for REST providers. Using the same technique, the view is very easy to expand with the following fields:
{modifiable: self.modifiable?, destroyable: self.destroyable?}
Can I create? And with what fields?
For the REST API, the new method is practically useless. And this is a great place to determine if we can create something and what exactly. For example:
Article.restrictions(current_user).allowed_fields[:create]
If we cannot create at all, Heimdallr :: Resource will respond to this request with an error. Otherwise, we will get a list of fields available for filling.
.creatable?
Heimdal also declare the
.creatable?
method
.creatable?
, so that it can be thrown through REST.
Can I update?
The idea is similar to creation. Only this time we will declare the edit method:
Article.restrictions(current_user).allowed_fields[:update]
Finally
Using Heimdallr and its extension, Heimdallr :: Resource, will help you easily manage access rights without unnecessary garbage in the code. And, importantly, you get extra magic for your REST-API. Remember,
Khomyakov is watching you!
ಠ_ಠ