Hướng dẫn xác thực người dùng web bằng gem cancan và cài đặt input form html bằng gem ckeditor

**1 Gem cancan **

Đây là gem thư viện rất được yêu thích sử dụng cho việc xác thực cũn như phân quyền các chức năng cho các level người dùng. Với cách cài đặt và sử dụng khá dễ hiểu.

1.1 Cài đặt và file cấu hình

Trước hết để cài đặt gem cancan này chúng ta cần phải thêm trong gemfile

gem 'cancancan', '~> 1.9' đối với rails 3 trở lên, sau đó chạy bundle để download gem và cài đặt

Tiếp theo là tạo file dùng để định nghĩa các quyền hạn của người dùng bằng cách chạy lệnh command

rails g cancan:ability

lệnh này sẽ tự động tạo ra file app/models/ability.rb, đây sẽ là file chúng ta thiết lập các quyền cho người dùng trên hệ thống web

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)
    if user.admin?
      can :manage, :all
    else
      can :read, :all
    end
  end
end

Đây là đoạn code mẫu mà file ability được tạo ra. Đọc code này ta có thể dễ dàng hình dung ngay ra được ý nghĩa của nó, giống như là ta đọc 1 đoạn văn bản vậy.

Ở đây cancan sẽ kiểm tra quyền của model user. Nếu hàm user.admin? có giá trị true thì user đó có mọi quyền hạn cho mọi đối tượng, còn nếu không thì user chỉ có thể xem tất cả các thông tin mà thôi.

ta có thể dễ thấy 3 trường tạo nên 1 lệnh định nghĩa quyền của người dùng.

thứ nhất đó là "can" để chỉ khả năng có quyền làm , ngoài ra để định nghĩa phủ định 1 quyền nào đó ta sẽ dùng "cannot" Thứ 2 là action, quyền đó là gì, ở đây cacan có mọi quyền cơ bản là create, update, read, delete, ngoài ra còn có 1 số quyền khác như invite... mà "manage" là 1 quyền đặc biệt bao gồm tất cả các quyền đã nói trên. thứ 3 là đối tượng chịu áp dụng (tên model) , all là tất cả các model, Ví dụ:

can :manage, Article  # user can perform any action on the Article
can :read, :all       # user can read any object
can :manage, :all     # user can perform any action on any object

ngoài ra ta có thể nhóm các quyền và các đối tượng chịu áp dụng vào 1 mảng khi khai báo chung với các quyền giống nhau

can [:update, :destroy], [Article, Comment] Ngoài ra ta còn có thể thêm các điều kiện đi kèm cho khai báo quyền hạn với 1 đối tượng nào đó vào cuối dòng.

ví dụ:

can :read, Project, :active => true, :user_id => user.id

ở đây ta có thể thấy là người dùng này có quyền đọc project , còn ":active => true, :user_id => user.id" là điều kiện, nôm na là người dùng này có quyền xem project với điều kiện trường active của project là true và project có trường user_id = id của người dùng, tương đương project là của người đó.

ngoài ra còn có thêm 1 số lựa chọn nữa các bạn có thể tham khảo tại đây

!lưu ý về việc create/update đối tượng thì cancan sẽ tự động check các strong params kiểu như sau:

  • create_params hoặc update_params (tùy theo controller nào được gọi tới)

  • <model_name>_params ví dụ micropost_params (model có tên là Micropost)

  • resource_params

Nếu bạn có khai báo 1 strong params nào khác khoài 3 loại trên trong controller thì bạn phải khai báo params đó với cancantheo ví dụ sau

class ArticlesController < ApplicationController
  load_and_authorize_resource param_method: :my_sanitizer

  def create
    if @article.save
      # hurray
    else
      render :new
    end
  end

  private

  def my_sanitizer
    params.require(:article).permit(:name)
  end
end

1.2 Kiểm tra quyền

Ở đây cancan hỗ trợ việc kiểm tra ở cả view và controller

với View:

<% if can? :update, @article %>
  <%= link_to "Edit", edit_article_path(@article) %>
<% end %>

ta dùng "can?" để kiểm tra thẩm quyền của user với @article đó với action là update (đã được khai báo ở file ability trên) rồi trả ra kết quả true hoặc false tương ứng.

Với Controller:

chúng ta dùng hàm "authorize!" để kiểm tra quyền và trả về lỗi nếu quyền đó là không được phép/ hoặc chưa được khai báo.

def show
  @article = Article.find(params[:id])
  authorize! :read, @article
end

Ngoài ra cancan còn hỗ trợ hàm "load_and_authorize_resource" để áp dụng việc check quyền đối với mọi controller, thường thì hàm này được khai báo trong class ApplicationController hoặc ở file BaseController mà các controller khác kế thừa

class ArticlesController < ApplicationController
  load_and_authorize_resource

  def show
    # @article is already loaded and authorized
  end
end

để tiện cho việc dùng

load_and_authorize_resource thì Can can cũng hỗ trợ hàm

skip_load_and_authorize_resource để loại bỏ việc xác thực đối với 1 số controller đặc biệt như các trang static page, trang signin, signup ...

Thêm vào đó ta có thể giới hạn các action chịu ảnh hưởng các hàm đó bằng thêm option only [action]. giống như dùng "before_action"

