How to restrict request based on params in ruby on rails - ruby-on-rails

I want to update my API to v5 (post /api/v5/registrations) and still want to support v1,v2,v3,v4. Under v5, I do not want process the create/update request if a params with registration[:secret_token] is missing from request body.
how to do this?
Invalid request= {:user=>{:name=>XYZ, :roll_number=>1}}
Valid Request: {:user=>{:name=>XYZ, :roll_number=>1, :secret_token=>"DSGASDFG34534"}}
Thanks in Advance

You can add following code in application_controller.rb
before_action :authorize!
private
def authorize!
head :forbidden unless params[:secret_token].present?
end

Related

Session not maintained on post request, angular2, rails5

I'm trying to get my head around the difference of how sessions are handled between GET and POST request.
In rails I'm setting a current_user with a session variable. This works fine for all get requests BUT when I do a POST it seems like the session variable is not carried over. This results in current_user = null
I guess these pictures explains it well.
Cookies on a working GET request - Working get request
Cookies on a NOT working POST request - enter image description here
Why is that?
Do I have to change the header in the angular2 request?
Is it a setting in rails to allow sessions with POST requests.
Here is some of my code...
Angular: Version 1 - Doesn't set my current_user
postSomeData( id : number ){
return this._http.post( "/api/something/" + id,
JSON.stringify("{id: id}") )
.map( response => response.json() )
}
Angular: Version 2 - Doesn't set my current_user
postSomeData( id : number ){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this._http.post(
"/api/lists/private/translation/" + id,
JSON.stringify("{id: id}"),
{ headers: headers, withCredentials: true } )
.map( response => response.json() )
}
Rails: ApplicationController
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
Rails 5.0.0.1
Angular 4.0.5
People usually do something like this in the ApplicationController or in a helper:
def set_user
unless #current_user.present?
#current_user = User.where(id: session[:user_id]).take || User.new
end
end
Then you can query if the user has any relations or has an ID, etc...
I faced the very same situation. Turned out that the problem is with the rails server, not angular.
When I make http post request the following statement shows in the logs and my session gets terminated.
WARNING: Can't verify CSRF token authenticity
I searched for this and the discussion on this thread did help me.
Adding the following line in my controller allowed me to make http post reqests.
skip_before_filter :verify_authenticity_token

Any alternative to redirect_to in rails api only application?

I would like to redirect to another url in rails controller action. like
def action_name
redirect_to url
end
I know i can do it simply using the above method. But i want to redirect in only one action in all my application. because of this i don't want to include this in controller.
include ActionController::Redirecting
is there any other way to redirect to a uri in api only applications. Thanks.
You can use respond_with which will respond with a appropriate response depending on the request type and the status of the model you pass to respond_with.
def create
#thing = Thing.create(thing_params)
respond_with(#thing)
end
This will give 201 - Created status and a location header if the the request is successful and a 422 - Unprocessable Entity if the validations fail.
Alternativly you can use head to send a header only response with no body.
def action_name
head :not_found, location: url
end
Note that :not_found could be any appropriate HTTP status.
If your using Rails API:
Route your path, and take the params, and return:
redirect_to controller: "client", action: "get_name", params: request.query_parameters and return

How to redirect from /:id to /:friendly_id

Is is possible to force a 301 redirect when someone attempts to browse to a page using the old /:id URL, rather than than the preferred /:friendly_id link?
Apparently such redirections help to tell Google that you have updated the link.. so it stops displaying the old non-friendly link.
With the latest version of friendly_id (5.0.3 at the time of writing this answer) and Rails 4, I do this in the controller:
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
...
private
def set_item
#item = Item.friendly.find(params[:id])
redirect_to action: action_name, id: #item.friendly_id, status: 301 unless #item.friendly_id == params[:id]
end
end
Here's a description of the redirect_to line, broken down piece by piece:
action: action_name retains the action that you're connecting to (which can be show, edit, update, or destroy based on the before_action that's in place) so that if you're accessing /items/1/edit you will be redirected to /items/pretty-url/edit
id: #item.friendly_id ensures that the URL you're being redirected to is the pretty URL
status: 301 sets the redirect to the status of 301, for SEO
unless #item.friendly_id == params[:id] makes sure that we're not redirecting people who access #item through its pretty URL
just defined the redirection inside the routes file
get '/:old_id', to: redirect {|params, req| "/#{X.find(params[:old_id]).friendly_id}" }
While James Chevalier's answer is correct, you can extract this method to the ApplicationController in order to use with any model that uses FriendlyId:
def redirect_resource_if_not_latest_friendly_id(resource)
# This informs search engines with a 301 Moved Permanently status code that
# the show should now be accessed at the new slug. Otherwise FriendlyId
# would make the show accessible at all previous slugs.
if resource.friendly_id != params[:id]
redirect_to resource, status: 301
end
end
As you can see it's also unnecessary to pass a specific action key to redirect_to. Passing a Rails model to redirect_to will automatically attempt to access the show action on the associated collection resource route (assuming it's set up that way). That also means it's unnecessary to pass an id key since FriendlyId always returns the latest slug in the model's #to_param.
Not being a huge fan of unless (confusing semantics) I tend to shy away from it but that's more my personal preference.
Routes
I don't think your routes are the problem here
The problem is the backend handling of the route (I.E whether it uses friendly_id or not). All Google will see is this:
domain.com/users/45
domain.com/users/your_user
If both of those routes work, Google will be happy. I think you're alluding to the idea that if you change the routes to only handle your_user, you'll need to be able to get Google to appreciate the redirects
Redirects
Considering you can handle both id and slug in the backend (we have code for this if you want), I'd handle redirects using the ActionDispatch::Routing::Redirection class:
#config/routes.rb
begin
User.all.each do |u|
begin
get "#{u.id}" => redirect("#{u.slug}")
rescue
end
end
rescue
end
Yes it is possible, you need to define both routes on your config/routes.rb
get 'path/:id' => 'controller#action'
get 'path/:friendly_id' => 'controller#action_2'
then in your legacy action method you need to provide a
return redirect_to controller_action_2_path(friendly_id: friendly_id),
status: :moved_permanently
this will generate a 301 response code. Which will eventually make bots start hitting your new pattern, without losing any of your traffic or indexing (SEO).

