Unable to set/use session variables in ruby on rails controller - ruby-on-rails

Can somebody tell me the right way to set/use session variables in a ruby on rails application from scratch. I am not able to set/use a session variable in my controller between two pages. I am using CookieStore session type. This is the syntax being used to set and get session variables:
session[:test] = "testing"
#test_str = session[:test]
Let me know in case I am missing on something.
This is how my controller looks like:
class PaymentsController < ApplicationController
# GET /merchant_test
def merchant_test
session[:test] = "testing"
render :layout => false
end
# POST /post_to_mobikwik
def post_to_mobikwik
zr = Mobikwik::Request.new(params)
#mobikwik_data = zr.all_params
#test_str = session[:test]
render :layout => false
end
# POST /z_response
def z_response
zr = Mobikwik::Response.new(request.raw_post)
#checksum_check = zr.valid?
#mobikwik_post = zr.all_params
#statuscode = #mobikwik_post['statuscode']
#statusmessage = #mobikwik_post['statusmessage']
if #statuscode == "0"
#verified = zr.verified?
else
#verified = false
end
render :layout => false
end

Your session cookie settings might be wrong. You should inspect the headers of the response and see what the Set-Cookie header looks like.
It might have a wrong domain or perhaps your cookie is https only and you're on http.
The configuration is usually done in some place like config/initializers/session_store.rb
Myapp::Application.config.session_store :cookie_store, {
:key => '_appname_session_id',
:path => '/',
:domain => nil, # accepted domain, for example '.example.com'
:expire_after => nil, # the session will be expired in X seconds unless active
:secure => false, # if true, cookie is valid only on https
:httponly => true # if true, javascript can't access the cookie
}

Try this in config/initializers/session_store.rb
AppName::Application.config.session_store :cookie_store, key: '_app-name_session'

As a rule of thumb, in rails we create a method
in ApplicationController, called current_user
In this free RailsCast, you see exactly are you need:

Related

How can I prefix cookies with __Host or __Secure?

I am trying to add prefix to session cookies in rails 6.0.3 app but couldn't find a way to get it done. I have tried adding key to options hash in session store but it didn't help and breaks my application. I am using auth-logic gem for authentication, I find no way to get it done gracefully but hopping on that there is some way.
conf/initalizers/session_store.rb
opts = {}
if Rails.configuration.host == "myapplication.com"
opts = {expire_after: 2.months, domain: :all}
end
unless Rails.env.test?
opts[:secure] = true
opts[:same_site] = :none
end
opts[:key] = '__Host-'
Rails.application.config.session_store :active_record_store, **opts
Attached is the screenshot of github cookies. I want my session headers as like in the image (prefixed with __Host-).
As per your link...
Cookies with the __Host- prefix must have a path of /
(meaning any path at the host) and must not have a Domain attribute.
So I would presume you need to remove the domain attribute and add the path. e.g.
opts = {}
if Rails.configuration.host == "myapplication.com"
opts = {expire_after: 2.months}
end
unless Rails.env.test?
opts[:secure] = true
opts[:same_site] = :none
opts[:path] = '/'
end
opts[:key] = '__Host-'
Rails.application.config.session_store :active_record_store, **opts

Ruby on Rails, Zendesk API integration not loading the client