load_and_authorize_resource :only => [:index, :show]

1.3 Xử lý các trường hợp không hợp lệ

Dối với các action không hợp lệ từ người dùng, cancan hỗ trợ cho chung ta việc config để chuyển tiếp tới 1 trang thông báo nào đó, ngoài ra có thể thêm các action khác tùy theo yêu cầu. Như ví dụ dưới là sẽ chuyển tới trang chính của web nếu như các truy cập là không hợp lệ.

class ApplicationController < ActionController::Base
  rescue_from CanCan::AccessDenied do |exception|
    redirect_to root_url, :alert => exception.message
  end
end

Và để chắc chắn rằng mọi controller đều được kiểm tra quyền chúng ta có thể dùng

check_authorization trong class ApplicationController

class ApplicationController < ActionController::Base
  check_authorization
end

Và nếu nuốn loại bỏ việc check quyền này ta dùng hàm

skip_authorization_check

**2. Gem ckeditor **

giới thiệu: đây là 1 gem hỗ trợ việc tạo ra đoạn text theo dạng html, giúp cho việc input dữ liệu dạn text với yêu cầu cao về giao diện.

2.1 cài đặt

như thường lệ để cài đặt gem nà cho rails ta thêm

gem 'ckeditor' vào file Gemfile

với trường hợp cần việc upload file (như là ảnh) vào text, chúng ta cần thêm 1 số gem hỗ trợ việc tạo model và lưu file:

ActiveRecord (paperclip, carrierwave, dragonfly) Mongoid (paperclip, carrierwave, dragonfly) trên là các gem hỗ trợ ứng với tên kiểu database.

Với:

ActiveRecord + paperclip:

the thêm gem

gem 'paperclip'

và dùng lệnh này chạy trên command để sinh ra file model, config cần thiết

rails generate ckeditor:install --orm=active_record --backend=paperclip

tương tự với:

ActiveRecord + carrierwave

gem 'carrierwave'
gem 'mini_magick'

rails generate ckeditor:install --orm=active_record --backend=carrierwave
Mongoid + paperclip
gem 'mongoid-paperclip', :require => 'mongoid_paperclip'

rails generate ckeditor:install --orm=mongoid --backend=paperclip
Mongoid + carrierwave
gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'
gem 'mini_magick'

rails generate ckeditor:install --orm=mongoid --backend=carrierwave

Ở trong github thực hành tôi dẽ dùng ActiveRecord + carrierwave

Sau khi chạy lệnh trên hệ thống sẽ tự tạo ra file model trong thư mụcapp/models/ckeditor.

Kế tiếp bạn cần thêm đường dẫn config cho ckeditor trong file configapplication.rb:

config.autoload_paths += %W(#{config.root}/app/models/ckeditor)
thêm đường dẫn trong file config/routes:

mount Ckeditor::Engine => "/ckeditor"

2.2 Sử dụng

Thêm ckeditor javascripts vào file app/assets/javascripts/application.js

//= require ckeditor/init trong view để dùng ckeditor cho việc tạo dữ liệu html ta dùng option "cktext_area"

<%= form_for @page do |form| -%>
  ...
  <%= form.cktext_area :notes, :class => 'someclass', :ckeditor => {:language => 'uk'} %>
  ...
  <%= form.cktext_area :content, :value => 'Default value', :id => 'sometext' %>
  ...
  <%= cktext_area :page, :info, :cols => 40, :ckeditor => {:uiColor => '#AADC6E', :toolbar => 'mini'} %>
  ...
<% end %>

ngoài ra để giảm thời gian chờ khi dùng ckeditor, chúng ta có thể giới hạn các plugin/ ngôn ngữ được load bằng cách thêm config vào file config/initializers/ckeditor.rb

Ckeditor.setup do |config|
  config.assets_languages = ['en', 'fr']
  config.assets_plugins = ['image', 'smiley']
end

2.3 mở rộng

ckeditor sử dụng với Formtastic integration thông qua cách khai báo:

<%= form.input :content, as: :ckeditor %>
<%= form.input :content, as: :ckeditor, input_html: { ckeditor: { height: 400 } } %>

với SimpleForm integration

<%= form.input :content, as: :ckeditor, input_html: { ckeditor: {toolbar: 'Full'} } %>

Khi sử dụng cùng với cancan

thêm config vào file config/initializers/ckeditor.rb để xác thực việc ckeditor sẽ đc dùng với cancan

Ckeditor.setup do |config|
  config.authorize_with :cancan
end

Sau đó khao báo quyền trong file ability của cancan nhu bình thường

can :access, :ckeditor   # quyền sử dụng ckeditor

các quyền cho việc sử dụng file, ảnh qua ckeditor

can [:read, :create, :destroy], Ckeditor::Picture
can [:read, :create, :destroy], Ckeditor::AttachmentFile

ngoài ra vì ckeditor lưu theo dạng html nên để hiện theo đúng kiểu html thì ờ view chúng ta dùng hàm sanitize . ví sụ trường content của @micropost, ở view t dùng như sau:

<%= sanitize @micropost.content %>

Sau đây là link project sample_app đã được áp dụng để test với 2 gem trên

https://github.com/leanh173/sample_app/tree/develop