Rails wicked gem - ruby-on-rails

I am having problems related to the links given to login and logout.
I am not using devise gem
In my code I have given the following links
<% if current_user %>
<li><%= link_to 'Logout',{:controller=>'sessions', :action=> 'destroy'}%></li>
<% else %>
<li> <%= link_to 'Signup',{:controller =>'users', :action => 'new'} %> </li>
<li> <%= link_to 'Login,{:controller =>'sessions', :action => 'new'} %> </li>
<% end %>
I am using the wicked gem which also has the following steps:
include Wicked::Wizard
steps :business, :login, :payment
If a user enters the form_for values for new method in users_controller and submits it, the user goes to the next step but the link it shows above is "Logout" i.e the user is logged in before signup.
What to do?
Pls, any solution given is appreciated
users_controller.rb:
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.update_attributes(user_params )
session[:user_id]= #user.id
redirect_to user_steps_path
else
render :new
end
end
private
def user_params
params.require(:user).permit( :fname, :lname, :email, :mob, :gender, :country, :state, :suburb, :postal ,:add)
end
end
user_steps_controller.rb
include Wicked::Wizard
steps :business, :login, :payment
def show
#user = current_user
render_wizard
end
def update
#user = current_user
params[:user][:current_step] = step
session[:user_id]= #user.id
#user.update_attributes(user_params )
render_wizard #user
end
private
def redirect_to_finish_wizard(options = nil)
redirect_to root_url
end
def user_params
params.require(:user).permit( :current_step,:cmpyname, :abnacn, :cmpyadd, :cmpydet,:cash, :paypal,:bsb,:usrname,:password, :password_confirmation, :selcat, :protit, :prodes)
end
end
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end

Just check in your views if the user is logged in to show your step form:
<% if user_signed_in?%>
instead of
<% if current_user%>

You need to sign out the user after creating it, you can do something like this
if resource.save
sign_out resource # resource = #user
You might need to override devise registrations controller for that if you are using devise!
EDIT:
In your create action you are setting session for newly created user, remove this line from your create action
session[:user_id]= #user.id
Hope this helps!

Instead of checking with current_user you should check <% if session[:user_id].present? %>
It may solve your problem

Related

ActiveModel::ForbiddenAttributesError in UserStepsController#update

I used Wicked gem to create a multistep form. First step is sign up with email name and password, second step would be address for now containing only the street. Here is my address.html.erb
<%= form_for #user, url: wizard_path do |f| %>
<div class="field">
<%= f.label :street %>
<%= f.text_area :street %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I permitted street and other params in the UsersController:
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to user_steps_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
end
I am getting the error mentioned in the title. And these are the params. It basically gets the street, but somhow assignes id to address?
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"ZOkBaqFUdFj47iI8vB0D4PI26ZsgEKasqbzvVM2ry4Z3e+AsYMh0yRSuUoZF5zbJ3SzAkPShI0sjaZOgh0yXRw==",
"user"=>{"street"=>"jef b"},
"commit"=>"Update User",
"id"=>"address"}
What is happening and how to correct it? Here is UserSteprController:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :address
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.attributes = params[:user]
render_wizard #user
end
private
def redirect_to_finish_wizard
new_user_profile_path(current_user.id)
end
end
Second line in the update action is wrong: #user.attributes = params[:user]
Thank you!
The reason you are getting a ActiveModel::ForbiddenAttributesError is that you are passing an unfiltered hash from the params to your model.
#user.attributes = params[:user]
Is pretty much a textbook example of a mass assignment vulnerability which allows a malicious user to assign any attribute they want like for example admin: true. Fortunately Rails has had built in mass-assignment protection since Rails 4 which stopped you from inflicting the vulnerability on your app.
You want to use update or update_attributes instead of the setter and pass it your filtered parameters instead.
#user.update_attributes(user_params)

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.

Prefill form fields as values in Rails

