Omniauth and strong parameters - ruby-on-rails-4

So this is probably going to be a very dumb question but i've set up Oauth with twitter as the provider in a similar way to Ryan Bates's 'Simple Omniauth Railscast - my question is now that that is set up and working should i be setting strong parameters in my sessions controller or is this not necessary?
SessionsController.rb
class SessionsController < ApplicationController
def create
#user = User.find_by_uid(auth_hash[:uid]) || User.create_user(auth_hash)
session[:uid] = #user.id
if #user
redirect_to root_path
else
redirect_to root_path, flash: {signinerror: "Oops, something went wrong with your sign in. Please try again."}
end
end
def auth_hash
request.env['omniauth.auth']
end
def destroy
session[:uid] = nil
redirect_to root_path
end
end
User.rb
class User < ActiveRecord::Base
has_many :opinions
def self.create_user(auth_hash)
create do |user|
user.provider = auth_hash[:provider]
user.name = auth_hash[:info][:name]
user.uid = auth_hash[:uid]
user.username = auth_hash[:info][:nickname]
user.email = auth_hash[:info][:email]
user.image = auth_hash[:info][:image]
end
end
end
Thanks

Since you don't use mass assignment on object creation, strong parameters will not give you any additional security.
With this plugin Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted.
https://github.com/rails/strong_parameters

Related

ActiveModel::ForbiddenAttributesError in UrlsController#create

I have the following code:
class UrlsController < ApplicationController
def new
#shortened_url = Url.new
end
def create
#shortened_url = Url.new(params[:url])
if #shortened_url.save
flash[:shortened_id] = #shortened_url.id
redirect_to new_url_url
else
render :action => "new"
end
end
def show
#shortened_url = Url.find(params[:id])
redirect_to #shortened_url.url
end
end
I'm getting the error I know is related to the required parameters and permit. Any one can tell me how the method need to be written?
this is the model:
class Url < ActiveRecord::Base
validates :url, :presence => true
end
the error is when submitting the form. I get
ActiveModel::ForbiddenAttributesError in UrlsController#create
It needed the use of strong parameters
class UrlsController < ApplicationController
def new
#shortened_url = Url.new
end
def create
#shortened_url = Url.new(shortened_url)
if #shortened_url.save
flash[:shortened_id] = #shortened_url.id
redirect_to new_url_url
else
render :action => "new"
end
end
def show
#shortened_url = Url.find(params[:id])
redirect_to #shortened_url.url
end
private
def shortened_url
params.require(:url).permit!
end
end

Best in place sending routing error

I have a user controller as follows. I want to use the best_in_place gem for editing name and bio in profile page. I have followed Ryan Bates railscast but its not working correctly. When I tried to see the error through the chrome inspector I see best in place is requesting url http://localhost:3001/user/3 and now throwing 404 not found error.
users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:profile]
respond_to :json, :html
def profile
#user = current_user
end
def show
#user = User.find(params[:id])
end
def update
if current_user.update(user_params)
respond_with current_user
end
end
def edit
#user = current_user
end
private
def user_params
params.require(:user).permit(:name, :bio)
end
end
routes.rb
Rails.application.routes.draw do
resources :user
end
In routes.rb you configured routes as resources :user which gives following routes:
But we can see your controller is UsersController so you have to configured your routes as: resources :users Which gives following routes:
Hope this may solve your problem.

undefined method `remember_token=' for #<User:0x0000010b454f50>

I'm working through Rails Tutorial and am stuck in Ch 8 -- the bit about login sessions. The error appears when I submit an email/pw combination that should create a new session.
Here's the error:
NoMethodError in SessionsController#create
undefined method `remember_token=' for #<User:0x0000010b454f50>
Extracted source (around line #435):
else
match = match_attribute_method?(method.to_s)
match ? attribute_missing(match, *args, &block) : super
end
end
From there I look under create in the sessions controller. And I try to see where I call the remember_token method.
Sessions controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
#if 1 then remember, else forget
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
remember user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
#call log_out helper method
log_out if logged_in?
redirect_to root_url
end
end
I call remember which looks important. Here's the session helper:
module SessionsHelper
#Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
def remember(user)
#generate a new token and provide the encrypted hash
user.remember
cookies.permanent.signed[:user_id]=user.id
cookies.permanent[:remember_token]=user.remember_token
end
def current_user
#if session user id exists, then set to user_id and then...
if (user_id = session[:user_id])
#set #current_user to the correct user account
#current_user||= User.find_by(id: user_id)
#else, if signed cookie user id exists, set to user_id and then...
elsif (user_id = cookies.signed[:user_id])
#set user to the correct user account and...
user = User.find_by(id: user_id)
#if user is authenticated, then log them in and set #current_user to user
if user && user.authenticated?(cookies[:remember_token])
log_in user
#current_user = user
end
end
end
def logged_in?
!current_user.nil?
end
#Forgets a persistent session
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
def log_out
forget(current_user)
session.delete(:user_id)
#current_user = nil
end
end
That looks like the key -- cookies.permanent[:remember_token]=user.remember_token.
But I don't know how to define the remember_token method. When I go through the tutorial I don't see any methods that describe remember_token. The closest I get is in the user model (method called remember):
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
belongs_to :courses
belongs_to :lessons
validates :first_name, presence: true, length: { maximum: 50 }
validates :last_name, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6}
#Returns the hash digest of the given string
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Password.create(string, cost: cost)
BCrypt::Password.create(string, cost: cost)
end
#Returns a random token
def User.new_token
SecureRandom.urlsafe_base64
end
#creates a new token (random string) then encypts it by returning a hash
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
#Returns true if the given token matches the digest
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
#forgets a user
def forget
update_attribute(:remember_digest, nil)
end
end
Am I understanding the interplay between the session controller, session helper and the user model? Is the interplay not setup right - and that's throwing the error? I don't understand why it doesn't work...
Background:
The tutorial intentionally avoids putting the remember_token in the database, as a security feature. So that isn't the problem.
The problem was in the sessions controller -- I didn't understand the ternary operator and had repeated a bit of its functionality. Here's the original Create method:
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
#if 1 then remember, else forget
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
remember user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
This bit remember user is redundant.
The ternary params[:session][:remember_me] == '1' ? remember(user) : forget(user) means if params[:session][:remember_me] is 1, then remember(user) else forget(user).
By then calling remember user (which is the same as remember(user)) I was executing the same things twice, causing the error.

