Encrypting Parameters across controllers - ruby-on-rails

I need to pass a parameter from one method in a controller to another.
From my understanding I have to pass the parameters as a GET exposing it in the url string.
What is the best way to encrypt the data so no one can see what is actually getting passed in the string? Also, is there a way to pass it via POST or is my original understanding correct?

I haven't used RoR, but in the web world, this problem is solved with sessions. Using sessions you can store the parameters on the server and avoid sending sensitive data with GET or POST (both are insecure).
The Ruby on Rails Security Guide looks like a great read related to this.

I suggest you abstract your code into lib/ so that you don't have to call additional methods. Instead of making a new HTTP request, just put the code in a central place and call it from there.
class MyController < ApplicationController
def index
MyLibrary::Thing.do_stuff
end
def show
MyLibrary::Thing.do_stuff
end
end
# lib/my_library/thing.rb
module MyLibrary
module Thing
def self.do_stuff
# do stuff!
end
end
end
That way you can access the same code in multiple actions, without doing extra HTTP requests.

Related

Does rails provide a request context?

I'm new to rails, I'm trying to figure out if rails have a request context where I can store variables and access them across the context of the request? I'm using rails 4.2.11.
I've tried to have a before filter in
class ApplicationController < ActionController::Base
before_action :set_request_context
def set_request_context
Thread.current[:request_context] = {:correlation_id => "some_unique_id"}
end
end
And then I'm accessing this variable else where in code. I'm sure this is not the right way because the application runs on ngnix and it may not guarantee that a request would be served by a single thread throughout.
When a request is processed within the method of the mapped controller a new object of a class is instantiated which needs access to this request_context.
class CreditCardPaymentsController < ApplicationController
def new
#paypage_id=PaypageIdRequest.new(:merchant_id => merchant_id)
end
end
Within the methods of PaypageIdRequest class I need to access the correlation_id I've set in :request_context.
Please note that I cannot send the variable to PaypageIdRequest class at the time of initialisation because there are so many places I'll have to send this variable and it would be bad practice to send it at all places considering Aspect Oriented Programming.
In Rails (and Rack applications in general) you have env which is a hash like object that contains the request object. env is used by middleware like Warden to inject themselves into your application.
However this is not a global. It is available in the routes, the controller and in views but not in models which are not request aware, neither is it available on whatever else you add to the MVC model like service objects or decorators unless you pass it.
If you want it to be available in your PaypageIdRequest class you need to use constuctor or method injection or make whatever you are saving a global.
Yes, Rails offers this functionality through it's CurrentAttributes API.
https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html

How to set variables across models for a single request in rails?