TL;DR Is it possible to prefill from one model in the same form builder to another model? Is there a correct way of solving this problem?
I have a form that takes several methods that requires to be passed in: name, email, bio, location, homepage, work.
Email method will need to be retrieved via Devise user model.
In my new.html.erb, it works fine as normal but it returns nil from the email field whenever I do the following:
<%= form_for :profile, url: profiles_path do |f| %>
...
<div><% if current_user.email.present? %><%= f.label :email %><br>
<%= f.email_field current_user.email %><% end %></div>
<% end %>
ProfilesController.rb:
class ProfilesController < ApplicationController
before_action :authenticate_user!, except: [:show]
before_action :find_profile_by_id, only: [:show, :edit, :update]
after_filter :destroy_user!, only: [:destroy]
def index
if #profile.blank?
render :new
else
render :show
end
end
def new
#profile = current_user.profiles.build
#user = current_user.where('email=?')
end
def create
#profile = current_user.profiles.build(profile_params)
if #profile.save
redirect_to #profile
else
render :new
end
end
def show
end
def edit
end
def update
if #profile.update(profile_params)
redirect_to #profile
else
render :edit
end
end
def destroy
#user.destroy
#profile.destroy
redirect_to root_path
end
private
def profile_params
params.require(:profile).permit(:name, :email, :bio, :location, :homepage, :work)
end
def find_profile_by_id
#profile = Profile.find(params[:id])
end
def destroy_user!
#user = User.find(params[:id])
end
end
Is this the correct solution for this?
Your approach seems generally okay but try changing
<%= f.email_field current_user.email %>
To
<%= f.email_field(current_user, current_user.email) %>

Rails: How do I resolve: Pundit::NotDefinedError in PostsController#show?