I am trying to set up the Zendesk API in my app, I have decided to go with the API that was built by Zendesk
I have set up the initializer object to load the client.
config/initializers/zendesk.rb
require 'zendesk_api'
client = ZendeskAPI::Client.new do |config|
# Mandatory:
config.url = Rails.application.secrets[:zendesk][:url]
# Basic / Token Authentication
config.username = Rails.application.secrets[:zendesk][:username]
config.token = Rails.application.secrets[:zendesk][:token]
# Optional:
# Retry uses middleware to notify the user
# when hitting the rate limit, sleep automatically,
# then retry the request.
config.retry = true
# Logger prints to STDERR by default, to e.g. print to stdout:
require 'logger'
config.logger = Logger.new(STDOUT)
# Changes Faraday adapter
# config.adapter = :patron
# Merged with the default client options hash
# config.client_options = { :ssl => false }
# When getting the error 'hostname does not match the server certificate'
# use the API at https://yoursubdomain.zendesk.com/api/v2
end
This is pretty much copy paste from the site, but I have decided on using the token + username combination.
I then created a service object that I pass a JSON object and have it construct tickets. This service object is called from a controller.
app/services/zendesk_notifier.rb
class ZendeskNotifier
attr_reader :data
def initialize(data)
#data = data
end
def create_ticket
options = {:comment => { :value => data[:reasons] }, :priority => "urgent" }
if for_operations?
options[:subject] = "Ops to get additional info for CC"
options[:requester] = { :email => 'originations#testing1.com' }
elsif school_in_usa_or_canada?
options[:subject] = "SRM to communicate with student"
options[:requester] = { :email => 'srm#testing2.com' }
else
options[:subject] = "SRM to communicate with student"
options[:requester] = { :email => 'srm_row#testing3.com' }
end
ZendeskAPI::Ticket.create!(client, options)
end
private
def for_operations?
data[:delegate] == 1
end
def school_in_usa_or_canada?
data[:campus_country] == "US" || "CA"
end
end
But now I am getting
NameError - undefined local variable or method `client' for #<ZendeskNotifier:0x007fdc7e5882b8>:
app/services/zendesk_notifier.rb:20:in `create_ticket'
app/controllers/review_queue_applications_controller.rb:46:in `post_review'
I thought that the client was the same one defined in my config initializer. Somehow I think this is a different object now. I have tried looking at their documentation for more information but I am lost as to what this is?
If you want to use the client that is defined in the initializer you would need to make it global by changing it to $client. Currently you have it setup as a local variable.
I used a slightly different way of initializing the client, copying from this example rails app using the standard Zendesk API gem:
https://github.com/devarispbrown/zendesk_help_rails/blob/master/app/controllers/application_controller.rb
As danielrsmith noted, the client variable is out of scope. You could instead have an initializer like this:
config/initializers/zendesk_client.rb:
class ZendeskClient < ZendeskAPI::Client
def self.instance
#instance ||= new do |config|
config.url = Rails.application.secrets[:zendesk][:url]
config.username = Rails.application.secrets[:zendesk][:username]
config.token = Rails.application.secrets[:zendesk][:token]
config.retry = true
config.logger = Logger.new(STDOUT)
end
end
end
Then return the client elsewhere by client = ZendeskClient.instance (abridged for brevity):
app/services/zendesk_notifier.rb:
class ZendeskNotifier
attr_reader :data
def initialize(data)
#data = data
#client = ZendeskClient.instance
end
def create_ticket
options = {:comment => { :value => data[:reasons] }, :priority => "urgent" }
...
ZendeskAPI::Ticket.create!(#client, options)
end
...
end
Hope this helps.

How do you test HttpAuthentication::Digest in rails 4?

I'm upgrading from rails 3 to rails 4 and trying to get digest authentication working based on this example:
http://lightyearsoftware.com/2009/04/testing-http-digest-authentication-in-rails/
It looks like the 'process_with_test' method was removed, so I think I can just override the controller's process method like this:
def authenticate_with_http_digest(user = API_USERNAME, password = API_PASSWORD, realm = API_REALM)
ActionController::Base.class_eval { include ActionController::Testing }
#controller.instance_eval %Q(
alias real_process process
def process(name)
credentials = {
:uri => request.url,
:realm => "#{realm}",
:username => "#{user}",
:nonce => ActionController::HttpAuthentication::Digest.nonce(Rails.configuration.secret_key_base),
:opaque => ActionController::HttpAuthentication::Digest.opaque(Rails.configuration.secret_key_base)
}
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Digest.encode_credentials(request.request_method, credentials, "#{password}", false)
real_process(name)
end
)
end
I can see the new method gets called, but I still get 401 access denied errors when I call the controller. I'm not sure I am creating the digest authentication correctly, but I don't know which part is incorrect. Does anyone have tips for debugging this?
I had the same issue. I read through the Rails 4 test cases and built the below solution. Its not perfect by any stretch of the imagination but it works in my test environment. It is a drop-in solution for the original authenticate_with_http_digest helper method.
Gist here:
https://gist.github.com/illoyd/9429839
And for posterity:
# This should go into spec/support/auth_spec_helpers.rb (if you are using RSpec)
module AuthSpecHelpers
##
# Convenience method for setting the Digest Authentication details.
# To use, pass the username and password.
# The method and target are used for the initial request to get the digest auth headers. These will be translated into 'get :index' for example.
# The final 'header' parameter sets the request's authentication headers.
def authenticate_with_http_digest(user, password, method = :get, target = :index, header = 'HTTP_AUTHORIZATION')
#request.env[header] = encode_credentials(username: user, password: password, method: method, target: target)
end
##
# Shamelessly stolen from the Rails 4 test framework.
# See https://github.com/rails/rails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/actionpack/test/controller/http_digest_authentication_test.rb
def encode_credentials(options)
options.reverse_merge!(:nc => "00000001", :cnonce => "0a4f113b", :password_is_ha1 => false)
password = options.delete(:password)
# Perform unauthenticated request to retrieve digest parameters to use on subsequent request
method = options.delete(:method) || 'GET'
target = options.delete(:target) || :index
case method.to_s.upcase
when 'GET'
get target
when 'POST'
post target
end
assert_response :unauthorized
credentials = decode_credentials(#response.headers['WWW-Authenticate'])
credentials.merge!(options)
path_info = #request.env['PATH_INFO'].to_s
uri = options[:uri] || path_info
credentials.merge!(:uri => uri)
#request.env["ORIGINAL_FULLPATH"] = path_info
ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
end
##
# Also shamelessly stolen from the Rails 4 test framework.
# See https://github.com/rails/rails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/actionpack/test/controller/http_digest_authentication_test.rb
def decode_credentials(header)
ActionController::HttpAuthentication::Digest.decode_credentials(header)
end
end
# Don't forget to add to rspec's config (spec/spec_helper.rb)
RSpec.configure do |config|
# Include auth digest helper
config.include AuthSpecHelpers, :type => :controller
end
Happy testing.

session cookie httponly false rails 3.1

I'm trying to turn httponly off for use in phonegap. I'm useing rails 3.1 and devise, each of which have reported (but not documented) ways of doing this, none of which work:
# application.rb
config.session_options = { :httponly => false } # no effect
config.session = { :httponly => false } # undefined method `session='
# devise.rb
config.cookie_options = { :httponly => false } # also no effect
to test I restarted the server, deleted the existing cookie, and reloaded the page. 'Http' column was still checked in the chrome debugger.
help!
This little snippet seems to work :
Testapp::Application.config.session_store :cookie_store, key: '_testapp_session', :domain => :all, :httponly => false
As far as I can tell, this is a bug in rails. Perhaps the option got removed, but the documentation stayed. Any ideas on this would be welcome!
I spent several thorough hours with ActionPack, and couln't find any reference to such a configuration option-- but I still don't have the full picture as to how it works. Specifically, there's the cookiestore which holdes cookies and writes them to the header (and is passed :httponly => true), but I couldn't find how the session is using the store-- with vague things like the Rails SessionManage module being a proverbial ghost town.
I hacked up a middleware which does the job:
# application.rb:
config.middleware.insert_before ActionDispatch::Cookies, "UnshieldCookie" # remove httponly.
# unshielded_cookie.rb
class UnshieldCookie
def initialize(app)
#app = app
end
def call(env)
status, headers, body = #app.call(env)
headers['Set-Cookie'].gsub!('HttpOnly', '') if headers['Set-Cookie'].present?
[status, headers, body]
end
end

