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
Related
I am building a simple website where people can upload their poems and writing new ones.
I am trying to use Pundit so that:
Everyone can see all the poems/poetries (in index)
Only logged in user can create a poetry
Only the user who create the poetry can updated OR delete it
I followed the official following documentations but I still need to login to perform any action in my controller.
I am not sure what I am doing wrong since I am following the docs and it seems a copy/paste type of work.
My code:
poetries_controller.rb
class PoetriesController < ApplicationController
before_action :set_poetry, only: [:show, :edit, :update, :destroy]
def index
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
def show
end
def new
#poetry = Poetry.new
authorize #poetry
end
def create
Poetry.create(poetry_params)
authorize #poetry
redirect_to poetries_path
end
def edit
end
def update
#poetry.save
redirect_to poetry_path(#poetry)
end
def destroy
#poetry.destroy
redirect_to poetries_path
end
private
def poetry_params
params.require(:poetry).permit(:title, :author, :body)
end
def set_poetry
#poetry = Poetry.find(params[:id])
authorize #poetry
end
end
application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :authenticate_user!
after_action :verify_authorized, :except => :index, unless: :devise_controller?
after_action :verify_policy_scoped, :only => :index, unless: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
flash[:alert] = "Non sei autorizzato a eseguire questa azione"
redirect_to(root_path)
end
end
poetry_policy.rb
class PoetryPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def show?
true # Anyone can view a poetry
end
def create?
true # Anyone can create a poetry
end
def update?
record.user == user # Only poetry creator can update it
end
def destroy?
record.user == user # Only poetry creator can update it
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope.all
end
end
end
index.html.erb
<div class="container">
<div class="row">
<% #poetries.each do | poetry| %>
<div class="col-xs-12 col-sm-4">
<% if policy(poetry).show? %>
<%= link_to poetry_path(poetry) do %>
<div class="card">
<div class="card-description">
<h2> <%=poetry.title=%> </h2>
<% a = sanitize(poetry.body.truncate(170), tags: %w(br)) %></p>
<p> <%= a %></p>
<p><i><%=poetry.author=%><i></p>
</div>
</div>
<% end %>
<% end %>
</div>
<% end %>
<!-- </div> -->
</div>
</div>
You call before_action :authenticate_user! in ApplicationController, that's why Devise doesn't allow you see poetries#index. It's not a Pundit problem at all.
Move this callback from ApplicationController to needed controllers, where you really want to check authentication. And restrict it with particular actions.
class PoetriesController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
end
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.
I have Rails 4.0.10. I'm following Michael Hartl's Ruby on Rails tutorial, and I'm trying to build a login function, but I'm getting the following error when I press the login button:
Routing Error
uninitialized constant SessionController
I followed the instructions exactly, so I'm confused about why I'm getting an error. What did I do wrong?
My Sessions Controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(id: params[session][:id])
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to root_path
else
flash.now[:danger] = 'Invalid'
render 'new'
end
end
def destroy
end
end
Routes:
Website::Application.routes.draw do
get 'login' => 'sessions#new'
post 'login' => 'session#create'
delete 'logout' => 'sessions#destroy'
get "users/new"
root 'home_page#home'
end
Sessions/new View:
<div id= "admin-sign-in">
<%= form_for(:session, url: login_path) do |f| %>
<%= f.label :id %>
<%= f.text_field :id %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>
</div>
Sessions Helper:
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
end
User Model:
class User < ActiveRecord::Base
has_secure_password
end
Users Controller:
class UsersController < ApplicationController
def new
#user = User.new
#users = User.all
end
def create
#user = User.new(user_params)
if #user.save
render :action => "crop"
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
end
ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
Instead of:
post 'login' => 'session#create'
Use the plural:
post 'login' => 'sessions#create'
class ApplicationController < ActionController::Base
protect_from_forgery
skip_before_filter :authenticate_user! , :only => ["welcome#index"]
# before_filter :authenticate_user! :except => ["welocme#index"]
def after_sign_in_path_for(user)
# user_dashboard_index_path
user_dashboard_index_path
end
def after_sign_out_path_for(user)
welcome_index_path
end
after_filter :authenticate_admin!
def after_sign_in_path_for(admin)
admin_dashboard_index_path
end
def after_sign_out_path_for(admin)
welcome_index_path
end
end
Admin should not access the users dashboard and similarly user should not access the admin dashboard.
How can I achieve this?
i have done in my project:
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
if user_signed_in?
user_dashboard_index_path
elsif admin_signed_in?
admin_dashboard_index_path
else
xyz_path
end
end
Same for sign-out:
def after_sign_out_path_for(resource)
if user_signed_in?
welcome_index_path
elsif admin_signed_in?
welcome_index_path
else
xyz_path
end
end
for authentication:
in (welcome/index)
<% if user_signed_in? %>
contant_of user
<% else %>
you are not authenticated #admin can not authenticate this page
<% end %>
Hope it would be helpfull
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 %>