Computer >> Máy Tính >  >> Lập trình >> Ruby

Hướng dẫn hoàn chỉnh để quản lý quyền người dùng trong ứng dụng Rails

Yêu cầu chung của các ứng dụng web là khả năng gán các vai trò và quyền cụ thể.

Nhiều loại ứng dụng web phân biệt giữa quản trị viên và người dùng thông thường trong việc cung cấp quyền truy cập hạn chế. Điều này thường được thực hiện bằng cách sử dụng một boolean đơn giản để xác định xem người dùng có phải là quản trị viên hay không. Tuy nhiên, các vai trò và quyền có thể trở nên phức tạp hơn nhiều.

Giá trị của ứng dụng của bạn nằm ở việc hạn chế quyền truy cập vào một số dữ liệu và hành động nhất định. Đó chắc chắn là điều bạn không muốn làm rối tung lên. Trong bài đăng này, chúng tôi sẽ giải thích cách triển khai các vai trò và quyền trong một ứng dụng Ruby on Rails cơ bản.

Tôi có cần một viên ngọc để quản lý quyền không?

Không, bạn không cần đá quý, đặc biệt nếu ứng dụng của bạn nhỏ và bạn muốn tránh thêm nhiều phụ thuộc vào cơ sở mã của mình. :

  • DeviseDevise là một gem để xác thực và quản lý vai trò, và nó là một giải pháp thực sự phức tạp và mạnh mẽ. Nó được biết đến như một giải pháp xác thực, vì vậy chỉ áp dụng nó cho cơ sở mã của bạn nếu bạn cần một thư viện rất mạnh mẽ.

  • Pundit:Pundit là một viên ngọc sử dụng các đối tượng Ruby đơn giản, và nó có lẽ là viên ngọc chính sách đơn giản nhất mà chúng tôi sẽ đề cập. Sử dụng đơn giản, có mức ủy quyền tối thiểu và tương tự như sử dụng Ruby thuần túy. Với 7,3 nghìn sao trên GitHub, nó hiện là viên ngọc chính sách phổ biến nhất.

  • CanCan:CanCan là một thư viện ủy quyền hạn chế các tài nguyên mà một người dùng nhất định được phép truy cập. Tuy nhiên, CanCan đã bị bỏ rơi trong nhiều năm và chỉ hoạt động với các bản phát hành Rails 3 trở về trước.

  • CanCanCan:CanCanCan là một thư viện ủy quyền khác cho Ruby và Ruby on Rails. Nó là một thay thế cho CanCan và hiện đang được duy trì. Với 4,9k sao trên GitHub, nó ít phổ biến nhất, nhưng nó hoạt động khá tốt và được duy trì tốt.

Tất cả những viên ngọc này đều tuyệt vời, nhưng không quá khó để tự xây dựng quyền trong Ruby đơn giản. Tôi sẽ chỉ cho bạn cách quản lý quyền mà không cần đá quý, bằng cách sử dụng chiến lược có tên là mẫu đối tượng chính sách.

Mẫu đối tượng chính sách

Đối tượng chính sách là một mẫu thiết kế được sử dụng để xử lý các quyền và vai trò. Bạn có thể sử dụng nó mỗi khi bạn phải kiểm tra xem một thứ gì đó hoặc ai đó có được phép thực hiện một hành động hay không. Nó bao gồm các quy tắc nghiệp vụ phức tạp và có thể dễ dàng được thay thế bằng các đối tượng chính sách khác với các quy tắc khác nhau. Tất cả các phụ thuộc bên ngoài được đưa vào đối tượng chính sách, đóng gói logic kiểm tra quyền, dẫn đến một bộ điều khiển và mô hình sạch. Đá quý như Pundit, Cancan và Cancancan thực hiện mô hình này.

Quy tắc đối tượng chính sách thuần túy

  • Kết quả trả về phải là một giá trị boolean
  • Logic phải đơn giản
  • Bên trong phương thức, chúng ta chỉ nên gọi các phương thức trên các đối tượng được truyền

Triển khai

