Allowing non-admin to create order in spree backend - spree

I would like to create a role which is not the admin that can create order in the spree backend, but I couldn't find the combination of actions that I need to activate for that user. I tried adding the default user permission and allow role to :manage Order, LineItem, Product, Variant to no success. Right now the role can create new order, but when I search for product to add to cart, the API controller would not allow it.
Update:
I try disabling the authentication for the API Spree::Api::Config[:requires_authentication] = false and it solve the problem. So it's definitely a problem with API authentication.
I am using solidus which is a fork of spree 2.4
can_be_like_customer
can :manage, Spree::Order
can :manage, Spree::LineItem
can :manage, Spree::Product
can :manage, Spree::Variant
def can_be_like_customer
can :display, Spree::Country
can :display, Spree::OptionType
can :display, Spree::OptionValue
can :create, Spree::Order
can [:read, :update], Spree::Order do |order, token|
order.user == user || order.guest_token && token == order.guest_token
end
can :display, Spree::Product
can :display, Spree::ProductProperty
can :display, Spree::Property
can :create, Spree.user_class
can :display, Spree::State
can :display, Spree::Taxon
can :display, Spree::Taxonomy
can :display, Spree::Variant
can :display, Spree::Zone
end
Log from terminal
Processing by Spree::Api::VariantsController#index as JSON
Parameters: {"q"=>{"product_name_or_sku_cont"=>"bag"}, "token"=>"", "in_stock_only"=>"true", "_"=>"1447839886731"}
Spree::User Load (0.4ms) SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."deleted_at" IS NULL AND "spree_users"."spree_api_key" = $1 LIMIT 1 [["spree_api_key", ""]]
Rendered /Users/harins/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/solidus_api-1.0.2/app/views/spree/api/errors/must_specify_api_key.v1.rabl (0.7ms)
Filter chain halted as :authenticate_user rendered or redirected
Completed 401 Unauthorized in 1778ms (Views: 1775.1ms | ActiveRecord: 0.4ms)

You need to override spree ability and add your role condition in initialize method. in that method add following code
# for orders
can :admin, Order
can [:modify, :display], Order
can [:create, :cart], Order
can [:admin, :display, :modify], LineItem
can [:admin, :display, :modify], Adjustment
can [:admin, :display, :modify], Payment
can [:admin, :display, :modify], ReturnAuthorization
can [:admin, :display, :modify], CustomerReturn
# for products
can :admin, Product
can [:modify, :display, :stock], Product
can :create, Product
can [:admin, :manage], Image
can [:admin, :manage], Variant
can [:admin, :manage], ProductProperty
can [:admin, :modify], OptionType

Turns out the admin user I created did not have an API generated. Had to go to that user in the admin section and generate an API key using the superadmin account (spree#example.com).

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.

Stripe Custom Payment Form: ActiveRecord::RecordNotFound

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.

Devise get remote_ip in model validator

I'm trying to validate user sign ups against a Spam service that requires the IP and eMail. I'm using Rails 4.2 with Devise. Devise has the current_sign_in_ip attribute, but it is nil during sign up.
Is there a way to pass the value of request.remote_ip from the Devise registration/create controller action to the User model on sign up?
I tried the following, but the key is not added:
class Users::RegistrationsController < Devise::RegistrationsController
def create
sign_up_params[:current_sign_in_ip] = request.remote_ip
super
end
end
Finally figured it out. The before_filter needed to be uncommented and the param added to the permit list.
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :configure_sign_up_params, only: [:create]
def create
params[:user][:current_sign_in_ip] = request.remote_ip
super
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.for(:sign_up) << :current_sign_in_ip
end
end

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