Unpermitted parameters. Devise + Oauth. Rails 4 - devise

Rails 4, Devise 3.0.3, Oauth-facebook
I've added two additional parameters in my User model - :name, :uid and trying to save it in my users table from my form (route /users/sign_up). But as result i receive record in table, which contains only default values for fields :name and :uid instead of values, which i put in my text_fields.
In console i've got the following message:
Unpermitted parameters: name, uid
WARNING: Can't mass-assign protected attributes for User: password_confirmation
app/models/user.rb:31:in `new_with_session'
Here is my user.rb model. I've tried to remove these fiels from attr_accessible but it gives no result.
class User < ActiveRecord::Base
attr_accessible :oauth_expires_at, :oauth_token, :oauth_secret, :email, :password, :uid, :provider, :name
default_scope -> {order('created_at ASC')}
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
has_many :microposts, :primary_key => "uid", dependent: :destroy
# validates :uid, presence: true
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["user_hash"]
user.email = data["email"]
end
end
end
end
Here is my users/omniauth_callbacks_controller.rb (without facebook method). I've tried to apply different advises related to before_filter, but it still does not work
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
before_filter :configure_permitted_parameters
def create
super
end
private
def configure_permitted_parameters
params.require(:user).permit(:name, :uid, :provider)
end
end
Here is my view-form ()
Sign up
<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :name %> <br />
<%= f.text_field :name, :autofocus => true %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.label :uid %><br />
<%= f.text_field :uid %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
Could you help me, i don't understand what am i doing wrong. How to configure whitelist for my strong params to receive proper values (which user put in view-form) ?
All my source code is available here: https://github.com/DavydenkovM/d23m
Thanks in advance!
UPDATE
I've remove attr_accessible fields and update my controller. But the problem with unpermitted params name and uid at the same point. Now my controller looks like:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
before_filter :configure_permitted_parameters, if: :devise_controller?
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
set_flash_message(:notice, :success, :kind => "Facebook") # if is_navigational_format?
else
redirect_to root_url if user_signed_in?
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def create
super
end
#def update
# person = current_account.user.find(params[:id])
# person.update_attributes!(person_params)s
# redirect_to person
#end
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:users) do |u|
u.permit(:name, :email, :password, :password_confirmation, :uid, :provider)
end
end
end
UPDATE 2.
I'm not clearly understand what is resource in devise_parameter_sanitizer.for(?) and where i need to assign this ?

Please try the following.
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
).permit!(:name, :uid, :provider)
end
user
end
Or
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"].permit!(:name, :uid, :provider), current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
set_flash_message(:notice, :success, :kind => "Facebook") # if is_navigational_format?
else
redirect_to root_url if user_signed_in?
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
Then try this
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation, :uid, :provider) }
end

attr_accessible is not available in Rails 4 because Strong Parameters is now used by default. It appears you are already using strong parameters, so you should simply remove the line from your user model with attr_accessible in it.

Related

Devise-Pundit: Create a page for Superadmin to create users, but it is not actually creating users

How do I enable the Superadmin to actually create Users? Do I need a policy CreateusersPolicy? My code currently takes me to a page/form where I can create a user, but it doesn't actually create the user.
Please let me know if I need to include more information!
config/routes.rb
Rails.application.routes.draw do
devise_for :users
resources :users, except: :create
root "pages#home"
get "index" => "users#index"
get 'create_user' => 'users#create', as: :create_user
controllers/application_controller.rb
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
def authorize_superadmin
redirect_to root_path, alert: 'Access Denied' unless current_user.superadmin?
end
end
I also don't know what to put here in the create section.
controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
#before_filter :authorize_superadmin, except [:show]
#after_action :verify_authorized
def create
# user create code (can't get here if not admin)
end
def index
#users = User.all
authorize User
end
def show
#user = User.find(params[:id])
authorize #user
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
authorize user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def secure_params
params.require(:user).permit(:role)
end
end
views/users/create.html.erb
<%= form_for User.new, url: create_user_path do |f| %>
<div><%= f.label :first_name %><br />
<%= f.text_field :first_name, autofocus: true %></div>
<div><%= f.label :last_name %><br />
<%= f.text_field :last_name, autofocus: true %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :phone_number%><br />
<%= f.phone_field :phone_number, autofocus: true %></div>
<div><%= f.label :street %><br />
<%= f.text_field :street, autofocus: true %></div>
<div><%= f.label :city %><br />
<%= f.text_field :city, autofocus: true %></div>
<div><%= f.label :state %><br />
<%= f.text_field :state, autofocus: true %></div>
<div><%= f.label :zip %><br />
<%= f.text_field :zip, autofocus: true %></div>
<div><%= f.label :password %> <% if #validatable %><i>(<%= #minimum_password_length %> characters minimum)</i><% end %><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %></div>
<div><%= f.submit "Create" %></div>
<% end %>
app/polices/user_policy.rb
class UserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#user = model
end
def index?
#current_user.superadmin?
end
def show?
#current_user.superadmin? or #current_user == #user
end
def update?
#current_user.superadmin?
end
def destroy?
return false if #current_user == #user
#current_user.superadmin?
end
def permitted_attributes
if #current_user.superadmin?
[:role]
else
[:name, :email]
end
end
end
You don't have a create? method in the UserPolicy file so you aren't actually authorizing anything (as far as I can tell).
It should read like this:
# app/policies/user_policy.rb
def create?
#current_user.superadmin?
end
# app/controllers/users_controller.rb
def create
authorize User
# rest of method to create user
end
Also, you don't need to (or want to IMO) have the authorize_superadmin method (you do have the before_filter commented out in the controller, so you aren't calling it) because a) you will call the authorize method in your action and this would be redundant and b) you want to keep your authorization logic in one location: the UserPolicy class. If the authorization fails, it will raise an exception and will not call the rest of the action.
The Pundit documentation is a great resource to get everything setup, but it does take a little bit of trial and error.
I also highly suggest that you create an ApplicationPolicy that you inherit all of your model specific authorization from so that you can catch things that you may not have defined. It is all in the documentation.

Can't mass-assign protected attributes: asset

I followed the tutorial screencast over here: http://www.emersonlackey.com/article/rails-paperclip-multiple-file-uploads. I want my model have multiple pictures upload show up.
I have examined carefully every steps, the most common issue is forget to add assets_attributes to attr_accessible, I have done that. Another issues might bbe forgot to add ID to asset model, i done that too. However, I still have trouble understanding why it happen.
Can't mass-assign protected attributes: asset in app/controllers/posts_controller.rb:24:in `update'
I have already add list of all attributes for a Post to post model. Like:
class Post < ActiveRecord::Base
attr_accessible :name, :content, :assets_attributes
validates :user_id, presence: true
belongs_to :user
has_many :assets
accepts_nested_attributes_for :assets, :allow_destroy => true
default_scope order: 'posts.created_at DESC'
end
Here is the post_controller.rb file:
def edit
#post = Post.find(params[:id])
5.times { #post.assets.build }
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to #post, :notice => "Post has been updated."
end
def create
post = current_user.posts.build(params[:post])
if post.save
flash[:success] = "post created success!"
redirect_to #post
else
#feed_items = []
flash[:failure] = "post created fail!"
redirect_to root_path
end
end
def new
#post = current_user.posts.new #if signed_in?
5.times { #post.assets.build }
end
Here is the template file:
<%= simple_form_for(#post, :html => {:multipart => true}) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :content %>
<%= f.text_field :content %>
<%= f.simple_fields_for :assets, :html => { :multipart => true } do |asset_fields| %>
<% if asset_fields.object.new_record? %>
<P><%= asset_fields.file_field :asset %> </P>
<% end %>
<% end %>
<%= f.simple_fields_for :assets, :html => { :multipart => true } do |asset_fields| %>
<% unless asset_fields.object.new_record? %>
<P><%= link_to image_tag(asset_fields.object.asset.url(:thumb)), asset_fields.objects.asset.url(:original) %>
<%= asset_fields.check_box :_destroy %></P>
<% end %>
<% end %>
Below is asset.rb:
class Asset < ActiveRecord::Base
belongs_to :post
has_attached_file :asset,
:style => { :large => "640x480", :medium => "300x300", :thumb => "100x100"} ,
:path => ":rails_root/public/system/posts/images/:id/:style/:filename",
:url => "/system/posts/images/:id/:style/:filename"
end
Can someone give me some hint ? Thanks a lot!
Your Asset model needs to have attr_accessible on it too - specifically for the asset field.

Rails Devise - Update column from another controller

Rails 3.0
Following these instructions:
https://github.com/plataformatec/devise/wiki/How-To%3a-Require-admin-to-activate-account-before-sign_in
I've generated a migration :approved (boolean) for my devise user.rb. Now I want to edit it with a checkbox from a different controller: unapproved_users_controller.rb.
When I load the form in the edit I get this error: undefined method `user_path'.
routes.rb, the resources for my new controller
resources :unapproved_users
app/models/user.rb, notice that :approved is attr_accessible.
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me, :approved
def active_for_authentication?
super && approved?
end
def inactive_message
if !approved?
:not_approved
else
super # Use whatever other message
end
end
def self.send_reset_password_instructions(attributes={})
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
if !recoverable.approved?
recoverable.errors[:base] << I18n.t("devise.failure.not_approved")
elsif recoverable.persisted?
recoverable.send_reset_password_instructions
end
recoverable
end
end
app/controllers/unapproved_controllers.rb
class UnapprovedUsersController < ApplicationController
def index
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#user.update_attributes(params[:user])
end
end
app/views/unapproved_users/index.html.haml
%h1 Users
= link_to "All Users", :action => "index"
|
= link_to "Users awaiting approval", :action => "index", :approved => "false"
%table
- #users.each do |user|
%tr
%td= user.email
%td= user.approved
%td= link_to "Edit", edit_unapproved_user_path(user)
app/views/unapproved_users/edit.html.haml
= render 'form'
app/views/unapproved_users/_form.html.haml
= form_for (#user) do |f|
-if #user.errors.any?
#error_explanation
%h2= "#{pluralize(#user.errors.count, "error")} prohibited this user from being saved:"
%ul
- #user.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :approved, 'Approved?'
= f.check_box :approved
.actions
= f.submit 'Save'
You need to change the form_for.
It should be
= form_for(#user, :url => unapproved_user_path(#user)) do |f|

Rails - Nested Model Fails to Save

I'm rather new to Rails and I'm writing a signup form that includes nested models. When I submit the form, the user is saved just fine, but the nested model does not save anything to the Subscription db, and the console throws no errors.
I sincerely hope I'm not missing something insanely obvious, and I appreciate any tips you can share. Thanks!
Here is the code-
Models:
class Plan < ActiveRecord::Base
attr_accessible :posts, :name, :price
has_many :users
end
class User < ActiveRecord::Base
belongs_to :plan
has_many :events
has_one :subscription, :autosave => true
accepts_nested_attributes_for :subscription
attr_accessible :subscription_attributes
def save_with_payment
if valid?
customer = Stripe::Customer.create(
email:email,
plan: plan_id,
card: stripe_card_token )
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
end
class Subscription < ActiveRecord::Base
attr_accessible :plan_id, :status, :user_id
belongs_to :user
end
This is the User controller:
def new
#user = User.new
plan = Plan.find(params[:plan_id])
#user = plan.user
#user.build_subscription
end
def create
#user = User.new(params[:user])
if #user.save_with_payment
sign_in #user
flash[:success] = "Welcome to the SendEvent!"
redirect_to #user
else
render 'new'
end
end
This is the form:
<%= form_for #user, :html => {:class => "form-inline"} do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="control-group">
<%= f.label :name, :class => "control-label" %>
<%= f.text_field :name %>
</div>
# A few more fields here and...
# The nested model:
<%= f.fields_for :subscription do |builder| %>
<%= builder.hidden_field :status, :value => true %>
<% end %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary", id: "submitacct" %>
<% end %>
Sample app from RailsCasts
RailsCasts Episode #196: Nested Model Form (revised)
Maybe help you.

Devise: change password

I've been stuck on this for over 24hrs trying to follow other solutions posted here, but I can't get this to work. I'm new to Rails and need help!
I want to get my /users/edit page working so that I can simply change a users password. Originally, I wanted to do it without current_password but I don't mind leaving it in there as long as I can get the password changing and updating.
Here's what I did:
I followed the example in the Devise Wiki and inserted it into my Users controller which I specified to inherit from Devise::RegistrationsController
class UsersController < Devise::RegistrationsController
...
end
I changed my routes:
devise_for :users, :controllers => { :registrations => 'users' } do
match '/users' => 'users#index'
end
And here's my model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :password, :password_confirmation, :current_password
attr_accessible :email, :password, :password_confirmation, :current_password, :remember_me, :full_name, :coach, :bio
validates :full_name, presence: true
end
I was assuming the UsersController I created would override the Registrations controller and that I would be able to change/update password. It works to the extent the redirect to root_path happens (which is only meant to happen after updating without current password) but the new password is not saved (I checked the logs and there was no SQL to show it was saved)...
Any ideas?
Try doing something similar to:
Devise Forgot Password for logged in user
This lets you have a separate view for changing the password.
The key is that I couldn't ever get it working in devise, so I wrote my own solution in the users controller and post to that instead of using the methods provided by devise.
add this to your user form where you want to be able to edit the password:
<%= form_for(#user, :url => url_for(:action => :do_reset_password) , :html => { :method => :post }) do |f| %>
<%= f.hidden_field :reset_password_token %>
<div><%= f.label :password, "New password" %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Change my password" %></div>
<% end %>
users controller:
def do_reset_password
id = params[:id]
# there may be a better way of doing this, devise should be able to give us these messages
if params[:user][:password] != params[:user][:password_confirmation]
flash[:alert] = "Passwords must match."
redirect_to :back
return
end
if #user.reset_password!(params[:user][:password],params[:user][:password_confirmation])
#user.save
respond_to do |format|
format.html { redirect_to '/home', notice: 'Your password has been changed.' }
end
else
flash[:alert] = "Invalid password, must be at least 6 charactors."
redirect_to :back
end
end
config/routes.rb
resource :users do
post 'do_reset_password'
end