Hãy bắt đầu với quy ước đặt tên; tên tệp có _policy được áp dụng hậu tố và lớp và chính sách ở cuối. Trong phương thức này, tên luôn kết thúc bằng ? ký tự (ví dụ:UsersPolicy#allowed? ).

Đây là một số mã ví dụ:

class UsersPolicy
  def initialize(user)
    @user = user
  end

  def allowed?
    admin? || editor?
  end

  def editor?
    @user.where(editor: true)
  end

  def admin?
    @user.where(admin: true)
  end
end

Tôi nên sử dụng chúng trong các trường hợp nào?

Khi ứng dụng của bạn có nhiều loại quyền truy cập hạn chế và các hành động bị hạn chế. Ví dụ:các bài đăng có thể được tạo bằng cách sau:

  • ít nhất một thẻ,
  • một hạn chế mà chỉ quản trị viên và người chỉnh sửa mới có thể tạo chúng và
  • một yêu cầu mà người chỉnh sửa cần được xác minh.

Đây là một bộ điều khiển mẫu không có đối tượng chính sách:

class PostsController < ApplicationController
  def create
    if @post.tag_ids.size > 0
    && (current_user.role == ‘admin’
    || (current_user.role == ‘editor’ && current_user.verified_email))
      # create
    end
  end
end

Bởi vì các kiểm tra điều kiện ở trên dài, xấu và không thể đọc được, nên áp dụng mẫu đối tượng chính sách.

Hãy bắt đầu bằng cách tạo PostsCreationPolicy .

class PostsCreationPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def self.create?(user, post)
    new(user, post).create?
  end

  def create?
    with_tags? && author_is_allowed?
  end

  private

  def with_tags?
    post.tag_ids.size > 0
  end

  def author_is_allowed?
    is_admin? || editor_is_verified?
  end

  def is_admin?
    user.role == ‘admin’
  end

  def editor_is_verified?
    user.role == ‘editor` && user.verified_email
  end
end

Bộ điều khiển của chúng tôi với đối tượng chính sách trông giống như sau:

class PostsController < ApplicationController
  def create
    if PostsCreationPolicy.create?(current_user, @post)
      # create
    end
  end
end

Cách sử dụng các đối tượng chính sách trong Rails

Tạo thư mục chính sách bên trong ứng dụng /policies và đặt tất cả các lớp chính sách của bạn ở đó. Khi bạn cần gọi bộ điều khiển, bạn có thể thực hiện trực tiếp trong một hành động hoặc sử dụng before_action :

class PostsController < ApplicationController
  before_action :authorized?, only: [:edit, :create, :update, :destroy]

  def authorized?
    unless ::PostsCreationPolicy.create?(current_user, @post)
      render :file => "public/404.html", :status => :unauthorized
    end
  end
end

Cách kiểm tra các đối tượng chính sách

Thật đơn giản để kiểm tra hành vi trong bộ điều khiển:

require 'rails_helper'

RSpec.describe "/posts", type: :request do
  describe "when user is not allowed" do
    let(:user_not_allowed) { create(:user, admin: false, editor: false) }
    let(:tag) { create(:tag) }
    let(:valid_attributes) { attributes_for(:post, tag_id: tag.id) }

    before do
      sign_in user_not_allowed
    end

    describe "GET /index" do
      it "return code 401" do
        diet = Post.create! valid_attributes
        get edit_post_url(post)
        expect(response).to have_http_status(401)
      end
    end
  end
end

Việc thử nghiệm chính sách cũng đơn giản; chúng tôi có rất nhiều phương pháp nhỏ chỉ với một trách nhiệm.

require 'rails_helper'

RSpec.describe PostsCreationPolicy do
  describe "when user is not allowed" do
    let(:user) { create(:user, editor: false, admin: false) }
    let(:user_editor) { create(:user, editor: true, email: verified) }
    let(:tag) { create(:tag) }
    let(:post) { create(:post, tag_id: tag.id) }

    describe ".create?" do
      context "when user is allowed" do
        it "creates a new post" do
          expect(described_class.create?(user_editor, post)).to eq(true)
        end
      end

      context "when user is not allowed" do
        it "does not create a new post" do
          expected(described_class.create?(user, post)).to eq(false)
        end
      end
    end

    # ...more test cases
  end
end

Chúng tôi kiểm tra xem đối tượng có được phép tạo trong mọi tình huống hay không.

Kết luận

Khái niệm mẫu chính sách tuy nhỏ nhưng tạo ra kết quả lớn. Khi nói đến thử nghiệm với RSpec, bạn không cần phải sử dụng các bản ghi cơ sở dữ liệu; các chính sách của bạn hoàn toàn là các đối tượng Ruby và việc kiểm tra của bạn sẽ đơn giản và nhanh chóng.