복붙노트

[RUBY-ON-RAILS] 루비 온 레일즈 모델 내에서 CURRENT_USER 액세스

RUBY-ON-RAILS

루비 온 레일즈 모델 내에서 CURRENT_USER 액세스

나는 루비 온 레일즈 응용 프로그램에서 세분화 된 액세스 제어를 구현해야합니다. 개별 사용자에 대한 권한은 데이터베이스 테이블에 저장되고 나는 그것이 특정 사용자 나 쓰기 그것을 읽을 수 있는지 여부를 결정하는 각각의 자원 (모델의 즉 인스턴스)를 수 있도록 최선의 것이라고 생각했다. 컨트롤러에이 결정을 만들기마다 확실히 매우 건조하지 않을 것입니다. 문제는이 작업을 수행하기 위해, 모델 may_read? (CURRENT_USER, ATTRIBUTE_NAME) 같은 것을 호출, 현재 사용자에 대한 액세스를 필요로한다는 것이다. 일반적으로 모델하지만, 세션 데이터에 액세스 할 수 없습니다.

현재의 thread로 현재 사용자에 대한 참조를 저장하는 아주 몇 가지 제안이 들어있다 이 블로그 게시물입니다. 이것은 확실히 문제를 해결할 것입니다.

이웃 Google 결과는 누구의 응용 프로그램을 한 번에 많은 사용자를 수용하지 않는 사람에 의해 생각되었다 내가 추측하지만 사용자 클래스에서 현재 사용자에 대한 참조를 저장하는 저를 조언했다. ;)

모델 내에서 액세스 내 소원 현재 사용자 (즉, 세션 데이터가) 잘못하고 내게 오는 긴 짧은 이야기, 나는 느낌을 얻을.

당신은 내가 틀렸다 방법을 말해 줄래?

