Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Rails 앱에서 사용자 권한 관리에 대한 전체 가이드

웹 애플리케이션의 일반적인 요구 사항은 특정 역할 및 권한을 할당하는 기능입니다.

많은 유형의 웹 응용 프로그램은 제한된 액세스를 제공할 때 관리자와 일반 사용자를 구분합니다. 이는 사용자가 관리자인지 여부를 결정하는 간단한 부울을 사용하여 수행되는 경우가 많습니다. 그러나 역할과 권한은 훨씬 더 복잡해질 수 있습니다.

애플리케이션의 가치는 특정 데이터 및 작업에 대한 액세스를 제한하는 데 있습니다. 그것은 확실히 당신이 망치고 싶지 않은 것입니다. 이 게시물에서는 기본 Ruby on Rails 애플리케이션에서 역할과 권한을 구현하는 방법을 설명합니다.

권한을 관리하려면 gem이 필요합니까?

아니요, 특히 애플리케이션이 작고 코드 기반에 더 많은 종속성을 추가하지 않으려는 경우 gem이 필요하지 않습니다. 그러나 대안을 찾고 있다면 역할과 권한을 다루는 가장 인기 있는 gem이 있습니다. :

  • DeviseDevise는 인증 및 역할 관리를 위한 보석이며 정말 복잡하고 강력한 솔루션입니다. GitHub에 21.7k 별이 있는 이 게시물에서 가장 인기 있는 저장소이지만 역할 관리 이상의 기능을 수행합니다. 인증 솔루션으로 알려져 있으므로 매우 강력한 라이브러리가 필요한 경우에만 코드베이스에 적용하십시오.

  • Pundit:Pundit은 단순한 Ruby 객체를 사용하는 보석이며 아마도 우리가 다룰 가장 간단한 정책 보석일 것입니다. 사용이 간편하고 최소한의 권한만 부여되며 순수한 Ruby를 사용하는 것과 유사합니다. GitHub에서 별 730만 개를 획득했으며 현재 가장 인기 있는 정책 보석입니다.

  • CanCan:CanCan은 지정된 사용자가 액세스할 수 있는 리소스를 제한하는 권한 부여 라이브러리입니다. 그러나 CanCan은 수년간 사용이 중단되었으며 Rails 3 및 이전 릴리스에서만 작동합니다.

  • CanCanCan:CanCanCan은 Ruby 및 Ruby on Rails를 위한 또 다른 인증 라이브러리입니다. CanCan의 대안이며 현재 유지 관리 중입니다. GitHub에서 별 4.9k로 인기가 가장 낮지만 꽤 잘 작동하고 잘 유지되고 있습니다.

이 모든 보석은 훌륭하지만 일반 Ruby에서 직접 권한을 구축하는 것은 그리 어렵지 않습니다. 정책 개체 패턴이라는 전략을 사용하여 gem 없이 권한을 관리하는 방법을 보여 드리겠습니다.

정책 개체 패턴

정책 개체는 권한 및 역할을 처리하는 데 사용되는 디자인 패턴입니다. 무언가 또는 누군가가 작업을 수행할 수 있는지 여부를 확인해야 할 때마다 사용할 수 있습니다. 복잡한 비즈니스 규칙을 캡슐화하고 규칙이 다른 다른 정책 개체로 쉽게 대체할 수 있습니다. 모든 외부 종속성이 정책 개체에 주입되어 권한 확인 논리를 캡슐화하여 깨끗한 컨트롤러와 모델을 생성합니다. Pundit, Cancan 및 Cancancan과 같은 보석이 이 패턴을 구현합니다.

순수 정책 개체 규칙

  • 반환은 부울 값이어야 합니다.
  • 논리는 단순해야 합니다.
  • 메소드 내부에서는 전달된 객체에 대해서만 메서드를 호출해야 합니다.

구현

명명 규칙부터 시작하겠습니다. 파일 이름에 _policy가 있습니다. 접미사가 적용되고 끝에 클래스와 정책이 적용됩니다. 이 방법에서 이름은 항상 ?로 끝납니다. 문자(예:UsersPolicy#allowed? ).

다음은 몇 가지 예시 코드입니다:

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

어떤 시나리오에서 사용해야 하나요?

앱에 두 가지 이상의 유형의 제한된 액세스 및 제한된 작업이 있는 경우. 예를 들어 다음을 사용하여 게시물을 작성할 수 있습니다.

  • 적어도 하나의 태그
  • 관리자와 편집자만 만들 수 있는 제한 사항 및
  • 편집자가 확인해야 하는 요구 사항입니다.

다음은 정책 개체가 없는 컨트롤러의 예입니다.

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

위의 조건 검사는 길고 보기 흉하고 읽을 수 없기 때문에 정책 개체 패턴을 적용해야 합니다.

먼저 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

정책 개체가 있는 컨트롤러는 다음과 같습니다.

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

Rails에서 정책 개체를 사용하는 방법

/policies 내에 정책 디렉토리 생성 거기에 모든 정책 클래스를 배치합니다. 컨트롤러를 호출해야 하는 경우 작업에서 직접 호출하거나 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

정책 개체를 테스트하는 방법

컨트롤러에서 동작을 테스트하는 것은 간단합니다.

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

정책 테스트도 간단합니다. 우리는 단 하나의 책임을 가진 많은 작은 방법을 가지고 있습니다.

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

모든 시나리오에서 개체 생성이 허용되는지 테스트합니다.

결론

정책 패턴 개념은 작지만 큰 결과를 가져옵니다. 단순하거나 복잡한 권한을 처리해야 할 때마다 정책 개체를 적용하는 것을 고려하십시오. RSpec으로 테스트할 때 데이터베이스 레코드를 사용할 필요가 없습니다. 정책은 순전히 Ruby 개체이며 테스트는 간단하고 빠릅니다.