Stripe Custom Payment Form: ActiveRecord::RecordNotFound - ruby-on-rails-4

I'm implementing Stripe's custom payment form as described in the Stripe documentation (Stripe Documentation, Stripe Git Repository) and I have trouble setting the instance variable #userafter form submission.
Controller
def payment
#user = User.find(session[:user_id])
if params[:stripeToken] != nil
#token = params[:stripeToken]
if #user.update(:stripe_pmt_token => #token)
create_stripe_customer
redirect_to confirm_path
else
render 'payment'
end
end
end
def create_stripe_customer
Stripe.api_key = "<TOKEN>"
#Create a Customer
#customer = Stripe::Customer.create(
:source => #user[:stripe_pmt_token],
:description => "#{#user.first_name}"
)
#user.update(:stripe_customer_id => #customer.id)
end
Routes
get 'payment' => 'checkout#payment'
post 'payment' => 'checkout#payment'
Server Logs
Started POST "/payment" for 69.23.75.159 at 2016-03-31 11:49:01 +0000
Processing by CheckoutController#payment as HTML
Parameters: {"stripeToken"=>"tok_17v<TOKEN>"}
Can't verify CSRF token authenticity
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", nil]]
Completed 404 Not Found in 2ms (ActiveRecord: 0.2ms)
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=):
app/controllers/checkout_controller.rb:5:in `payment'
When the payment page loads for the first time the instance variable #user = User.find(session[:user_id])gets set correctly. However, when the payment form is submitted and the code of def paymentexecutes the second time the instance variable is not set correctly.
Error message: ActiveRecord::RecordNotFound and Couldn't find User with 'id'=
Somehow rails cannot retrieve the [:user_id] value from the session, even though it is set and I am able to display it on the payment page with <%= session[:user_id] %>. Please let me know why this is the case and what I need to change. Thanks!

This issue seems to be unrelated to Stripe but to User.find(session[:user_id]) where the session[:user_id] is empty.
How do you handle user authentication and session? You can look using the Devise gem for that.
Also, using User.find means that you need to handle the exception ActiveRecord::RecordNotFound somewhere (preferably universal) in your application. Otherwise, using User.find_by and checking if the result returns nil is an alternative.

Related

How do I pre-populate my Rails 4 application with a Google user?

I’m using Rails 4.2.5 with the “omniauth-google-oauth2” gem. In my application, the only way users will be able to sign in is through their Google or Facebook logins. What I would like is to pre-populate my application with an initial user, me (email = ‘davea#gmail.com”), with the admin role. It would be nice to do this programmatically so that when I roll this out to other environments I can use the same code.
My roles table (through db/seeds.rb) has the roles
Admin
User
and my app/model/user.rb file has
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
I’m not sure how to do what I want, however, and so would value some counsel.
Now assume you have your Google uid. Just create a user from your seeds, e.g.:
user = User.new(
provider: "google",
uid: "your-google-id",
email: "davea#gmail.com",
name: "Your name"
)
user.roles << admin_role # Replace this line with your role assignment
user.save # Perhaps use save(validate: false) if there're validations for other fields
With that, when you log in with Google, the omniauth logic should be able to find the seed user, which means you'll be able to act as admin.
Please do note this assumes you won't need the Google oauth token to do any further operation as you don't have that saved, and from your from_omniauth it doesn't save if the user record exists already.
P.S. from your example code, Oauth info is saved directly to User model (provider and uid). With that, I'm afraid a user won't be able to log in with Facebook and Google at the same time as both would want to save to these two fields.
Update: pasting a model from my codebase that is a separate model from User which allows multiple providers login. Of course the controller needs to update to use Authorization instead of User. Just in case it helps.
class Authorization < ActiveRecord::Base
belongs_to :user
def self.from_omniauth(auth)
authorization = where(auth.slice(:provider, :uid)).first_or_create
return authorization if authorization.user
if user = User.where(email: auth.info.email).first
authorization.bind_user(user)
else
user = authorization.create_user(auth.info)
end
authorization
end
def bind_user(user)
self.user = user
save
end
def create_user(info)
user = User.new(
email: info.email,
password: Devise.friendly_token[0, 20],
first_name: info.first_name,
last_name: info.last_name,
)
user.save(validate: false)
bind_user(user)
user
end
end
You will have to run a request through your controller through your seed.rb file in order to execute the OAuth2 process.
Since you will most likely have to enter in credentials or select your google account from a GUI, I suggest running a system command in your seed.rb file that opens a browser to the url of your authorize action.
# Mac:
system("open <url_to_authorize_action>")
If this needs to be serialized, immediately after, add a while loop that checks the DB every N time threshold to see if that user is authorized.
while <user_not_authorized> do
sleep <N seconds>
end
You could roll this to multiple dev environments but obviously not production.

Pundit: auhorize Index in nested resources

Using Rails 4.2.4 with Devise (3.5.2) and Pundit (1.0.1). Decent_exposure (2.3.2).
I have a simple nested associaton for User and Idea:
class User < ActiveRecord::Base
has_many :ideas
...
class Idea < ActiveRecord::Base
belongs_to :user
...
In routes.rb
devise_for :users
resources :users do
resources :ideas
end
Then I am simply trying to disallow access to users/1/ideas if current_user is not the owner of the Ideas (in this example, if current_user.id != 1).
I can not figure out how to do it. I am able to show just the current_user Ideas in the Index view with:
[Ideas controller]
def show
authorize idea
end
[Idea policy]
def show?
#current_user == #idea.user
end
But how can I prevent a user to simply navigate to other user's Idea index page?
I guess that in Ideas controller I should use something like:
def index
authorize user
end
But then what? How can I send to the User Policy the info regarding the Idea collection?
Or should I authorize via the Idea Policy itself?
Duplicating my response on GitHub here because this gets more traffic.
One way is to create a stub Idea owned by the user to authorize against.
def index
#user = User::find(params[:user_id])
idea = Idea.new(user_id: #user.id)
authorize idea
# ...
end
and an index? method in your IdeaPolicy
def index?
record.user_id = user.id
end
Another way is to change what you're authorizing against. Instead of authorizing against Idea, authorize against the User.
def index
#user = User::find(params[:user_id])
authorize #user, :show_ideas?
# ...
end
and create a new show_ideas? method on your UserPolicy
def show_ideas?
user.id == record.id
end

Extra Attributes while login with facebook using OAuth and Devise Rails 4 app

i've been searching for a way to do the following and I haven't succeded yet.
I have already implemented Facebook Login with my app, but i am looking for a way to ask the user for some extra attributes that are not provided by Facebook (Like home address for example).
I tried redirecting the user to the edit_user_registration page once he is logged in for the first time, but I can't add the new attributes because the user won't know his password due to the fact that is provided by Facebook and its encripted.
Thanks in advance for any answers!
First override Devise::OmniauthCallbacksController controller. Add a method for Facebook connection in which you have to do two things. First if the user is already on your system, this method should log the user in directly.
Second if the user is new, you should build a user object, extract the parameters you need from Facebook and update this user object with these parameters. After that you should render a page where the user can input their own password and add the extra information you need.
omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
user = User.find_by_fb_token(token)
if user.present?
sign_in user, event: :authentication
else
#user = User.new(**Facebook parameters**)
render 'devise/registrations/after_social_connection'
end
end
end
In the after_social_connection view, add a form for #user where you will have all attributes from Facebook prepopulated. Do not forget to add :password and :password_confirmation fields for the user to be able to have a password on your own application. In this view you can add whatever attributes you like the user to input.
Since you are using Rails 4, you also need to override devise parameters sanitizer (strong parameters) to be able to input extra fields in a form. This can be done in the application controller (the lazy way)
application_controller.rb
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:password, :password_confirmation, :email. :name, :biography, :profile_picture) }
end
end
For further reading
https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview

devise 'authorize User' results in undefined method

I'm starting with the Rails 4.1 Pundit / Devise app from RailsApps.org and continue to get undefined method errors when 'authorize User' is called in the User controller. The user can register, log in, and edit their account info. When the Users link is clicked, the following results:
NoMethodError in UsersController#index
undefined method `authorize' for #
Here is the UsersController...
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = User.all
authorize User # <== This is the line the error occurs on
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
and the ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
Any thoughts on how to resolve this?
Thanks in advance!
Comment: This is from the RailsApp Pundit Quickstart guide to explain authorize
The keyword authorize is a helper method that provides a shortcut to a longer statement that implements the actual authorization. We never see the full statement because we use the helper method, but if we were to use it, it would look like this:
raise "not authorized" unless UserPolicy.new(current_user, User).index?
The authorize helper method finds a UserPolicy class and instantiates it, passing the current_user object and either the User class or an instance of the User model, and calling an index? method to return true or false. You may wonder why we can provide either the User class (as in the index action) or the #user instance variable (as in the show action).
Pundit looks for a policy object when authorize is called from a controller action. We already saw that Pundit will find a UserPolicy if given any of these arguments:
authorize User – the User class
authorize #user – an instance variable that is an instance of the User class
authorize user – a simple variable that is an instance of the User class
authorize #users – an array of User objects
To me, it seems as if the helper method is found sometimes like in show and update but not index.
It looks like this issue is being discussed here: https://github.com/RailsApps/rails-devise-pundit/issues/10
Basically, your solutions are to:
A) Restart the rails server and the problem should go away. You will have to do this whenever the problem shows up (editing the file, etc). (It shouldn't happen in production if it's any consolation)
B) Move the code in config/intiializers/pundit.rb in to ApplicationController (without the included do...end block)
User is a class name, not an instance. Also authorize used for create/update/edit actions. For index you should use Policy.
For example, UserPolicy:
def index
#users = UserPolicy::Scope.new(current_user, User).resolve
end

Stop redirect_to and link_to from controller method ruby

May be my question would be awkward but i am new to ruby.
I am working on a project. My task is that when in table status has value "submitted" then the link "advance_status" shouldn't be clicked or redirected or render.
I know logic will be implemented in controller but what should i write in my advance_status method in the controller?
In your controller you can do something like:
def advance_status
if thing.status == 'submitted'
# Redirect to previous page if status is 'submitted'
session[:alert] << 'This is not allowed'
redirect_to :back and return
end
# Other code...
end