ROR: sending an object in response

I have 2 ruby on rails apps. With app A I post app B some data (in the form of a hash). I then want app B to send a hash on this data (with some modifications) back to app A in the response.
I have tried the code below App A
response = Net::HTTP.post_form(uri, params)
quotes.push(response.body)
and in App B
details = get_details //returns a hash
respond_with details
But its not working. Is what im doing even possible? Is there a way I can place this hash in my response?
Any help would be appreciated
Solution #1
If you use respond_with you need also specify formats which your app should respond to. For this you should use respond_to method.
Example:
class TestController < ApplicationController
respond_to :json
def index
details = get_details
respond_with(details)
end
end
Also check this good article about respond_to method.
Solution #2
Just use render json: {...} in your controller action.
Example:
class TestController < ApplicationController
def index
details = get_details
render json: details
end
end
In your app A response.body will contain a string with the data from your app B. So you need to parse that string.
In your app A:
require 'json' # this is unnecessary if app A is a Rails app
response = Net::HTTP.post_form(uri, params)
parsed_response = JSON.parse(response.body)
quotes.push(parsed_response)
The rails way to do that is using JSON as an exchange format. have a look at the guides for how to use that: http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-json
It is also possible to use ActiveResource for such a communication. It allows direct access to your rails API.

Force Omniauth to use json for callback?

I'm attempting to integrate Omniauth into an API written in rails, to be used by an Android application. This means that I want to be able to handle the omniauth callback with JSON.
By default, Omniauth sends its callbacks to /auth/:provider/callback, is there a way that I can force Omniauth to instead send its callbacks to /auth/:provider/callback.json?
You can specify format in action where handling callback:
# in route.rb
match '/auth/:provider/callback' => 'authentications#create'
# in authentications_controller.rb
class AuthenticationsController < ApplicationController
def create
# your code here
respond_to do |format|
format.json { ... } # content to return
end
end
end
I managed to do that by inspecting the request object on my rails backend.
When I make the request on my app, I add data on the submition defining the format:
format: "json"
And the omniauth then makes the callback for
/auth/:provider/callback
Wich in my case matches
sessions#create
as an HTML request. But once there, if you look at your request object in rails, and search for the omniauth.params hash you'll see that one of the values there is the format passed on tha data of the initial request:
"omniauth.params"=>{"format"=>"json", "provider"=>"facebook", "code"=>"..."}
Its a mather of you searching for this "format"=>"json" and doing a render json as an answear.
I hope it solves your problem.
# app/controllers/users_controller.rb
def authenticate
#credentials = request.env['omniauth.auth']
render json: #credentials
end
# config/routes.rb
get '/auth/:provider/callback', to: 'users#authenticate', as: 'user_auth'
And then all requests made to /auth/:provider/callback will return a JSON response by default.

Resources