해결법

  1. ==============================

    1.나는 당신의 본능이 올바른지 모델에서 CURRENT_USER를 유지하기 위해 말할 것입니다.

    나는 당신의 본능이 올바른지 모델에서 CURRENT_USER를 유지하기 위해 말할 것입니다.

    마찬가지로 다니엘 나는 마른 컨트롤러와 지방 모델에 대한 모든 해요,하지만 책임의 명확한 구분도 있습니다. 컨트롤러의 목적은 들어오는 요청 및 세션을 관리하는 것입니다. 이 모델은 질문 "캔 사용자 x는이 개체에 Y를 할 수 있습니까?"대답 할 수 있어야하지만,이 CURRENT_USER를 참조하는 것은 무의미합니다. 당신은 콘솔 어떤 경우? 그것은 cron 작업 실행은 무엇인지?

    모델의 올바른 권한 API를 사용하여 대부분의 경우이 여러 가지 작업에 적용되는 한 줄 before_filters으로 처리 할 수 ​​있습니다. 일을 더 복잡지고 그러나 경우 비 대한되는 것을 컨트롤러를 방지하기 위해 더 복잡한 인증 로직을 캡슐화하는 (아마도 lib 디렉토리 /에서) 별도의 레이어를 구현하고, 너무 꽉 웹 요청에 결합되는 것을 모델을 방지 할 수 있습니다 / 응답주기.

  2. ==============================

    2.이 질문에 많은 응답되어 있지만 난 그냥 빨리 내 두 센트를 추가 싶었어요.

    이 질문에 많은 응답되어 있지만 난 그냥 빨리 내 두 센트를 추가 싶었어요.

    사용자 모델에 #current_user 접근 방식을 사용하는 것은 스레드로부터의 안전성으로 인해주의를 구현해야합니다.

    당신이 어떤 식 으로든 저장하고 값을 검색으로 Thread.current를 사용하는 기억한다면 사용자에 클래스 / 싱글 방법을 사용하기 괜찮습니다. 그러나 그것은 쉽지 않다으로 그 다음 요청 말아야하지 권한을 상속하지 않도록 당신은 또한 Thread.current를 재설정해야하기 때문이다.

    당신이 창문 밖으로 스레드 안전을 던지고있다 기억, 클래스 또는 싱글 변수의 상태를 저장하는 경우 내가 만들려고 노력하고있는 점이다.

  3. ==============================

    3.데이터베이스와 작업 모델의 일이다. 현재 요청에 대한 사용자를 알고 포함한 웹 요청을 처리, 컨트롤러의 일이다.

    데이터베이스와 작업 모델의 일이다. 현재 요청에 대한 사용자를 알고 포함한 웹 요청을 처리, 컨트롤러의 일이다.

    모델 인스턴스가 현재 사용자를 알아야 할 필요가있는 경우 따라서, 컨트롤러는 그것을 말해야한다.

    def create
      @item = Item.new
      @item.current_user = current_user # or whatever your controller method is
      ...
    end
    

    이 항목은 CURRENT_USER에 대한 attr_accessor이 있다고 가정합니다.

    (주 - 내가 먼저 다른 질문에 대한이 답변을 게시,하지만 난 그냥 질문이 하나의 중복 인 것으로 나타났습니다.)

  4. ==============================

    4.나는 마른 컨트롤러 및 지방 모델에 대한 모든이야, 나는 인증이 원칙을 아프게하지한다고 생각합니다.

    나는 마른 컨트롤러 및 지방 모델에 대한 모든이야, 나는 인증이 원칙을 아프게하지한다고 생각합니다.

    나는 지금 년간 레일 코딩 봤는데 PHP 커뮤니티에서오고있다. 나를 위해, 그것은 "요청 길이의 세계"로 현재 사용자를 설정하는 사소한 솔루션입니다. 이것은 예를 들어, 어떤 프레임 워크에서 기본적으로 수행됩니다

    YII에서는 YII : $ APP-> 사용자 -> 정체성을 호출하여 현재 사용자에 액세스 할 수 있습니다. http://www.yiiframework.com/doc-2.0/guide-rest-authentication.html 참조

    Lavavel에서, 당신은 또한 전화 인증하여 같은 일을 할 수 있습니다 : 사용자 (). http://laravel.com/docs/4.2/security 참조

    왜 난 그냥 컨트롤러에서 현재 사용자를 통과 할 수 있는지?

    이제 우리는 다중 사용자를 지원하는 간단한 블로그 애플리케이션을 만드는 것을 가정 해 봅시다. 우리는 공공 사이트 (익명 사용자가 읽을 수 및 블로그 게시물에 코멘트) 및 관리자 사이트를 만드는 (사용자가 로그인하고 데이터베이스에 자신의 콘텐츠에 대한 CRUD 액세스 할 수 있습니다.)

    여기에 "표준 액세스 라우터는"입니다 :

    class Post < ActiveRecord::Base
      has_many :comments
      belongs_to :author, class_name: 'User', primary_key: author_id
    end
    
    class User < ActiveRecord::Base
      has_many: :posts
    end
    
    class Comment < ActiveRecord::Base
      belongs_to :post
    end
    

    이제, 공공 사이트 :

    class PostsController < ActionController::Base
      def index
        # Nothing special here, show latest posts on index page.
        @posts = Post.includes(:comments).latest(10)
      end
    end
    

    즉 깨끗하고 간단했다. 그러나 관리자 사이트에서 뭔가가 더 필요하다. 이것은 모든 관리 컨트롤러에 대한 기본 구현 한 것입니다 :

    class Admin::BaseController < ActionController::Base
      before_action: :auth, :set_current_user
      after_action: :unset_current_user
    
      private
    
        def auth
          # The actual auth is missing for brievery
          @user = login_or_redirect
        end
    
        def set_current_user
          # User.current needs to use Thread.current!
          User.current = @user
        end
    
        def unset_current_user
          # User.current needs to use Thread.current!
          User.current = nil
        end
    end
    

    그래서 로그인 기능을 첨가하고 현재 사용자는 글로벌에 저장됩니다. 이제 사용자 모델은 다음과 같습니다 :

    # Let's extend the common User model to include current user method.
    class Admin::User < User
      def self.current=(user)
        Thread.current[:current_user] = user
      end
    
      def self.current
        Thread.current[:current_user]
      end
    end
    

    User.current 이제 스레드 안전

    의이 활용하는 다른 모델을 확장 할 수 있습니다 :

    class Admin::Post < Post
      before_save: :assign_author
    
      def default_scope
        where(author: User.current)
      end
    
      def assign_author
        self.author = User.current
      end
    end
    

    그것은 단지 현재 사용자의 게시물에 로그인 거기 같은 느낌 있도록 포스트 모델은 확장되었다. 그 방법은 멋지다!

    관리자 포스트 컨트롤러는 다음과 같을 수 있습니다 :

    class Admin::PostsController < Admin::BaseController
      def index
        # Shows all posts (for the current user, of course!)
        @posts = Post.all
      end
    
      def new
        # Finds the post by id (if it belongs to the current user, of course!)
        @post = Post.find_by_id(params[:id])
    
        # Updates & saves the new post (for the current user, of course!)
        @post.attributes = params.require(:post).permit()
        if @post.save
          # ...
        else
          # ...
        end
      end
    end
    

    코멘트 모델의 경우, 관리자 버전은 다음과 같을 수 있습니다 :

    class Admin::Comment < Comment
      validate: :check_posts_author
    
      private
    
        def check_posts_author
          unless post.author == User.current
            errors.add(:blog, 'Blog must be yours!')
          end
        end
    end
    

    이럴이 확실히 사용자가 액세스 할 수 있는지 확인하기 위해 강력한 & 안전한 방법입니다 / 그들의 데이터를 한 번에 모두를 수정합니다. 모든 쿼리 요구 "(...) current_user.posts.whatever_method"로 시작하는 경우 얼마나 많은 개발자의 요구 쓰기 테스트 코드에 대해 생각해? 많이.

    내가 틀렸다하지만 내가 생각하는 경우에 저를 수정 :

    그것은 모든 문제에 대한 분리합니다. 그것은 단지 컨트롤러가 인증 검사를 처리해야하는 것이 분명하더라도, 결코 현재 컨트롤러 레이어를 유지해야 로그인 한 사용자.

    한가지 기억해야 할 : DO NOT을 그것을 과도하게! User.current를 사용하거나 어쩌면 콘솔 등의 응용 프로그램에 액세스하지 않는 이메일 노동자가있을 수 있음을 기억 ...

  5. ==============================

    5.현재 모델 싱글, 여기에 덮여 : 고대 스레드하지만, 레일 5.2부터 지적 가치, A는 구운에서 이에 대한 해결 방법이 있습니다 https://evilmartians.com/chronicles/rails-5-2-active-storage-and -beyond # 전류 - 모든

    현재 모델 싱글, 여기에 덮여 : 고대 스레드하지만, 레일 5.2부터 지적 가치, A는 구운에서 이에 대한 해결 방법이 있습니다 https://evilmartians.com/chronicles/rails-5-2-active-storage-and -beyond # 전류 - 모든

  6. ==============================

    6.자, 여기 내 생각은 CURRENT_USER 적용하거나 조회 할 그럼, 왜 u는 사용자 모델 또는 u는 권한이 원하는 데이터 모델에 이러한 권한을 추가하지 마십시오, 마지막으로 사용자 인스턴스입니다?

    자, 여기 내 생각은 CURRENT_USER 적용하거나 조회 할 그럼, 왜 u는 사용자 모델 또는 u는 권한이 원하는 데이터 모델에 이러한 권한을 추가하지 마십시오, 마지막으로 사용자 인스턴스입니다?

    내 생각 엔 u는 어떻게 든 구조 조정에 모델을 필요로하고처럼 PARAM으로 현재 사용자를 전달한다는 것입니다 :

    class Node < ActiveRecord
      belongs_to :user
    
      def authorized?(user)
        user && ( user.admin? or self.user_id == user.id )
      end
    end
    
    # inside controllers or helpers
    node.authorized? current_user
    
  7. ==============================

    7.나는 항상 깜짝 놀라게하고있어 질문자의 기본 비즈니스 요구의 아무것도 모르는 사람들이 응답 "그냥하지 않습니다". 네, 일반적으로 피해야한다. 그러나이 적절하고 매우 유용한 모두의 상황이있다. 난 그냥 하나를 자신했다.

    나는 항상 깜짝 놀라게하고있어 질문자의 기본 비즈니스 요구의 아무것도 모르는 사람들이 응답 "그냥하지 않습니다". 네, 일반적으로 피해야한다. 그러나이 적절하고 매우 유용한 모두의 상황이있다. 난 그냥 하나를 자신했다.

    여기 내 솔루션했다 :

    def find_current_user
      (1..Kernel.caller.length).each do |n|
        RubyVM::DebugInspector.open do |i|
          current_user = eval "current_user rescue nil", i.frame_binding(n)
          return current_user unless current_user.nil?
        end
      end
      return nil
    end
    

    이 프레임을 찾고 스택 뒤쪽으로 안내하는 CURRENT_USER에 응답합니다. 아무것도 발견되지 않는 경우는 nil을 반환합니다. 그것은 기대 수익 유형을 확인하여보다 강력하게, 그리고 아마도 프레임의 소유자를 확인하여 컨트롤러의 일종이지만, 일반적으로 단지 멋쟁이 작동 할 수있다.

  8. ==============================

    8.나는 내 응용 프로그램이 있습니다. User.current_user 클래스 변수와 세트가 : [사용자] 그것은 단순히 현재의 컨트롤러 세션을 찾습니다. 이 코드는 생산에서 작동하고 아주 간단합니다. 나는 내가 해낸 말할 수 좋겠지 만, 내가 인터넷 천재 다른 곳에서 빌려 생각합니다.

    나는 내 응용 프로그램이 있습니다. User.current_user 클래스 변수와 세트가 : [사용자] 그것은 단순히 현재의 컨트롤러 세션을 찾습니다. 이 코드는 생산에서 작동하고 아주 간단합니다. 나는 내가 해낸 말할 수 좋겠지 만, 내가 인터넷 천재 다른 곳에서 빌려 생각합니다.

    class ApplicationController < ActionController::Base
       before_filter do |c|
         User.current_user = User.find(c.session[:user]) unless c.session[:user].nil?  
       end
    end
    
    class User < ActiveRecord::Base
      attr_accessor :current_user
    end
    
  9. ==============================

    9.나는 선언적 인증 플러그인을 사용하고, 그리고 그것은 밖으로 풀 CURRENT_USER에 before_filter를 사용하여 당신이 CURRENT_USER와 함께 언급되는 비슷한을 수행하고 모델 층은 그것을 얻을 수있는 곳에 보관하십시오. 다음과 같습니다 :

    나는 선언적 인증 플러그인을 사용하고, 그리고 그것은 밖으로 풀 CURRENT_USER에 before_filter를 사용하여 당신이 CURRENT_USER와 함께 언급되는 비슷한을 수행하고 모델 층은 그것을 얻을 수있는 곳에 보관하십시오. 다음과 같습니다 :

    # set_current_user sets the global current user for this request.  This
    # is used by model security that does not have access to the
    # controller#current_user method.  It is called as a before_filter.
    def set_current_user
      Authorization.current_user = current_user
    end
    

    비록 선언적 권한 부여의 모델 기능을 사용하지 않는. "스키니 컨트롤러 - 지방 모델"나는 모든있어 접근하지만 내 느낌은 권한 부여 (뿐만 아니라 인증)는 컨트롤러 레이어에 속하는 일이다.

  10. ==============================

    10.내 느낌은 현재 사용자가 MVC 모델의 "컨텍스트"의 일부입니다, 현재 시간 등의 현재 사용자에 대해 어떻게 생각 등 현재 로그 스트림, 현재 디버깅 수준, 현재의 트랜잭션 (transaction) 당신이 모든을 전달할 수 " 당신의 함수에 인수로 양식 ". 아니면 현재 함수 본문 외부 환경 변수에 의해 사용할 수 있도록. 로컬 컨텍스트 스레드 이유로 인해 쉬운 스레드 안전의 글로벌하거나 범위 변수보다 더 나은 선택입니다. 조쉬 K 말했듯이, 스레드 현지인 위험은, 작업 후 의존성 주입 프레임 워크는 당신을 위해 할 수있는 일을 삭제해야한다는 것입니다. MVC는 애플리케이션 현실의 다소 단순화 된 그림이 아니라 모든 그것에 의해 덮여있다.

    내 느낌은 현재 사용자가 MVC 모델의 "컨텍스트"의 일부입니다, 현재 시간 등의 현재 사용자에 대해 어떻게 생각 등 현재 로그 스트림, 현재 디버깅 수준, 현재의 트랜잭션 (transaction) 당신이 모든을 전달할 수 " 당신의 함수에 인수로 양식 ". 아니면 현재 함수 본문 외부 환경 변수에 의해 사용할 수 있도록. 로컬 컨텍스트 스레드 이유로 인해 쉬운 스레드 안전의 글로벌하거나 범위 변수보다 더 나은 선택입니다. 조쉬 K 말했듯이, 스레드 현지인 위험은, 작업 후 의존성 주입 프레임 워크는 당신을 위해 할 수있는 일을 삭제해야한다는 것입니다. MVC는 애플리케이션 현실의 다소 단순화 된 그림이 아니라 모든 그것에 의해 덮여있다.

  11. from https://stackoverflow.com/questions/1568218/access-to-current-user-from-within-a-model-in-ruby-on-rails by cc-by-sa and MIT license