views:

132

answers:

2

I think I am missing something while using the Authlogic gem w/ Rails. To set the stage I have multiple users and each user can create posts and comments. Upon the display of a post or comment I would like to give the user who created them the option to edit or destroy.

I am successfully using the following code to hide and show elements based on if a user is logged in or not but can't seem to find out how to only show these links to the actual user who created them...not any user that is logged in.

<% if current_user %>
   <%= link_to 'Edit', edit_question_path(question) %> | 
   <%= link_to 'Destroy', question, :confirm => 'Are you sure?', :method => :delete %>
<% else %>
   <p>nothing to see here</p>
<% end %>

Here is the def of current_user located in the application controller in case I need to change something here.

class ApplicationController < ActionController::Base

  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details#  

  helper_method :current_user

  private

  def current_user_session
    return @current_user_session if defined?(@current_user_session)
    @current_user_session = UserSession.find
  end

  def current_user
    return @current_user if defined?(@current_user)
    @current_user = current_user_session && current_user_session.record
  end
end
+1  A: 

Authentication solutions like authlogic were not built to support what you're trying to do. There are authorization solutions you can use on top on authlogic that let you do fine-grained checks like whether or not a user may access a given action:

<% if current_user.may_update_question? @question %>
  <%= link_to 'Edit', edit_question_path(@question) %>
<% end %>

The example above is uses Aegis.

henning-koch
I just installed this gem and created the permissions controller where I entered the following: role :user permission :edit_question do |user, question| allow :user do question.creator == user # registered users may only edit their own posts end end endThen entered your above code in the view, and I am getting: undefined method `permissions' for Aegis:Module
bgadoci
Hey man, I got KandadaBoggu's solution to work. Thanks though.
bgadoci
Thanks for this suggesting Aegis @henning-koch. Works brilliant. :)
Shripad K
A: 

Try this:

class ApplicationController < ActionController::Base

  # add your methods (eg: current_user etc)
  helper_method :current_user, :logged_in?, :current_user_is_owner?

  def init_data
    klass = controller_name.camelize.singularize.constantize #User
    param_key = controller_name.camelize.downcase.singularize.to_sym # :user
    obj = case (action_name.to_sym)
      when :new, :create
        klass.new(params[param_key])
      when :edit, :show, :destroy 
        klass.find(params[:id])
      when :update
        klass.find(params[:id]).tap{|o| o.attributes = params[param_key]}
    end
    instance_variable_set("@#{param_key}", obj) # set the obj to @line_item    
  end

  def require_user
    return true if logged_in?
    render_error_message("You must be logged in to access this page", 
        new_user_session_url)
    return false
  end

  def require_owner
    obj = instance_variable_get("@#{controller_name.singularize.camelize.underscore}") # LineItem becomes @line_item
    return true if current_user_is_owner?(obj)
    render_error_message("You must be the #{controller_name.singularize.camelize} owner to access this page", root_url)
    return false
  end

  def logged_in?
    return current_user != nil 
  end

  def current_user_is_owner?(obj)
    logged_in? and obj.respond_to?(:user_id) and 
         (obj.send(:user_id) == current_user.id)    
  end

  def render_error_message message, url
    respond_to do |format|
      format.html do
        flash[:notice] = message
        if request.xhr?
          head :bad_request, :error => message
        else
          redirect_to url
        end
      end
      format.json { render :json => message, :status => :unprocessable_entity }
      format.xml { render :xml => message, :status => :unprocessable_entity }
    end    
  end

end

Now in your controller

class PostsController < ApplicationController
  before_filter :require_user  # all actions require user to be logged in
  before_filter :init_data     # create a member variable called @post, initialized based on the action
  before_filter :require_owner, :only => [:edit, :update, :destroy] #edit, update, and destroy actions require ownership

  def update
    if @post.save
    else
    end
  end
end

In the view code:

<% if current_user_is_owner?(question) %>
  .. display something
<% end %>
KandadaBoggu
Ok so I am implementing the above before moving on the to Aegis solution from henning-koch as I like to understand what is happening. I put the application controller code in but did not delete the current def's. In the SitesController I put in the :require_owner line from your second code block. I then put in an <% if @require_owner %> wrapper around my edit and destroy links. They successfully hide the links but don't show them to owner when logged in.
bgadoci
You are supposed to use require_owner as a filter function. In your views use `current_user_is_owner?`. I have updated the code, take a look.
KandadaBoggu
Dude you are some sort of wizard. Thanks again.
bgadoci
quick follow up. the require_owner in the filter function seems to be limiting access even to the actual owner. Any bug in the code. I can't seem to find it.
bgadoci
Have you committed to code at github? If so I can take a look.
KandadaBoggu
Yep it is committed. Give it like five minutes from now bc I am pushing another round.
bgadoci
Best I can tell is it has something to do with the line: `return true if current_user_is_owner?(obj)`
bgadoci
I tried `return true if current_user == @user` but that produced the same result.
bgadoci
try `current_user.id == @user.id`.
KandadaBoggu
tried that and got error: `Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id`
bgadoci
btw, the url trying to be accessed is `http://localhost:3000/users/current/edit`
bgadoci
but I also get the same result when accessing `http://localhost:3000/questions/20-first-question-from-bgadoci/edit`
bgadoci