1. Gem cancancan là gì?
- Gem cancancan là một thư viện phân quyền trong ruby và ruby on rails, nó hạn chế những tài nguyên mà một người dùng nhất định được phép truy cập.
- Tất cả các quyền được xác định trong một hoặc nhiều file ability và không bị trùng lặp trên controllers, views, và truy vấn database.
- Giúp dễ bảo trì và kiểm tra việc phân quyền logic.
2. Các chức năng chính
Bao gồm 2 chức năng chính:
- Thư viện authorizations cho phép định nghĩa các quy tắc để truy cập các đối tượng khác nhau và cung cấp helpers để kiểm tra các quyền đó.
- Rails helpers để đơn giản hóa code trong controllers bằng cách thực hiện việc tải và kiểm tra quyền của các models một cách tự động và giảm mã trùng lặp.
3. Cài đặt
Thêm vào Gemfile:
1 2 | gem "cancancan" |
Sau đó chạy lệnh sau:
1 2 | bundle install |
4. Định nghĩa Abilities
User permissions được xác định trong class Ability.
Tạo class Ability trong app/models/ability.rb bằng lệnh sau:
1 2 | rails g cancan:ability |
Ví dụ về các quy tắc để đọc một model Post:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">class</span> <span class="token class-name">Ability</span> <span class="token keyword">include</span> <span class="token constant">CanCan</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Ability</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span>user<span class="token punctuation">)</span> can <span class="token symbol">:read</span><span class="token punctuation">,</span> <span class="token constant">Post</span><span class="token punctuation">,</span> <span class="token keyword">public</span><span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token keyword">if</span> user<span class="token punctuation">.</span>present<span class="token operator">?</span> <span class="token comment"># additional permissions for logged in users (they can read their own posts)</span> can <span class="token symbol">:read</span><span class="token punctuation">,</span> <span class="token constant">Post</span><span class="token punctuation">,</span> user_id<span class="token punctuation">:</span> user<span class="token punctuation">.</span>id <span class="token keyword">if</span> user<span class="token punctuation">.</span>admin<span class="token operator">?</span> <span class="token comment"># additional permissions for administrators</span> can <span class="token symbol">:read</span><span class="token punctuation">,</span> <span class="token constant">Post</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
TÌm hiểu thêm về Defining Abilities.
5. Can? và cannot?
- Hai method can? và cannot? dùng để kiểm tra quyền của người dùng hiện tại.
Ta sử dụng 2 method này trong views hoặc controllers.
Ví dụ:
1 2 3 4 | <span class="token operator"><</span><span class="token operator">%</span> <span class="token keyword">if</span> can<span class="token operator">?</span> <span class="token symbol">:read</span><span class="token punctuation">,</span> <span class="token variable">@post</span> <span class="token string">%> <%= link_to "View", @post %></span> <span class="token operator"><</span><span class="token operator">%</span> <span class="token keyword">end</span> <span class="token operator">%</span><span class="token operator">></span> |
Tìm hiểu thêm về checking abilities.
6. Fetching records – tìm nạp bản ghi
- Cancancan có khả năng truy xuất tất cả các đối tượng mà người dùng được phép truy cập
1 2 | <span class="token constant">Post</span><span class="token punctuation">.</span>accessible_by<span class="token punctuation">(</span>current_ability<span class="token punctuation">)</span> |
-> Sẽ sử dụng các quy tắc của bạn để đảm bảo rằng người dùng chỉ truy xuất danh sách các bài Post có thể đọc được.
Tìm hiểu thêm về Fetching records.
7. Controller helpers
7.1 Authorizations
- Cancancan cung cấp cho rails phương thức
authorize!
trong controller sẽ đưa ra một ngoại lệ nếu user không thể thực hiện được action đã cho. - Ví dụ:
1 2 3 4 5 | <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span> <span class="token variable">@post</span> <span class="token operator">=</span> <span class="token constant">Post</span><span class="token punctuation">.</span>find<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token symbol">:id</span><span class="token punctuation">]</span><span class="token punctuation">)</span> authorize<span class="token operator">!</span> <span class="token symbol">:read</span><span class="token punctuation">,</span> <span class="token variable">@post</span> <span class="token keyword">end</span> |
7.2 Loaders
- Method load_and_authorize_resource được cung cấp để tự động cho phép tất cả các actions trong resource controllers kiểu RESTful (thiết lập nó cho các action lẻ tẻ).
- Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">class</span> <span class="token class-name">PostsController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> load_and_authorize_resource <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span> <span class="token comment"># @post is already loaded and authorized</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">index</span></span> <span class="token comment"># @posts is already loaded with all posts the user is authorized to read</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
Tìm hiểu thêm về Authorizing Controllers Action.
7.3 Strong parameters
- Bạn phải làm sạch đầu vào trước khi lưu bản ghi, trong các actions như :create và :update
- Đối với action :update, cancancan sẽ tải và cấp quyền cho resource nhưng không tự động thay đổi nó, vì vậy cách sử dụng thông thường như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">update</span></span> <span class="token keyword">if</span> <span class="token variable">@post</span><span class="token punctuation">.</span>update post_params <span class="token comment"># hurray</span> <span class="token keyword">else</span> render <span class="token symbol">:edit</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">post_params</span></span> params<span class="token punctuation">.</span><span class="token keyword">require</span><span class="token punctuation">(</span><span class="token symbol">:post</span><span class="token punctuation">)</span><span class="token punctuation">.</span>permit<span class="token punctuation">(</span><span class="token symbol">:body</span><span class="token punctuation">)</span> <span class="token keyword">end</span> |
- Với action :create, cancancan sẽ cố gắng khởi tạo một instance mới với đầu vào được làm sạch bằng cách tìm xem controller của bạn có các phương thức sau hay không (theo thứ tự):
create_params
<model name>_params
(quy ước mặc định trong rails để đặt tên cho method params của bạn).resource_params
(phương pháp đặt tên chung trong mỗi controller).
Ngoài ra, nếu bạn muốn sử dụng một method tùy chỉnh khác để làm sạch đầu vào, bạn có thể sử dụng
load_and_authorize_resource
với tùy chọnparam_method
để chỉ định.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">class</span> <span class="token class-name">PostsController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> load_and_authorize_resource param_method<span class="token punctuation">:</span> <span class="token symbol">:my_sanitizer</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">create</span></span> <span class="token keyword">if</span> <span class="token variable">@post</span><span class="token punctuation">.</span>save <span class="token comment"># hurray</span> <span class="token keyword">else</span> render <span class="token symbol">:new</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">private</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">my_sanitizer</span></span> params<span class="token punctuation">.</span><span class="token keyword">require</span><span class="token punctuation">(</span><span class="token symbol">:post</span><span class="token punctuation">)</span><span class="token punctuation">.</span>permit<span class="token punctuation">(</span><span class="token symbol">:name</span><span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
- Bạn cũng có thể sử dụng chuỗi sẽ được đánh giá trong ngữ cảnh của controller bằng cách sử dụng
instance_eval
và phải chứa code ruby hợp lệ.
1 2 | load_and_authorize_resource param_method<span class="token punctuation">:</span> <span class="token string">'permitted_params.post'</span> |
- Cuối cùng, bạn cũng có thể liên kết
param_method
với đối tượng Proc sẽ được gọi với controller như một đối số duy nhất.
1 2 | load_and_authorize_resource param_method<span class="token punctuation">:</span> <span class="token builtin">Proc</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token punctuation">{</span> <span class="token operator">|</span>c<span class="token operator">|</span> c<span class="token punctuation">.</span>params<span class="token punctuation">.</span><span class="token keyword">require</span><span class="token punctuation">(</span><span class="token symbol">:post</span><span class="token punctuation">)</span><span class="token punctuation">.</span>permit<span class="token punctuation">(</span><span class="token symbol">:name</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Tìm hiểu thêm về strong parameters.
8. Xử lý truy cập trái phép
- Nếu cấp quyền cho người dùng không thành công, ngoại lệ CanCan::AccessDenied sẽ được bắn ra. Bạn có thể nắm bắt và sửa đổi hành vi của nó trong controller như ví dụ sau:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">class</span> <span class="token class-name">ApplicationController</span> <span class="token operator"><</span> <span class="token constant">ActionController</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span> rescue_from <span class="token constant">CanCan</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">AccessDenied</span> <span class="token keyword">do</span> <span class="token operator">|</span>exception<span class="token operator">|</span> respond_to <span class="token keyword">do</span> <span class="token operator">|</span>format<span class="token operator">|</span> format<span class="token punctuation">.</span>json <span class="token punctuation">{</span> head <span class="token symbol">:forbidden</span><span class="token punctuation">,</span> content_type<span class="token punctuation">:</span> <span class="token string">'text/html'</span> <span class="token punctuation">}</span> format<span class="token punctuation">.</span>html <span class="token punctuation">{</span> redirect_to main_app<span class="token punctuation">.</span>root_url<span class="token punctuation">,</span> notice<span class="token punctuation">:</span> exception<span class="token punctuation">.</span>message <span class="token punctuation">}</span> format<span class="token punctuation">.</span>js <span class="token punctuation">{</span> head <span class="token symbol">:forbidden</span><span class="token punctuation">,</span> content_type<span class="token punctuation">:</span> <span class="token string">'text/html'</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
9. Lock it down
- Nếu bạn muốn đảm bảo phân quyền xảy ra trên mọi action trong ứng dụng của mình, hãy thêm check_authorization vào ApplicationController.
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">ApplicationController</span> <span class="token operator"><</span> <span class="token constant">ActionController</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span> check_authorization <span class="token keyword">end</span> |
Nó sẽ đưa ra một ngoại lê nếu phân quyền không xảy ra trong một action, để bỏ qua phân quyền trong một controller cụ thể, hãy thêm method skip_authorization_check
vào trong controller đó.
TÌm hiểu thêm về Ensure Authorizations.
Cảm ơn các bạn đã theo dõi. Nguồn bài viết: wiki cancancan in rails.