Where does 'session' come from? - ruby-on-rails

I'm building a sessions controller in my rails app, and I'm just not sure why something is working here. In the create and destroy actions, session[index] is assigned to either nil or a user id. But this sessions hash isn't defined anywhere (as far as I can see). Why is this working? Can anyone clarify this for me?
(for the sake of clarity, there is no sessions model)
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to products_url, :note => "Logged in!"
else
render "new"
end
def destroy
session[:user_id] = nil
redirect_to products_url, :notice => "Logged out!"
end
end

The session instance method functions like a Hash and is part of the Rails API.
Rails does all the work of setting up an encrypted, tamperproof session datastore. By default, session data is saved as a cookie in the browser. You can specify other storage mechanisms but CookieStore is the default and the most convenient.
The CookieStore default is set in the config/initializers/session_store.rb file:
Rails.application.config.session_store :cookie_store, key: '_learn-rails_session'
You can learn more about sessions in Rails:
RailsGuides Action Controller Overview
Rails API ActionDispatch::Session::CookieStore
For more information, I've written a Rails Devise Tutorial that shows how sessions are managed with the Devise authentication gem.

By default sessions are stored in a cookie in the client-side (i.e. the user's browser's cookie). It is not stored on the server-side (i.e. where the Rails app is actually running.)
When you use the session hash, Rails is smart enough to look/ask for the session information accordingly. In the default case, Rails knows to set the session information in the browser's cookie, or retrieve the information from the browser's cookie.
You can also pick where to put your session store by setting the config.session_store configuration variable.
See the Rails guide for more info.

Related

Create simple site password for Rails - no username