How can I make cookies secure (https-only) by default in rails?

In a Rails controller, I can set a cookie like this:
cookies[:foo] = "bar"
And specify that the "secure" (https-only) flag be on like this:
cookies[:foo, :secure => true] = "bar"
:secure is false by default. How can I have cookies be secure by default, application-wide?
This is on Rails 2.3.8
There's no need to monkeypatch ActionController/ActionDispatch, and force_ssl has side effects (e.g. when behind an ELB).
The most straightforward way to achieve secure cookies is to modify config/initializers/session_store.rb:
MyApp::Application.config.session_store(
:cookie_store,
key: '_my_app_session',
secure: Rails.env.production?
)
starting with rails 3.1, according to the rails security guide, you can simply set the following in your application.rb:
config.force_ssl = true
this forces the cookie to be sent over https only (and I assume everything else, too).
Thanks #knx, you sent me down the right path. Here's the monkeypatch I came up with, which seems to be working:
class ActionController::Response
def set_cookie_with_security(key, value)
value = { :value => value } if Hash != value.class
value[:secure] = true
set_cookie_without_security(key, value)
end
alias_method_chain :set_cookie, :security
end
What do you think?
Quick and dirty solution: i think it is possible by modifying []= method in action pack cookies module (actionpack/lib/action_controller/cookies.rb)
from:
def []=(name, options)
if options.is_a?(Hash)
options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
options["name"] = name.to_s
else
options = { "name" => name.to_s, "value" => options }
end
set_cookie(options)
end
to:
def []=(name, options)
if options.is_a?(Hash)
options.merge!({:secure => true})
options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
options["name"] = name.to_s
else
options = { "name" => name.to_s, "value" => options }
end
set_cookie(options)
end
# session only available over HTTPS
ActionController::Base.session_options[:secure] = true
You should look at the rack-ssl-enforcer gem. I was just looking for a clean answer to this and it solves the problem independent of which version of Rails you're on, plus it's extremely configurable.
You can do this as mentioned in some of the above answers (use secure option in the config/initializers/session_store.rb file):
MyApp::Application.config.session_store :cookie_store, key: '_my_app_session',
secure: Rails.env.production?
which will only secure the session cookie, but other cookies will not be secure.
If you want to secure all the cookies in your Rails app by default, you can use the secure_headers gem. Just add the secure_headers gem to your Gemfile, bundle install the gem and create a config/initializers/secure_headers.rb file with this content:
SecureHeaders::Configuration.default do |config|
config.cookies = {
secure: true, # mark all cookies as "Secure"
}
end
This will make all the cookies secure in your Rails app by default.
You can also add these recommended configurations and set the httponly and samesite options as well:
SecureHeaders::Configuration.default do |config|
config.cookies = {
secure: true, # mark all cookies as "Secure"
httponly: true, # mark all cookies as "HttpOnly"
samesite: {
lax: true # mark all cookies as SameSite=lax
}
}
end

Resources