1

I have two layouts Admin and Domain. And I don't need any extra configuration in Admin layout. but if user tries to access Domain layout they must be in their valid domain.

This means that, I need to customize all of my Domain policy to include both current_user as well as current_domain. I found this can be done with UserContext and pundit_user... so here is what I have done:

application_controller.rb

class ApplicationController < ActionController::Base
  include Pundit
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  def pundit_user
    UserContext.new(current_user, current_domain)
  end

  def after_sign_out_path_for(resource)
    root_path
  end

  def current_domain
    @current_domain ||= Domain.where(name: requested_domain).first
  end
  helper_method :current_domain

private

  def requested_domain
    return request.env["SERVER_NAME"]
  end

  def user_not_authorized
    # reset_session
    flash[:alert] = "You are not authorized to perform this action"
    redirect_to(request.referrer || root_path)
  end
end

Note that, when I access Admin layout, current_domain will be nil and if I visit any routes of Domain layout, then current_domain will set to currently accessing domain.

user_context.rb

class UserContext
  attr_reader :current_user, :current_domain

  def initialize(current_user, current_domain)
    @current_user = current_user
    @current_domain = current_domain
  end
end

PROBLEM

Suppose I have this policy:

user_policy.rb

class UserPolicy < ApplicationPolicy
  attr_reader :user, :scope

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

  def index?
    binding.pry # debugging
    current_user.admin? ||
    current_user.domain == current_domain
  end

private

  def current_user
    # return user.is_a?(User) ? user : user.current_user
    user.current_user
  end

  def current_domain
    # return user.is_a?(User) ? nil : user.current_domain
    user.current_domain
  end

end

when application runs current_user and current_domain must available in UserPolicy as per documentation(https://github.com/elabs/pundit#additional-context).

But I am getting

undefined method `current_user' for #<User:0x007fcefbc2b150>

That means, still I have user object in it, not user.current_user and user.current_domain

Please let me know, if you need further description. What am I missing here?

przbadu
  • 5,769
  • 5
  • 42
  • 67

1 Answers1

0

It was my own dumb mistake.

PROBLEM

I had a before_filter call in domain/base_controller.rb something like:

class Domain::BaseController < ApplicationController
  before_action :authenticate_user!
  before_action :domain_exists?
  before_action :verify_domain!

private

  def verify_domain!
    # PROBLEM: this line was updating pundit_user again to user object
    raise Pundit::NotAuthorizedError unless DomainConsolePolicy.new(current_user, current_domain).authorized?
  end

  def domain_exists?
    if current_domain.blank?
      redirect_to root_path, alert: 'Domain that you provided is not valid or is permanently removed!'
    end
  end
end

SOLUTION:

I have used headless policy for this because now I have both current_user and current_domain set with pundit_user in application_controller

domain/base_controller.rb

class Domain::BaseController < ApplicationController
  before_action :authenticate_user!
  before_action :domain_exists?
  before_action :verify_domain!

private

  def verify_domain!
    # SOLUTION
    authorize :domain_console, :has_access?
  end

  def domain_exists?
    if current_domain.blank?
      redirect_to root_path, alert: 'Domain that you provided is not valid or is permanently removed!'
    end
  end
end

policy/domain_console_policy.rb

class DomainConsolePolicy < Struct.new(:user, :domain_console)

  def has_access?
    user.current_user.admin? ||
    user.current_user.domain_id == user.current_domain.id
  end

end

Thanks

przbadu
  • 5,769
  • 5
  • 42
  • 67