I would like to protect a site with a very simple password only validation when user first visits site. I currently use http authentication, but that requires a username & password. I can hardcode password in back end. Basics of site: local sports league where we keep our stats and info about league. Simply trying to keep "riff-raff" out :)
I am a ruby on rails newbie, and am using this site as a way to learn. Any help out there would be appreciated!
You could do something cookie-based.
In your ApplicationController, you'd implement a method for determining if the cookie is present that states that the visitor has entered your password – if the cookie isn't present, then you'll redirect to your password page:
class ApplicationController < ActionController::Base
def require_password_verification
unless cookies[:visitor_password_verified]
return redirect_to <whatever your passwords#new path is>
end
end
end
The controller for your password page would look something like this:
class PasswordController < ApplicationController
def new
# Nothing needed here because all your #new view needs is a password field
end
def create
unless params[:password].present?
return redirect_back(fallback_location: root_path, alert: 'Password is required.')
end
if params[:password] == Rails.configuration.visitor_password
cookies[:visitor_password_verified] = true
redirect_to(root_path, notice: 'Password verified.')
else
cookies.delete(:visitor_password_verified)
redirect_back(fallback_location: root_path, alert: 'You've entered the wrong password.')
end
end
end
Your password would be stored in the application.rb file, like so:
config.visitor_password = '12345'
Normally, you would never store a password in this way because it's not secure at all but considering your use case, it's probably fine, since having a single password for everybody is already not secure. 😃 However, if you did want to step up the security a notch, I would recommend storing your password in an environment variable, and then you could set the password like so:
config.visitor_password = ENV['VISITOR_PASSWORD']
That way, at least your password isn't hard-coded and accessible to anybody who looks at your, assumedly public, repo.
And then you can require the "password has been entered" cookie for whatever views you want like so:
class LeagueStatsController < ApplicationController
before_action :require_password_verification
def index
# Whatever
end
end
If somebody hits your league_stats#index page, then it's going to check to make sure the visitor_password_verified cookie is present and true first. If it is, then they'll get through to the view. If it's not, they'll be redirected to your passwords#new page.

Rails session doesn't expire at browser close [duplicate]

This question already has answers here:
Session not destroyed when closing browser - RailsTutorial.org
(2 answers)
Closed 5 years ago.
It's my understanding that the default behavior of Rails, when storing a session e.g. session[:id] = 1, is that it will save the id to a cookie, which will expire when the user closes the browser window. However, in my app, when I close (exit out) the browser and restart it, the browser still 'remembers' me as being logged in. Here is my controller code:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
log_in user
redirect_to user
else
flash.now[:notice] = "Invald email / password combination"
render 'new'
end
end
def destroy
end
end
and my helper file:
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
def logged_in?
!current_user.nil?
end
def user_name
#current_user.first_name ? (#current_user.first_name + " ") : nil
end
end
I have nothing in the application controller nor did I ever mess with the initializers or config files regarding the session. What could be causing my session to persist and not expire the cookie?
Withing config/initializers/session_store.rb file add
Rails.application.config.session_store :cookie_store, key: '_app_session', expire_after: nil
Instead of
Rails.application.config.session_store :cookie_store, key: '_app_session'
Please have a look at here for more info.
The solution is to simply turn off cookies or reset cookies, which results in a new session being created with each visit. The solution looked like this:
On client side, write a beforeunload event handler.
From that handler make an AJAX request to logout URL, in your case it
might be a special view that deletes the cookie (see Rails docs on
cookies).
The AJAX request has to be synchronous to make sure beforeunload
event handler waits for its completion.
refer this https://codedecoder.wordpress.com/2013/12/04/clear-session-or-cookie-browser-close-ruby-jquery/ it might also help

Adding sessions and session_store back into a Rails 5 API (sessions not persisting)

I am attempting to set up a basic sessions-based authentication strategy in a Rails 5 API.
I think I'm mostly running into a configuration confusion, because a new session is generated on each request.
I've added the cookies and cookie store middleware back into the application
config/application.rb
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore
config.api_only = false
end
It seems like I have to set api_only to false or I get #<ActionDispatch::Request::Session:0x7fd40b2eec80 not yet loaded>
I added the session_store initializer and the cookie_serializer:
config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store, key: '_tunr_sol_json_api_session'
config/initializers/cookie_serializer.rb
Rails.application.config.action_dispatch.cookies_serializer = :json
I'm not storing sessions in the database.
I have a sessions controller that sets a current_user_id key to the sessions object when the user is successfully authenticated.
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.find_by(username: params[:user][:username])
if user && user.authenticate(params[:user][:password])
session[:current_user_id] = user.id
render json: {status: 201, message: "session created", user: user}
else
render json: {status: 401, message: "unauthorized"}
end
end
def destroy
session[:current_user_id] = nil
render json: {status: 204, message: "no content"}
end
end
The behavior
The auth strategy I have set up with bcrypt and has_secure_password works. And in looking at the session in sessions#create it successfully sets the user id to the session.
But the session doesn't seem to persist or get stored in the browser. sessionStorage after login has a length of 0
When I log out and check the session, it creates a new session.
Question
Not sure what configuration I'm missing or have wrong to get this functionality set up with the api.
You also need to change your Application controller to:
class ApplicationController < ActionController::Base

rails 4 - ldap authentication and user objects without database

I have an app where users log in and the LDAP server is used to authenticate them. That part I have down, but the hiccup comes when dealing with the users and multiple sessions.
When a user is authenticated I create a new user object from a user model that inherits from ActiveModel, saves their name and email from the LDAP entry. This is done in sessions_controller with user as an instance variable. i.e. #user = User.new
However when current_user in application_controller checks for the user object, it's value is nil.
I'm guessing this is a scoping issue where the application controller can't see the instance variable in session controller's value.
Without using a database, how could I handle saving this minimal info about a user and have it persist through out their session? Before when using a class variable ##user and setting it in application_controller other users were only able to log in one at a time...Thanks for any help in advance!
Assuming your User object is small, you can store it in the session.
class SessionController < ApplicationController
def authenticate
# Handle LDAP-auth and we now have a user
# ldap_authenticated? should be true
# ldap_user contains relevant info from LDAP
session[:user] = User.new(email: ldap_user.email) if ldap_authenticated?
end
end
module ApplicationHelper
# Use this to access the current user everywhere in the app
def current_user
session[:user]
end
end

How force that session is loaded?

I'm working on an application that needs to use session id information. My session is stored in cookies. The problem I have is that my session is not immediately available to the controller when a user comes to the site for the first time. I think I may be missing something about how sessions are initialized in Rails. But I'm positve about the fact that the session is not loaded because this is the output of session.inspect:
#<Rack::Session::Abstract::SessionHash:0x15cb970 not yet loaded>
Here is how to reproduce the problem with Rails 3.2.11 and ruby 1.9.3:
Create a new application with a test controller:
rails new my_app
cd my_app/
rails g controller test
rm app/assets/javascripts/test.js.coffee
touch app/views/test/index.html.erb
Try to get the session id in that controller:
class TestController < ApplicationController
def index
puts session[:session_id]
puts session.inspect
end
end
Add the needed routes:
MyApp::Application.routes.draw do
resources :test
end
Then access the application and see what it does:
rails server
got to: http://localhost:3000/test
That is the output in the console:
#<Rack::Session::Abstract::SessionHash:0x3fd10f50eea0 not yet loaded>
Then again http://localhost:3000/test and this time we have a session:
400706c0b3d95a5a1e56521e455075ac
{"session_id"=>"400706c0b3d95a5a1e56521e455075ac", "_csrf_token"=>"Euaign8Ptpj/o/8/ucBFMgxGtiH7goKxkxeGctumyGQ="}
I found a way to force initialization of the session. Accessing the session apparently does not force initialization but writing into the session does. What I do in my controller is this now:
class MyController < ApplicationController
protect_from_forgery
def index
session["init"] = true
do_stuff
end
end
Still I'm not sure if this should be considered normal behavior in Rails. It doesn't look right to me having to write into the session to force initialization. Reading should be enough.
I agree with #joscas answer but instead of writing a value, I'd delete it as to not have redundant data.
class MyController < ApplicationController
protect_from_forgery
def index
session.delete 'init'
do_stuff
end
end
The session is loaded this way too.
Note: Make sure you don't use the key to be deleted in your application.
Here's some relevant code from ActionDispatch::Session:
def [](key)
load_for_read!
#delegate[key.to_s]
end
private
def load_for_read!
load! if !loaded? && exists?
end
Which implies that the session object will be loaded as soon as you access any value by its key via [].
I don't really understand your question. If you require a user to register or sign in before being able to access the site there should be no problem. When creating a user his information is immediately stored in a cookie. For example:
User controller: (registering is done through users#new)
def create
#user = User.new(params[:user])
if #user.save
cookies.permanent[:remember_token] = user.remember_token
redirect_to root_path, notice: "Thank you for registering!"
else
render :new
end
end
Sessions controller: (signing in is done through sessions#new)
def create
user = User.find_by_email(params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
cookies.permanent[:remember_token] = user.remember_token
redirect_to root_path, notice: "Logged in."
else
flash.now.alert = "Email or password is incorrect."
render :new
end
end
So the problem probably boils down to the fact that the cookie that stores session_id wasn't created yet at the point where you tried to access the session.
When you read session object, Rails calls a private method session.load_for_read!, but, as its name suggests, it only loads session for reading and doesn't instantiates the session if it simply doesn't exist.
On the other hand, calling session.merge!({}) (for example) forces session instantiation.
Yo! #zetetic's answer above made me realize that we can just do
session.send(:load!)
and it'll load the session 🙂.

Resources