The example below is how I'm authenticating my users today:
def create
if is_internal_request(params[:authenticity_token])
#user = User.authenticate(params[:email], params[:password])
if #user
session[:user_id] = #user.id
render :json => true
else
render :json =>
{
error:
{
code: 1,
message: t('globals.errors.authentication.user-not-found')
}
}.to_json
end
end
end
Pay attention to this fragment:
render :json =>
{
error:
{
code: 1,
message: t('globals.errors.authentication.user-not-found')
}
}.to_json
Based on it, I want to know if it's organized and solid. I mean, is that something in the edge of the right way?
Forward thinking
Lets suppose that there are some places in my application that check about user's e-mail availability. I want to be DRY and reuse that verification every time I need to use it. If the way that I'm doing the validation (as below) isn't "perfect" at all, how can I create a validation layer that can be useful every time I want?
I mean, instead of create and recreate that fragment of code each time that I want to validate, what's the best way to do something like this?:
email = params[:email]
unless email_is_available(email)
render :json => { message: 'E-mail isn't available' }
end
With "what's the best way to do something like this?" I'm saying, where I have to place email_is_available function to get it working right?
Every Controller can access ApplicationController methods as they inherit from it.
Anyways, I'd recommend using a gem like Devise for a more complete solution.
GL & HF.
What about something like this?
if #user.valid?
render :json => true
else
error_hash = {}
#user.errors.each{|k,v| error_hash[k] = "#{k.capitalize} #{v}"}
#this will give you a hash like {"email" => "Email is not available"}
render :json => {:errors => error_hash}
end
At the client end, you will get this back (eg as an object called data), see if there is a value for data.errors and then display the various error messages to the user.
Note i haven't plugged in any translation stuff but you can do that :)
I'd suggest you take a look at Ryan Bates' Railscast on authentication from scratch. It walks you through all the issues and is much lighter weight than relying on something as big and heavy as Devise.
http://railscasts.com/episodes/250-authentication-from-scratch
Related
I have a webhooks controller which listens for when a new customer is created in Shopify.
def customer_created_callback
#first_name = params[:first_name]
#last_name = params[:last_name]
#email = params[:email]
#customer = Customer.new(first_name: #first_name, last_name: #last_name, email: #email)
if #customer.save
redirect_to customers_url
else
puts "error"
end
end
I am wondering if I should be creating the customer in this controller action. It seems like I should be handling the customer creation in Customer#create. But I'm not sure if it's a good idea to be passing all these params to a new action.
What's a good example of where I should be redirecting_to? I thought the idea of a webhook is that it's happening in the background so no page is actually going to be rendered..
Hope those make sense.
It seems like I should be handling the customer creation in Customer#create
Where the code lives is up to you but you must keep it DRY. Personally I like to use Service Objects since they make testing easier.
What's a good example of where I should be redirecting_to?
You need to return a 200 response with no content:
render :nothing => true, :status => 200
Typically you'll use a background job which will use the service object when it runs. This post on Webhook Best Practices is an excellent resource to get acquainted with the in's and out's of web hooks.
I'm building a Rails server for the back-end of an iPhone app. Rails sends JSON to the front-end and I find myself doing something like this.
#user = User.find(1)
#user["status"] = "Some cool status"
render :json => #user.to_json
In my rspec tests I get
DEPRECATION WARNING: You're trying to create an attribute `status'. Writing arbitrary attributes on a model is deprecated. Please just use `attr_writer` etc.
I find it hard to find an appropriate alternative when it's just as easy to write an key value to the object that will be sent to the iPhone.
My question is what are some viable alternatives to what I'm trying to do and what's specially "wrong" with my code, besides the deprecation.
You can convert your User object to hash and then mix additional keys to it:
class User
def to_hash
hash = {}
instance_variables.each {|var| hash[var.to_s.delete("#")] = instance_variable_get(var) }
hash
end
end
And in your controller:
user = User.find(1)
user = user.to_hash
user[:status] = "Some cool status"
render :json => user.to_json
PS. No need to use instance variable #user as you render json anyway, local user variable is good enough.
In a standard Rails controller, I'd create a record like this:
#user = User.new(params[:user])
This assumes that the form parameters that come in are nested.
I've been playing with Backbone.js and I noticed that by default, Backbone doesn't nest the parameters the way a normal Rails form might, which is actually something I expected. So I'm wondering what I should do...
Do I
figure out on the server-side if it's a request from Backbone by looking at accepts headers, etc and manipulate the params myself so I can keep my controller code small:
do_some_params_manipulation_with(params)
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html {redirect_to users_url}
format.json {render :json => #user.to_json }
end
end
Or, do I instantiate the object in each branch which ends up with repeated code but might be more maintainable in the long run....
respond_to do |format|
format.html do
#user = User.new(params[:user])
if #user.save
redirect_to users_url
end
end
format.json do
#user = User.new(params) # and rely on mass-assignment protection
if #user.save
render :json => #user.to_json
end
end
end
or do I modify my Backbone.js models by overriding the .toJSON method (which I'm not entirely sure how to do because I don't know enough about Backbone.js yet) so that it nests the params?
In this situation, I have access to both sides of the app, but I am interested in what others are doing.
It's nice when you can have the general Rails forms and Backbone forms match with respect to the root node. That's why in my last application I chose to override the Backbone models' toJSON method.
You could override the global toJSON method as Raimonds Simanovskis suggested. But even the non-DRY way approach isn't so bad. Just one line of boilerplate for each model definition:
// Depends on Underscore.js
User = Backbone.Model.extend({
toJSON: function() {
return { user: _.clone( this.attributes ) }
},
// Your other methods here
});
Edit: Corrected code sample. Sorry for the errors, I was translating from CoffeeScript to JavaScript.
If you are using the backbone-rails gem, looks like you can do
var User = Backbone.Model.extend({
paramRoot: 'user'
});
Around line 45 on github
Credit PL J and stream 7 on this link
I have made a little hack to namespace save requests under model.name property.
It monkey patches toJSON() during sync() call only and restores original method so you can use it as usual.
I have implemented it in CoffeeScript.
Check it here
It should be noted that if you opt for the currently accepted answer (patching toJSON at the model level) you are affecting reading as well. Maybe that goes without saying, maybe not. But you will have a lot of work to do when rendering models/collections if you put this patch into affect in a backbone app. Therefore, I personally wouldn't use it as-is.
In one of the answers to Rails mass assignment and Backbone.js there is mentioned patch https://gist.github.com/719080 which I think will do what you need.
As of Rails 3.1 there's now a new initializer called wrap_parameters.rb which handles this problem by default. The code that handle this case is:
# Disable root element in JSON by default.
ActiveSupport.on_load(:active_record) do
self.include_root_in_json = false
end
Bada bing!
I've inherited a little rails app and I need to extend it slightly. It's actually quite simple, but I want to make sure I'm doing it the right way...
If I visit myapp:3000/api/persons it gives me a full list of people in XML format. I want to pass param in the URL so that I can return users that match the login or the email e.g. yapp:3000/api/persons?login=jsmith would give me the person with the corresponding login. Here's the code:
def index
if params.size > 2 # We have 'action' & 'controller' by default
if params['login']
#person = [Person.find(:first, :conditions => { :login => params['login'] })]
elsif params['email']
#persons = [Person.find(:first, :conditions => { :email => params['email'] })]
end
else
#persons = Person.find(:all)
end
end
Two questions...
Is it safe? Does ActiveRecord protect me from SQL injection attacks (notice I'm trusting the params that are coming in)?
Is this the best way to do it, or is there some automagical rails feature I'm not familiar with?
Yes, the code you listed should be safe from SQL Injection.
Yes, this is generally acceptable rails code...but
There are some oddities.
Your index action talks about #person and #persons. By convention, #persons is expected, and #person is unusual. I suspect you can eliminate #person, and clear things up in one swoop. Something like this (untested):
def index
#persons = if params[:email]
Person.find_all_by_email(params[:email])
elsif params[:login]
Person.find_all_by_login(params[:login])
else
Person.all
end
end
Don't forget to update your view -- I suspect it's still looking for #person. If your view is doing anything "interesting" with #person, you probably want to move it to your show action.
Here's the ruby on rails security guide section on SQL Injection. It looks like what you have, using hash conditions, is pretty safe. Sounds like using Person.find_by_login or Person.find_by_email might be a little better.
I am making an activeresource call to a service, and I'd like some custom error messages as feedback. I have some validations that aren't normal model validations, so I can't just return #object.errors.
So, for instance, one of my validations is this. Two objects have a many to many relationship, but I want to restrict one object to only have a limited number (say 2) of relationships to other objects. Here's some code:
In the client:
response = Customer.find(customer_id).put(:add_user, :user_id => user_id)
This puts a request to add a user to the customer. Then in the service I want to check that this addition is valid.
def add_user
#user = User.find(params[:user_id])
#customer = Customer.find(params[:id])
if #customer.users.length > 2
render :xml => "ERR_only_2_users_allowed", :status => :unprocessable_entity
end
end
Here's my problem. In active resource, if the return status is an error, the client side completely fails. I could change the status to 200 and I get back the body err msg fine, but this seems to defeat the purpose of having error reponse codes.
I can put the whole request call from the client in a begin/rescue block
begin
response = Customer.find(customer_id).put(:add_user, :user_id => user_id)
rescue ActiveResource::ResourceInvalid => e
#return error code
end
but when I catch the 422 (unprocessable_entity) response, I get nothing of the body back, so I don't get my custom error message. response = nil
Does anyone know how I can achieve these custom error message with the proper response codes?
This may or may not be your problem, but both of ours seem very close. I'm using a custom put method, but his should work for you too. What's going on is that the code that does this:
rescue ResourceInvalid => error
errors.from_xml(error.response.body)
end
Is only working with the standard save method. If you want errors added when other methods are called it looks like you need to do it yourself.
I had to add it to
vendor/rails/activeresource/lib/active_resource/custom_methods.rb
Here is what my diff from git looks like:
old code:
def put(method_name, options = {}, body = '')
connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
end
new code:
def put(method_name, options = {}, body = '')
begin
connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
rescue ResourceInvalid => error
errors.from_xml(error.response.body)
end
self
end
So look at the stack trace when get the exception thrown for the 422 and see which method it's calling exactly. Then add something like what I have and you should be good to go.
Don't ask me why the activeresource folks thought validations should only work with their save method. the save method does a create or update, but calling 'put or post' is the exact same thing, IMO. If we want validations to work on save we want them to work on put and post...anyway give it a shot.
I'm not sure if i need the self at the end...i may not. I'm not totally done with this as I just figured out how to make it work.
Erik
I think that your problem might be the response isn't an xml document but just a plain string. Try changing your render statement to something like:
render :xml => { :error => "ERR_only_2_users_allowed" }, :status => :unprocessable_entity