📜 ⬆️ ⬇️

Heimdallr: field model protection and the new CanCan

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:

  1. Access to models
  2. 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? #      scope :fetch scope :delete can [:view, :create, :update] else #      -  scope :fetch, -> { where('owner_id = ? or secrecy_level < ?', user.id, 5) } scope :delete, -> { where('owner_id = ?', user.id) } # ...        # (    )... if record.try(:owner) == user can :view can :update, { secrecy_level: { inclusion: { in: 0..4 } } } else can :view cannot :view, [:secrecy_level] end # ...      ,   . can :create, %w(content) can :create, { owner_id: user.id, secrecy_level: { inclusion: { in: 0..4 } } } end end end 

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 # => nil 

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:


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:


It looks like this:

 class ArticlesController < ApplicationController include Heimdallr::Resource load_and_authorize_resource #    : # # load_and_authorize_resource :resource => :article #  : # # routes.rb: # resources :categories do # resources :articles # end # # load_and_authorize_resource :through => :category def index # @articles   restrict' end def create # @article   restrict' end end 

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:

  1. What entities can I get through the index?
  2. Which ones can I change?
  3. Which ones can I remove?
  4. Can I create a new entity?
  5. What fields can I set when updating?
  6. 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!

ಠ_ಠ

Source: https://habr.com/ru/post/141282/


All Articles