The scenario: I need to give models access to API tokens stored in the session.
Background: I have an API-driven rails 3 application utilizing DataMapper(DM) and a DM adapter to interface with the API. Each DM model has a corresponding REST-ish API endpoint much like you get with rails scaffolding. The API requires various headers for requests, including API tokens, keys, ids etc. The headers have nothing to do with the requested data, they exist for authorization and tracking purposes only. A number of these tokens are stored in the session. I want a clean way to make these API headers available to any model during a request.
Possible solutions:
1. Passing session variables from the controller to the models
The obvious answer is passing the tokens in a hash or other object from the controller to the models. A controller action might have the following: #user = User.find(params[:id], api_headers).
The problem is needing to override any model method to accept the additional api_headers object. Not counting methods defined by Rails and DataMapper, there are hundreds of methods already defined in the application models that would need to be rewritten. So I'm ruling out a rewrite, and this also doesn't seem like a good solution since it would require overriding a ridiculous number of DM methods like the User#find example above.
2. Some metaprogramming hack
I could catch any ArgumentError's on DM's base class and check if the last argument is the api_headers object, then set the values as instance variables and invoke the requested method. This thought exercise already has me cringing at dealing with optional arguments etc. If given long enough I could probably create a functional Frankenstein that should get me fired but probably wouldn't.
3. Use a singleton (current preferred solution)
In the application controller set a before_filter to dump the session-stored API headers into a singleton ApiHeaders object. Then any model making an API request can get that singleton with the required API headers.
An additional after_filter* on the application controller would set all attributes to nil on the ApiHeaders singleton at the end of the request to prevent leaking headers between requests.
This is currently my preferred solution but I don't like that the API header values could potentially carry over into other requests if the after_filter doesn't get invoked. I don't know in which scenarios this might happen (in an application error perhaps?) which raises concerns. All I know is the values don't necessarily die with the request.
4. Custom code
Drop support of DataMapper and the custom API adapter and manually make all API calls, passing through all required API headers. Besides the fact I don't have time for this level of rewrite, why use a framework at all if you have to throw a huge chunk out to support a custom auth scheme?
Summary
What's the cleanest way to get these pesky API tokens from the session into the bowels of the application where they can be sent with each API request? I'm hoping for a better solution than those listed above.
* An alias for after_action
I set the current user and the request information on my User model using the request_store gem which is just a tiny shim over thread local storage with a bit of clean-up.
This makes the information available from any of my models via the User class. I have User.current, User.request and User.location available wherever I need it.
Your controller just has to set User.current and User.request once it has authenticated the user.
Example User model:
# models/user.rb
require 'request_store'
class User
def self.current
RequestStore.store[:current_user]
end
def self.current=(user)
RequestStore.store[:current_user] = user
end
def self.request
RequestStore.store[:current_request]
end
def self.request=(request)
# stash the request so things like IP address and GEO-IP based location is available to other models
RequestStore.store[:current_request] = request
end
def self.location
# resolve the location just once per request
RequestStore.store[:current_location] ||= self.request.try(:location)
end
end
Use Thread.current, which is passed in from request to model (note, this breaks if, inside your request, you use sub-threads). You can store the attribute you want to share in a cattr_accessor or in rails cache:
in a cattr_accessor
class YourClass
cattr_accessor :my_var_hash
...
# and in your controller
# set the var
YourClass.my_var_hash = {} if YourClass.my_var_hash.nil?
YourClass.my_var_hash[Thread.current.object_id] = {}
YourClass.my_var_hash[Thread.current.object_id][your_var] = 100
... and in your model
lvalue = YourClass.my_var_hash[Thread.current.object_id][your_var]
Note, if you use this method, you will also want to make one of the hash values a timestamp, and do some housekeeping on getting, by deleting old keys, b/c you'll eventually use up all your system memory if you don't do the housekeeping
with cache:
# in your controller
#var = Rails.cache.fetch("#{Thread.current.object_id}_var_name") do
return 100 # do your work here to create the var value and return it
end
# in your model
lvalue = Rails.cache.fetch(("#{Thread.current.object_id}_var_name")
You can then set the cache expiration to 5 minutes, or you can wildcard clear your cache at the end of your request.

What's the absolute path to current request object in Rails environment?

I'm searching for the current request of class Rack::Request to find the params. Suppose I've spawned a debugger in my model, I don't want to send a new request, but still find my params.
I couldn't find any class attributes, that would store current request, which is reasonable.
I don't know how to find any instances of ApplicationController or Rack::Server, which might contain the info.
Also, peaking into the log is considered too much effort, so I'd like the effort to be concentrated on finding the request object, not telling me to grep/search through log.
In hopes of being able to be lazy,
Love Dzhon.
It's possible I'm misunderstanding your question, but from within a controller you can simply access a request object to get its details, and params to get the params.
ItemController
def show
#page_variable = request.inspect + params.inspect
end
end
If you want to make the request object available to your models you can create a class accessor and store it at the beginning of any action (via a before_filter in the application controller) for example. More details why here.

Make Rails return response from other local URL

I want a /plan method to return a json object that's itself returned by another local (but belonging to another web app in java) URL (let's call it /plan2 for the sake of this question).
What I want is not a redirect but really to have /plan return the data as it is returned by /plan2, to which I'm appending a bunch of other keys. Since the request is local, would using Net::HTTP be overkill? What are my options, considering I'd also like to send an HTTP Accept header along.
Shooting in the dark here, but I am assuming that /plan belongs to public Rails app and the /plan2 is the url from another web app (maybe not even Rails) on the same server accessible by Rails app but not publicly available. If this is the case, then yes, you can get the response, but I would suggest rather OpenURI or Mechanize than Net::HTTP. If you put it in respond_to JSON format block then everything should be fine with Accept header also.
are you speaking of re-using functionality of another controller-method?
there is no standard way of doing this in rails. you could either put the common functionality into a module and include it this way, or use inheritance if the functionality is in another controller. if it's the same controller class, you could just call the method.
if it's a "private" url, how are you going to call it via http?!
I would suggest encapsulating whatever functionality /plan2 uses and simply re-use that in /plan1
But if you just want to get it to work...
class PlanController < ApplicationController
def plan1
plan2(extra_parameter_1: true, extra_parameter_2: 'hello')
end
def plan2(extra = {})
params.merge!(extra)
# Whatever your code was before...
end
end