whenever I run the below program and try to view my posts (in my show view) as any user, I am introduced to this error page:
Pundit::NotDefinedError in PostsController#show
unable to find policy of nil
Within that error page:
def show
#post = Post.find(params[:id])
authorize #posts # <- The error highlights this line
end
I'm not sure how to get around this dilemma as I'm just learning about Pundit Policy rules and am new to Rails and Ruby. Any help would be much appreciated. Below are my policy pages and related pages:
User.rb Model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
def member?
role == 'member'
end
def guest?
role == 'guest'
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.
include Pundit
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError do |exception|
redirect_to root_url, alert: exception.message
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
end
end
Posts Controller
class PostsController < ApplicationController
# before_action :flash_attack
# protected
# def flash_attack
# flash[:notice] = "Create/Edit/Comment on a post!"
# end
def index
#posts = Post.all
authorize #posts
end
def show
#post = Post.find(params[:id])
authorize #posts
end
def new
#post = Post.new
authorize #post
end
def create
#post = current_user.posts.build(params.require(:post).permit(:title, :body))
authorize #post
if #post.save
flash[:notice] = "Post was saved."
redirect_to #post
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def edit
#post = Post.find(params[:id])
authorize #post
end
def update
#post = Post.find(params[:id])
authorize #post
if #post.update_attributes(params.require(:post).permit(:title, :body))
flash[:notice] = "Post was updated."
redirect_to #post
else
flash[:error] = "There was an error saving the post. Please try again."
render :edit
end
end
end
Application Policy
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?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin?)
end
def edit?
update?
end
def destroy?
update?
end
def scope
record.class
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
Posts Policy
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(:published => true)
end
end
def index?
true
end
def show?
true
end
def update?
user.admin? or not post.published?
end
end
end
Index View
<h1>All Posts</h1>
<% if policy(Post.new).create? %>
<%= link_to "New Post", new_post_path, class: 'btn btn-success' %>
<% end %>
<% #posts.each do |post| %>
<div class="media">
<div class="media-body">
<h4 class="media-heading">
<%= link_to post.title, post %>
</h4>
<small>
submitted <%= time_ago_in_words(post.created_at) %> ago by <%= post.user.name unless post.user.nil? %><br>
<%= post.comments.count %> Comments
</small>
</div>
</div>
<% end %>
Show View
<h1> <%= #post.title %> </h1>
<% if policy(#post).edit? %>
<%= link_to "Edit", edit_post_path(#post), class: 'btn btn-success' %>
<% end %>
<p> <%= #post.body %> </p>
Thanks in advance everyone. Let me know if any more information would be great.
#posts is nil in show action, you should use #post as such:
authorize #post
I had this issue when working on a Rails 6 API only application with the Pundit gem.
I was running into the error below when I test my Pundit authorization for my controller actions:
Pundit::NotDefinedError - unable to find policy of nil
Here's how I solved:
The instance variables called by the authorize method in your controller must correspond to the instance variable of the controller action being called.
So for the index action it should be #posts:
authorize #posts
For the show action it should be #post:
authorize #post
and for the create action it should be #post
authorize #post
and so on.
That's all.
I hope this helps

admincontroller using wrong helper for create action

I am trying to create an admin instance through my admins controller create action, but I keep getting an error that says:
ActiveRecord::RecordNotFound in AdminsController#show: Couldn't find User with id=4
The trace indicates that it is attempting to use the sessions helper (for user) instead of the appropriate adminsessions helper.
app/helpers/sessions_helper.rb:20:in `current_user'
app/helpers/sessions_helper.rb:12:in `signed_in?'
app/views/layouts/application.html.erb:13:in
app_views_layouts_application_html_erb__1013605049_93953830
I can log in correctly and the admin is created. I just think the problem has to do with the redirect_to #admin in my admins controller, though I'm not sure.
How do I set it up so that my admins controller uses the adminsessions helper instead of the sessions helper? Any help would be greatly appreciated.
adminsessions_controller.rb
class AdminsessionsController < ApplicationController
def new
#title = "Log in"
end
def show
#title = "Admin session"
end
def create
admin = Admin.authenticate(params[:adminsession][:email],
params[:adminsession][:password])
if admin.nil?
flash.now[:error] = "Invalid email/password combination."
#title = "Log in"
render 'new'
else
sign_in admin
redirect_to admin
end
end
def destroy
sign_out
redirect_to root_path
end
end
admins_controller.rb
class AdminsController < ApplicationController
def index
#user = User.all
end
def show
#admin = Admin.find(params[:id])
end
def new
#admin = Admin.new
#title = "New admin"
end
def create
#admin = Admin.new(params[:admin])
if #admin.save
sign_in #admin
flash[:success] = "Welcome admin!"
redirect_to #admin
else
#title = "New admin"
render 'new'
end
end
end
new.html.erb (form where I create new user)
<div id="signupform_new">
<%= form_for(#admin) do |f| %>
<div class="field">
<%= f.label :username %>
<%= f.text_field :name, :class => "round" %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email, :class => "round" %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password, :class => "round" %>
</div>
<div class="field">
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, :class => "round" %>
</div>
<div class="action">
<%= button_tag "", :class => "acctSubmit" %>
</div>
<% end %>
</div>
sessions_helper.rb
module SessionsHelper
def sign_in(user)
session[:user_id] = user.id
self.current_user = user
end
def signed_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def current_user?(user)
user == current_user
end
def authenticate
deny_access unless signed_in?
end
def sign_out
session[:user_id] = nil
self.current_user = nil
end
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
clear_return_to
end
def deny_access
store_location
redirect_to login_path, :notice => "Please log in to access this page."
end
private
def store_location
session[:return_to] = request.fullpath
end
def clear_return_to
session[:return_to] = nil
end
end
adminsessions_helper.rb
module AdminsessionsHelper
def sign_in(admin)
adminsession[:admin_id] = admin.id
self.current_admin = admin
end
def signed_in?
!current_admin.nil?
end
def current_admin=(admin)
#current_admin = admin
end
def current_admin
#current_admin ||= Admin.find(adminsession[:admin_id]) if adminsession[:admin_id]
end
def current_admin?(admin)
admin == current_admin
end
def authenticate
deny_access unless signed_in?
end
def sign_out
adminsession[:admin_id] = nil
self.current_admin = nil
end
def redirect_back_or(default)
redirect_to(adminsession[:return_to] || default)
clear_return_to
end
def deny_access
store_location
redirect_to login_path, :notice => "Please log in to access this page."
end
private
def store_location
adminsession[:return_to] = request.fullpath
end
def clear_return_to
adminsession[:return_to] = nil
end
end
All helpers are (by default) mixed in and available in all controllers. Looks like the methods you are using should be protected or private members of your controllers instead. You can make them helper methods to be available in your views, i.e. helper_method :signed_in?.
Personally I never liked the lack of namespacing with helpers anyway. I like the presenter pattern much better (see RailsCasts Pro].

Resources