How is the `flash` in Rails made global to an application? - ruby-on-rails

I'm curious as to how Rails achieves this. I notice that you can access the flash variable globally in an app, but it isn't prefixed with an # or a $.
I can also see that there's a method for accessing the flash and there's also an initializer as well that will set #flash, but how is it that I can call flash as a local variable?

Session
Further to apneadiving's answer, the flash is part of the middleware stack (ActionDispatch::Flash). It's actually a non-persistent session cookie:
--
From the docs:
The flash is a special part of the session which is cleared with each
request. This means that values stored there will only be available in
the next request, which is useful for passing error messages etc.
Much in the same way that params works (on a per request basis), the flash variable is only populated with data from the previous request.
--
Middleware
If you take apneadiving's comment, you'll see that the flash is created through the middleware stack - meaning the local nature of the variable is only set for that particular request (much the same as params). This is why you can access / set a flash message in any controller - because it's defined higher up the "middleware stack" - it provides a scope which appears global
I'm sure someone like apneadiving can explain it better than me, but that's how I see it

Related

How to get the myshopify_domain from a ShopifyApp::Authenticated controller without an API call

I am trying to reduce unnecessary calls to the Shopify API from a controller that inherits from ShopifyApp::AuthenticatedController, for example to get the myshopify_domain:
myshopify_domain = ShopifyAPI::Shop.current.myshopify_domain
Is there some method in ShopifyApp::SessionRepository or somewhere else in the ShopifyApp that I can call to retrieve Shop.current.myshopify_domain without making an actual call to the Shopify API webservice? If not, can I store the myshopify_domain, once retrieved, in the ShopifyApp::SessionRepository?
If you are in the AuthenticatedController, dump the following to the console:
session.to_json
You will see that you can access all sorts of stuff about the current session, such as:
session["shopify_domain"]
session["shop_id"]
I had the same problem with a muli-store app, where I needed to pull data tied to a specific store. ShopifyAPI::Shop.current.myshopify_domain is redundant in that you are slowing down the controller waiting for Shopify's response, and you are tinking down the api bucket limit. The session object is the superior method to avoid all of that, and should be accessible from any controller which inherits the ShopifyApp::Authenticated controller.
Your question is confusing. At the point where you are doing calls to the API, you clearly already know the myshopify_domain, as you cannot do API calls with that the shops name and token.
So now we're past that point, and you are asking how you can somehow have the myshopify_domain be more convenient for you to use? Just make yourself a little helper so that when you open a session, you have access to shop_name or whatever you want.
Shopify always sends you shop name in their requests, so you're covered there as it's a param, and your own interface code and calls will also be setting up the shop name too, so you're really now into some pretty esoteric territory to need anything else.
Seem like you're caught in a classic "the dog chasing its own tail", but why?
The myshopify_domain is usually available in the session parameters:
if !session[:myshopify_domain].nil? && !session[:myshopify_domain].empty?
session[:myshopify_domain]
else
session[:myshopify_domain] = ShopifyAPI::Shop.current.myshopify_domain
end

Updating and accessing a session with Ruby on Rails

I'm working with ruby on rails 2.5.
I have an object "payment_plan". This object can change with a toggle behavior that changes, and I need to keep this object alive thorough all the session and at the end it should be save par of it in my mongo db. I need to access the latest status of the object always. The controller should be capable to update the object and the view should be able to access the latest state of the object.
Any insights on how to do something like this would be great :)
I have try to create a helper function in the application controller but had problem accessing it from the view.
Also I prefer not to save the state of the object in the db, because it will be too many db calls later.
To access a controller helper function from view, define it as a helper:
class SomeController < ApplicationController
helper def some_helper
end
end
As for storing some data in session - it's ok, rails has nice session store mechanism for session[:my_object_prop] = 1/session[:my_object_prop] (see in official guide)
But keep in mind, that:
by default session is stored in cookies, which are passed in headers from client browser with every request to your server (even to images, if they are on the same domain), so it's only practical to save small amounts of data there.
user can clear their's cookies and data will be lost (this is usually fine)
the opposite of the latter - a user may come to your app with session data from old version of your code
thank you very much, that was very helpfull!
Another question, can I override a session value after setting it up?
for example session[:plan_id]="plan_id_1" and they further on in the run do something like this: session[:plan_id]="plan_id_2"
thanks!!!

Web2py disable session cookies per controller

