Authlogic Current User Question - hiding admin links - ruby-on-rails

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

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.

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 %>

Related

Rendering partials anywhere on your rails app

I have a conversation partial with route being resources:conversations, but when i try to render this partial in my navigation bar (to list all their active conversations), i get an undefined method error.
undefined method `author' for nil:NilClass
Right now i'm doing <%= render 'conversations/conversation' %>
Is there a special way to render your partials ANYWHERE on your website?
EDIT: Providing codes
conversations/_conversation.html.erb
<div>
From <strong><%= conversation.author.email %></strong> to
<strong><%= conversation.receiver.email %> (<%#= conversation.receiver.online? ? 'online' : 'offline' %>)</strong>
<br>
<%= link_to 'View conversation', conversation_path(conversation) %>
<hr>
</div>
layouts/_navigation.html.erb
<%= render 'conversations/conversation' %>
routes
#Chat
resources :conversations
resources :personal_messages
resources :users
mount ActionCable.server => '/cable'
EDIT2:
conversations_controller.rb
class ConversationsController < ApplicationController
before_action :set_conversation
before_action :check_participating!, except: [:index]
def index
#conversations = Conversation.participating(current_user).order('updated_at DESC')
respond_to do |format|
format.html
format.js
end
end
def show
#personal_message = PersonalMessage.new
#conversations = Conversation.participating(current_user)
end
private
def set_conversation
#conversation = Conversation.find_by(id: params[:id])
end
def check_participating!
redirect_to root_path unless #conversation && #conversation.participates?(current_user)
end
end
applicationcontroller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :authenticate_user!
end
The normal way to do this is:
render(partial: 'conversations/conversation', object: #conversation)
If you don't provide a reference object there's no data binding to the local conversation variable and you get the nil reference error.

Rails Omniauth twitter gem - not authorizing user correctly

I'm building a Rails app which allows users to create and book onto events. I've integrated the twitter omniauth gem along with devise. It logs me in correctly and redirects back however when I click on the link to create an event or book an event the app redirects me back to the sign in page. I've set the site up so that only signed in users can do this but it doesn't appear to cover the omniauth integration.
I also have no way to sign-out from one user to another if I use Twitter to sign in. I want to add Facebook auth also but want to fix this first. What code (inc. validations) am I missing to cover these functions?
Here's the relevant code so far -
Events Controller -
class EventsController < ApplicationController
before_action :find_event, only: [:show, :edit, :update, :destroy,]
# the before_actions will take care of finding the correct event for us
# this ties in with the private method below
before_action :authenticate_user!, except: [:index, :show]
# this ensures only users who are signed in can alter an event
def index
if params[:category].blank?
#events = Event.all.order("created_at DESC")
else
#category_id = Category.find_by(name: params[:category]).id
#events = Event.where(category_id: #category_id).order("created_at DESC")
end
# The above code = If there's no category found then all the events are listed
# If there is then it will show the EVENTS under each category only
end
def show
end
def new
#event = current_user.events.build
# this now builds out from a user once devise gem is added
# after initially having an argument of Event.new
# this assigns events to users
end
# both update and create actions below use event_params as their argument with an if/else statement
def create
#event = current_user.events.build(event_params)
# as above this now assigns events to users
# rather than Event.new
if #event.save
redirect_to #event, notice: "Congratulations, you have successfully created a new event."
else
render 'new'
end
end
def edit
# edit form
# #edit = Edit.find(params[:id])
#event = current_user.events.find(params[:id])
end
def update
if #event.update(event_params)
redirect_to #event, notice: "Event was successfully updated!"
else
render 'edit'
end
end
def destroy
#event.destroy
redirect_to root_path
end
private
def event_params
params.require(:event).permit(:title, :location, :date, :time, :description, :number_of_spaces, :is_free, :price, :organised_by, :url, :image, :category_id)
# category_id added at the end to ensure this is assigned to each new event created
end
def find_event
#event = Event.find(params[:id])
end
end
Application controller -
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
devise_parameter_sanitizer.for(:account_update) << :name
end
# the application controller
# handles everything across the site
# make the current_user AND the logged_in? available to
# be used in the views as well as the controllers
helper_method :current_user
helper_method :logged_in?
helper_method :logged_out?
def current_user
# this is who I am signed in as
#current_user = User.find(session[:uid])
end
def logged_in?
# am i logged in?
# do i have a cookie called uid?
session[:uid].present?
end
def make_sure_logged_in
# If I'm not logged in, redirect me to the log in page
if not logged_in?
flash[:error] = "You must be signed in to see that page"
redirect_to new_session_path
end
end
def logged_out?
session[:uid] = nil
flash[:success] = "You've logged out"
redirect_to root_path
end
end
index.html.erb - events
<header>
<div class="category">
<%= link_to image_tag('MamaKnows.png'), root_path, id: "home" %>
<% Category.all.each do |category| %>
<li><%= link_to category.name, events_path(category: category.name) %></li>
<% end %>
<!-- The code loop above creates category links to the home page -->
</div>
<nav id="nav">
<% if logged_in? %>
<%= link_to 'Create Event', new_event_path %>
<%= link_to 'Account', user_path(current_user) %>
<%= link_to 'Sign out', destroy_user_session_path, :method => :delete %>
<% else %>
<%= link_to "Create an Event", new_user_session_path %>
<% end %>
</nav>
</header>
<% #events.each do |event| %>
<%= link_to (image_tag event.image.url), event %>
<h2><%= link_to event.title, event %></h2>
<h2><%= link_to event.date.strftime('%A, %d %b %Y'), event %></h2>
<% end %>
OmniauthCallback Controller
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def twitter
#details = request.env["omniauth.auth"]
#provider = #details["provider"]
#provider_id = #details["uid"]
#user = User.where(provider: #provider, provider_id: #provider_id).first
if #user.present?
#sign them in
else
# make a new user
#user = User.new
#user.provider = #provider
#user.provider_id = #provider_id
# because of has_secure_password - will this work?
#user.password = "AAAAAA!!"
#user.password_confirmation = "AAAAAA!!"
# let's save the key and secret
#user.key = #details["credentials"]["token"]
#user.secret = #details["credentials"]["secret"]
# lets fill in their details
#user.name = #details["info"]["name"]
if #provider == "twitter"? #user.save!(:validate => false) : #user.save!
# the above if statement allows for twitter to skip validation which requires an email
#user.email = #details["info"]["email"]
end
#user.save!
end
session[:uid] = #user.id
flash[:success] = "You've signed in"
redirect_to root_path
end
def password_required?
super && provider.blank?
end
end
Any assistance would be appreciated.

Rails many to many association

I have implemented a save buttons for jobs and it work fine now i want to list some of the saved jobs in the jobs index page for this i have this code
<h3>Saved Jobs</h3>
<ul>
<% #user.saved_jobs.limit(5).order(:created_at).reverse_order.each do |saved_job| %>
<li><%= link_to saved_job.job.title, saved_job.job.url %>
<span class="delete_button">
<%= link_to "X", saved_job, :method => :delete, :remote => true %></span></li>
<% end %>
</ul>
<%= link_to "see all", saved_jobs_path %>
but when i want access to the jobs index page i get this error undefined method saved_jobs' for nil:NilClass
this my saved_jobs controller
class SavedJobsController < ApplicationController
before_filter :authenticate_user!
def index
#saved_jobs = SavedJob.find_all_by_user_id(current_user.id)
end
def create
#job = Job.find(params[:saved_job][:job_id])
current_user.save_job!(#job)
respond_to do |format|
format.html { redirect_to #job }
format.js
end
end
def destroy
#job = SavedJob.find(params[:id]).job
current_user.saved_jobs.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to #job }
format.js
end
end
end
and this is my user controller
class ProfilesController < ApplicationController
before_filter :authenticate_user!
def show
#user = User.find_by_slug(params[:id])
if #user
#posts = Post.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def index
#users = user_scope.paginate(page: params[:page], per_page: 2)
end
private
def user_scope
current_user ? User.where.not(id: current_user.id) : User.all
end
end
The error suggests that you didn't set #user in your controller action. Can you post the code from your controller?
Update based on controller code:
Your index action in SavedJobsController does not set #user. When you then call #user.saved_jobs.limit(5)..etc... in the view #user is nil, rather than current_user or whatever.
Additionally, you have set #saved_jobs - why not just use that instead of #user.saved_jobs?
for instance:
#in SavedJobsController
def index
#saved_jobs = current_user.saved_jobs.limit(5).order('created_at DESC')
end
and then, in your view:
<% #saved_jobs.each do |saved_job| %>

Rails - before_filter undefined local variable

I have this form where a user input a review. A user must be signed with Facebook to save a review.
I use a before_filter to check if the user is signed in or not.
But I get this error: undefined local variable or method signed_in_user'
.
The other thing is, how do I logged the user in with facebook and the save its review? Without losing and making the user input the same review again.
Review form:
<%= form_for [#school, Review.new] do |f| %>
<%= f.text_area :content %>
<% if current_user %>
<%= f.submit 'Save my review', :class => "btn" %>
<% else %>
<%= f.submit 'Save my review and sign me into facebook', :class => "btn" %>
<% end %>
<%end %>
ReviewsController
class ReviewsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
def create
#school = School.find(params[:school_id])
#review = #school.reviews.new(params[:review])
#review.user_id = current_user.id
if #review.save
redirect_to #review.school, notice: "Review has been created."
else
render :new
end
end
def new
#school = School.find_by_id(params[:school_id])
#review = Review.new
end
end
ReviewsHelper
module ReviewsHelper
def signed_in?
!current_user.nil?
end
def signed_in_user
unless signed_in?
redirect_to "/auth/facebook"
end
end
end
I am using omniauth to authenticate users from facebook.
include your ReviewsHelper in controller:
class ReviewsController < ApplicationController
include ReviewsHelper #or helper :reviews
before_filter :signed_in_user, only: [:create, :destroy]
def create
#school = School.find(params[:school_id])
#review = #school.reviews.new(params[:review])
#review.user_id = current_user.id
if #review.save
redirect_to #review.school, notice: "Review has been created."
else
render :new
end
end
def new
#school = School.find_by_id(params[:school_id])
#review = Review.new
end
end
Your helper is not included in the controller by default.
You can include it as codeit suggested.
Most people put their before filters in ApplicationController as a private method.
EDIT:
To persist the log in, save it to the session data. Look up sessions in the Rails Guides.
I have meet the same problem before. The helper is to help edit the view layer. The before_filter method cannot be written in the helper by default, unless you write 'include BlbalblaHelper' in the controller.
You Can just write the before_filter method in the application_controller.rb as a private method, or in lib/ folder. I think both of them are better approach for DRY.

Rails DRY problem: Need same code in controller and view

I have the following login check in my page:
class LoungeController < ApplicationController
before_filter :confirm_logged_in
def index
end
end
while confirm_logged_in defined here:
class ApplicationController < ActionController::Base
protect_from_forgery
protected
def confirm_logged_in
return true if current_user
redirect_to(:controller => 'access', :action => 'login')
return false # halts the before_filter
end
def current_user
return false unless session[:user_id]
user = User.find(session[:user_id])
return false unless user
(user.display_name == session[:user_display_name]) ? user : nil
end
end
Now, I want to use confirm_logged_in also in app/views/layouts/application.html.erb:
<% if confirm_logged_in %>
<div id="logged_in_as">You are logged in as <%= session[:user_display_name] %></div>
<div id="logout"><%= link_to("Logout", {:controller => "access", :action => "logout"}, :id => "logout_link") %></div>
<% end %>
How would you suggest to solve this problem ? Where should I define confirm_logged_in ?
You can use helper_method
controller.rb
helper_method :confirm_logged_in, :current_user
protected
def confirm_logged_in
# code...
end
def current_user
# code...
end

Resources