Getting the current request in rails from a file in lib/

I've put all of my user-authentication code in one place, namely lib/auth.rb. It looks like this:
lib/auth.rb
module Admin
def do_i_have_permission_to?(permission)
# Code to check all of this goes here
end
end
I include this module as part of the application helper, so these functions are available in all the views:
application_helper.rb
require 'auth'
module ApplicationHelper
include Admin
# other stuff here
end
And I also include it as part of the application controller, so the controllers likewise can call the functions:
application.rb
require 'auth'
class ApplicationController < ActionController::Base
include Admin
end
So far, so good.
The problem is that my application is not like a normal web app. Specifically, more than one user can be logged into the system from the same computer at the same time (using the same browser). I do authentication for actions by looking at all the people who are logged in from that IP and if they can all do it, it passes.
What this means is that, if an admin wants to do something, that admin has to log everyone else out first, which is annoying. But we want the admin seal of approval on everything the admin does. So the suggestion given to me was to have it so the admin can supply a username/password combo on any page they would not normally have access to (e.g. an 'edit user' page would have these extra input fields) and the authentication routines would check for that. This means
Admin::do_i_have_permission_to?(permission)
needs to get at the current request parameters. I can't just use params[:foo] like I would in a controller, because params isn't defined; similarly request.parameters[:foo] will also not work. My searching has revealed:
The current search parameters are in the current request,
The current request is in the current controller,
The current controller is in the current dispatcher, and
I'm not sure the current dispatcher is kept anywhere.
That said, experience tells me that when I'm jumping through this many hoops, I'm very probably Doing It Wrong. So what is the right way to do it? Options I've considered are:
Just move all the functions currently in auth.rb into the ApplicationHelper where (I think) they'll have access to the request and such. Works, but clutters the hell out of the helper.
Move all the functions somewhere else they'll see those methods (I don't know where)
I'm just plain missing something.
In a typical Rails application, authentication information is stored in the active session, not the parameters. As such, it's pretty straightforward to write a helper that does what you want.
It seems rather unorthodox to create a module that is then included in ApplicationHelper. The traditional approach is to create a separate helper which in this case would probably be called AuthenticationHelper. This can then be included in any required controllers, or if you prefer, loaded into ApplicationController to make it available universally.
In general terms, Helpers should not include other Helpers. It is better to simply load multiple helpers into a given Controller.
Helper methods have full access to any instance variables declared within the controller context they are operating from. To be specific, these are instance variables only (#name) and not local variables (name). Helper methods are executed for a particular view as well.
Further, I'm not sure why a user would be providing credentials and performing an operation in the same step, at least for traditional web-based apps. Usually the process is to log in and then perform an action separately.
However, in the case of an API where each transaction is an independent operation, the most straightforward approach is to do is pull out the relevant request parameters that deal with authentication, establish some controller instance variables, and then proceed to perform the particular request given the constraints that the credentials impose.
The approach I usually follow for this sort of thing is to layer in an authentication structure in the ApplicationController itself which can perform the required checks. These are protected methods.
While it's tempting to roll in a whole heap of them such as can_edit_user? and can_create_group? these very quickly get out of hand. It is a simpler design to put in a hook for a general-purpose can_perform? or has_authority_to? method that is passed an operation and any required parameters.
For example, a very rough implementation:
class ApplicationController < ActionController::Base
protected
def has_authority_to?(operation, conditions = { })
AuthenticationCheck.send(operation, conditions)
rescue
false
end
end
module AuthenticationCheck
def self.edit_user?(conditions)
session_user == conditions[:user]
end
end
class UserController
# ...
def edit
#user = User.find(params[:id])
unless (has_authority_to?(:edit_user, :user => #user))
render(:partial => 'common/access_denied', :status => :forbidden)
end
rescue ActiveRecord::RecordNotFound
render(:partial => 'users/not_found')
end
end
Obviously you'd want to roll a lot of the authority checks into before_filter blocks to avoid repetition and to promote consistency.
A full framework example might be of more help, such as the Wristband user authentication system:
http://github.com/theworkinggroup/wristband/tree/master

Resources