Is there a way to not save session ids & the session cookie in web2py on a per-controller level? global_settings.web2py_disable_session = True will do it for the whole site, but I want some pages to retain sessions.
If you don't need to use the parameter-based rewrite system, you can disable sessions based on routing using the pattern-based rewrite system. In the routes.py file, you would do something like this:
routes_in = [
('/myapp/default/$anything', '/myapp/default/$anything',
dict(web2py_disable_session=True))
]
The optional third element of a routes_in tuple is a dictionary, which will be used to update request.env. The above will add web2py_disable_session=True to request.env only for routes starting with /myapp/default/ (setting global_settings.web2py_disable_session=True, on the other hand, will add web2py_disable_session=True to request.env for all requests).
Alternatively, you can simply call session.forget(response) in any controller or action that does not need the session (or conditionally in a model file depending on the requested path). If no session cookie or file have yet been created, this will prevent their creation. Although simpler, this method is slightly less efficient than the above, as it will still result in the session initialization code running on every request.
One final alternative would be to create a custom WSGI application function in the WSGI handler file that conditionally adds web2py_disable_session=True to the WSGI environment dictionary depending on the requested route. Then pass the modified environment dictionary to gluon.main.wsgibase.

Is it safe to accept URL parameters for populating the `url_for` method?

I am using Ruby on Rails 4.1.1 and I am thinking to accept parameters (through URL query strings) that are passed directly to the url_for method, this way:
# URL in the browser
http://www.myapp.com?redirect_to[controller]=users&redirect_to[action]=show&redirect_to[id]=1
# Controller
...
redirect_to url_for(params[:redirect_to].merge(:only_path => true))
Adopting the above approach users can be redirected after performing an action. However, I think people can enter arbitraryparams that can lead to security issues...
Is it safe to accept URL parameters for populating the url_for method? What are pitfalls? What can happen in the worst case?
By logging params during requests to my application I noted Rails adds always :controller and action parameters. Maybe that confirms url_for can be used the above way since it is protected internally and works as-like Rails is intended to.
This it is safe internally as Ruby On Rails will only be issuing a HTTP redirect response.
As you are using only_path this will protect you from an Open redirect vulnerability. This is where an email is sent by an attacker containing a link in the following format (say your site is example.com).
https://example.com?foo=bar&bar=foo&redirect=http://evil.com
As the user checks the URL and sees it is on the example.com domain they beleive it is safe so click the link. However, if there's an open redirect then the user ends up on evil.com which could ask for their example.com password without the user noticing.
Redirecting to a relative path only on your site fixes any vulnerability.
In your case you are giving users control of your controller, action and parameters. As long as your GET methods are safe (i.e. no side-effects), an attacker could not use this by creating a crafted link that the user opens.
In summary, from the information provided I don't see any risk from phishing URLs to your application.
Rails redirect_to sets the HTTP status code to 302 Found which tells the browser to GET the new path as you defined it by url_for. GET is a considered a safe method in contrast to
... methods such as POST, PUT, DELETE and PATCH [which] are intended for
actions that may cause side effects either on the server, or external
side effects ...
The only problem would have been if someone could gain access to methods such as create and destroy. Since these methods use HTTP methods other than GET (respectively POST and DELETE) it should be no problem.
Another danger here is if you go beyond CRUD methods of REST and have a custom method which responses to GET and changes the database state:
routes.rb
resources something do
member do
get :my_action
end
end
SomethingController
def my_action
# delte some records
end
For future ref:
Rails has a number of security measurements which may also interest you.
It's not exactly an answer, just wanted to point out that you shouldn't use something like
url_for(params)
because one could pass host and port as params and thus the url could lead to another site and it can get worse if it gets cached or something.
Don't know if it threatens anything, but hey, it's worth pointing out

Prevent Ruby on Rails from sending the session header

How do I prevent Rails from always sending the session header (Set-Cookie). This is a security problem if the application also sends the Cache-Control: public header.
My application touches (but does not modify) the session hash in some/most actions. These pages display no private content so I want them to be cacheable - but Rails always sends the cookie header, no matter if the sent session hash is different from the previous or not.
What I want to achieve is to only send the hash if it is different from the one received from the client. How can you do that? And probably that fix should also go into official Rails release? What do you think?
Rails only adds the session cookie data to the Set-Cookie header if it has been touched. You might be setting things to the values that they already contain - it's not smart enough to check to see if the data is actually different.
edit My response is a little misleading. When you are using the cookie session store, a new cookie is set if the cookie value (after Marshaling) changes.
See actionpack/lib/action_controller/session/cookie_store.rb
For Rails 3 then use this.
env['rack.session.options'][:skip] = true
or the equivalent
request.session_options[:skip] = true
You can find the documentation for it here http://doc.rubyists.com/rack/Rack/Session/Abstract/ID.html
Here is the crucial line:
config.action_controller.session_store = :nil_session_store
See the whole post.

Resources