Only allow a user to edit their own content

I am using RoR 4 and the Devise gem for user authentication. I want a user to be able to edit their own content and not anyone else's content. The authenticate_user method only seems to make sure the user is logged in before they can edit the content. But another user can just sign up and edit everyone else's content.
My controller looks like:
class PrayersController < ApplicationController
before_action :find_prayer, only: [:show, :edit, :updated, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#prayers = Prayer.all.order("created_at DESC")
end
def show
end
def new
#prayer = current_user.prayers.build
end
def edit
end
def create
#prayer = current_user.prayers.build(prayer_params)
if #prayer.save
redirect_to #prayer, notice: "Successfully created prayer"
else
render 'new'
end
end
def update
#prayer = Prayer.find_by_id(params[:id])
if #prayer.update(prayer_params)
redirect_to #prayer, notice: "Prayer was successfully updated"
else
render 'edit'
end
end
def destroy
#prayer.destroy
redirect_to root_path
end
private
def prayer_params
params.require(:prayer).permit(:title, :body)
end
def find_prayer
#prayer = Prayer.find(params[:id])
end
end
I tried to make a before_action of my own that looked something like this:
def own_prayer
if !current_user == Prayer.current_user
redirect_to #prayer, notice: "You cannot edit this prayer"
end
end
But that did not work. I can limit access to the form via the view with a similar action but I don't think this is entirely safe?
Thank you
I guess you don't have class method current_user on Prayer class. Also seems you have has_many :prayers in your User model. So to get prayer's user you need to call user method on Prayer instance variable.
It's supposed to be like that:
#prayer = Prayer.find params[:id]
unless current_user == #prayer.user
redirect_to(#prayer, notice: "You cannot edit this prayer") and return
end
If you need more tricky restriction rules then use cancan gem

Adding an editable field to omniauth generated / existing user

I'm using omniauth-twitter to create and authenticate users in my rails app and I'm successfully getting everything I need from Twitter, avatar, username, description, etc. But I'd like to let users add a custom string to display on their account page.
I added a column to the User table and ran the migration. The column is there.
I can't seem to get the update form to work, however. I'm not seeing errors. I just get a page refresh. Since I didn't have an existing form or controller methods to begin with I added them manually.
Here's my Users controller (I'm using friendly-id, hope that doesn't throw you.)
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.friendly.find(params[:id])if params[:id]
end
def edit
#user = User.friendly.find(params[:id])if params[:id]
end
def update
#user = User.friendly.find(params[:id])if params[:id]
end
private
def user_params
params.require(:user).permit(:custom_text)
end
end
I'm not sure if I need both edit and update methods here, but I thought I'd try.
Here's my form (SLIM Template) which I include on the user's show page:
= form_for #user do |f|
= f.text_field :custom_text
= f.submit
The submit button works, as it were, but nothing is updated.
I'm pretty sure I'm just overlooking something painfully obvious.
I can't seem to get the update form to work, however. I'm not seeing errors.
Nothing is getting updated because you are not updating anything in the first place. As per the current code in the update action, its just selecting the record to be updated from the database but doing nothing with it.
def update
#user = User.friendly.find(params[:id])if params[:id] ## Simply selecting
end
In order to update the fetched record, you should call either update or update_attributes method on the instance of User model passing the changed attributes values to the method.
SOLUTION:
Use the following updated code in your UsersController. I have also DRYed up the code little bit by adding a before_action callback named set_user. The set_user method will be called every time before performing the actions such as show, edit and update and will take care of setting the instance variable #user.
class UsersController < ApplicationController
## Adding a before action callback
before_action :set_user, only: [:show, :edit, :update]
def index
#users = User.all
end
def show
end
def edit
end
def update
if #user.update(user_params)
## Redirect to user show page on successful update
redirect_to #user, notice: 'User was successfully updated.'
else
## Render user edit page again upon failure to update
render action: 'edit'
end
end
private
def set_user
#user = User.friendly.find(params[:id]) if params[:id]
end
def user_params
params.require(:user).permit(:custom_text)
end
end
Have you tried changing your form code to set the multipart:true to allow file to be sent
Try this and then upload the photo
= form_for #user,html: {multipart: true} do |f|
= f.file_field :custom_photo
= f.submit
I might also be missing something painfully obvious or new in Rails 4, but it seems like you should actually be telling the DB to update your record:
def update
#user = User.friendly.find(params[:id]) if params[:id]
#user.update_attributes(user_params) if #user
end
private
def user_params
params.require(:user).permit(:custom_text)
end
Without this second line in the update action, it's just finding the record and not doing anything with it...