Sharing the variables between the actions in the controller in Rails - ruby-on-rails

I am working on a module where I have to take the consent of the user to save the set of records.
those set of records are created in an action, which has to be made available in another action of the same controller, the records are being saved by the user consent.
now I can send these set of records to UI, from UI to again controller, if the user continues to save, if not cancel.
Problem is there will be thousands of records, which is painful to carry between UI and controller so My plan is to make the set of records available to the action which is being called by the continue button
the code
def create
#valid_members = generate_member_upload_results(params[:member_upload_user][:members_list])
end
in this action #valid_members is going to have the set of records. after this action executes in UI we will ask user whether the records are to be saved if no then cancels if yes then the following action will takes palce
def create_member
count = 0
unless #valid_members.blank?
#valid_members.each do |m|
count = count + 1
m.save(:validate => false)
end
end
redirect_to :back , notice:'#{count} members records created'
end
I want my #valid member should the same object which I used in create def.

I'm not entirely sure this is feasible with the flow you're suggesting. This sounds like something that could be resolved with a multi-step form but you would need to pass the data across or temporarily store it, which is seemingly what you're trying to avoid.
Alternatively, can you create a Rails endpoint that services the first step via javascript directly from the frontend? That can return the data without the user leaving the page, they can then confirm they are happy and submit the page once with approval.

Related

Some questions about security in Rails 5

I've got a number of security concerns about my current application and wondering if I am leaving myself open to abuse, in the following arenas.
a) .My main access control method is by maining a current_user, current_company current_project method in my application controller. These methods return object based on stored session keys established when a user logs in and cleared when they log out. I.e if I want to know something about the current user, I can call "current_user.role" or if I want see whether the account a user is trying to change belongs to him, I check whether the associated account id which is requested in the url actually belongs to that user, essentially as follows
in Account controller
def account_info
redirect_to login_path if !user.logged_in
account_id=params[:account_id]
#account = Account.find(account_id)
unless account_belongs_to_user(account_id)
redirect_to unauthorized_path
end
end
In my application controller, when a user is initially authenticated, I do something like this:
session[:current_user_id] = user.id
and clear that session key when the user logs out.
Then when account is requested, and account_belongs_to_user is called, the application controller processes it, more or less like this:
def account_belongs_to_user(account_id)
account = Account.find(account_id)
return account.user_id==session[:current_user_id]
end
So I guess my security scheme ultimately relies on whether the session data is secure and not trivially spoofable.
b) When I render pages I sometimes pass objects which have senstive data to my erb pages to generate the page text.
For example, I might pass a "company" object (ActiveRecord) to the view to generate an invoice screen. But the company object, passed as #company, has a lot of sensitive data like access keys and the like. Not really being fully aware of the the internals, if I don't specifically include something like:
<%= #company.access_token %>
on my web page, can I be confident that the attributes of #company won't somehow be passed into the browser unless I specifically ask for them to be rendered on the page?
This is obviously an issue when using rails to serve data for say, AngularJS single page applications, as everything I pass for Angular to render the page I assume is probably accessible to an evil-doer even if not on the page itself, but I'm hoping that's not the case with pages generated server side by rails.
This may be a naive question, but thanks as I just want to be certain what I am doing before start spilling secrets all over the place.
put an authentication for the token using active_record callback
https://guides.rubyonrails.org/active_record_callbacks.html

Recording activities of every controller in rails

I have created a log model and a method in every controller to keep record of action performed in every controller. that method populate logs modle. But i don't know how keep record of user creation, deletion and update using this function.
The method that i have created is:-
def keep_record(msg)
#log = Log.new
#log.user_id = current_user.id
#log.description = msg
#log.save
end
How can i use this method to keep record of creating, editing and removing user in devise gem.
Can anyone suggest me how to modify Registration_controller to keep record of creating, deleting and updating user.
I'm not sure what you mean by "track the activities", but it sounds like you want to track when a user changes those attributes. I would suggest looking into ActionCable, which the user will make a connection with, and basically subscribe to a channel, and you can record what they are doing.
Here is a good place to start:
Actioncable connected users list

Rails controller actions & cache - handling simultaneous/overlapping requests

My Rails app depends on the Rails cache to temporarily hold user input and pass it between controller actions during the user log in process. However, I realized that I have made a pretty serious error (since I am super-new to Rails and MVC, in general) and could use some help/advice/wisdom - basically, if two users are simultaneously (or nearly simultaneously) submitting data and going through the controller steps below, there's going to be some trouble - lost data, one user's data being entered as another, etc. When I was building this, I did not understand the nature of controllers, though I for sure have a better idea now....
In short, here's how the code below is intended to work: a non-logged-in user fills out a form and submits that data which is passed to the submission action and held in a Rails cache object called incoming_report; the user is then prompted to log in (via Devise), and a successful log in triggers the approval controller action which reads the incoming_report cache object, saves that data to the database, and then creates a new cache object called ids to hang on to the id #'s of the user's entries; this redirects to the summary action which gathers up those ids and shows the corresponding data to the user as a summary of their entry.
report_controller.rb
#ON SUBMITTING THE FORM...
def submission
#incomingReport = ActiveSupport::JSON.decode(params[:report])
#SUBMITTED DATA IS HELD IN THE CACHE AS 'incoming_report' TO PASS TO approval CONTROLLER ACTION ON SUCCESSFUL LOGIN
Rails.cache.write("incoming_report",#incomingReport)
end
#ON SUCCESSFUL LOG IN...
def approval
#incomingReport = Rails.cache.read("incoming_report")
#newReportIDArray = Array.new
#incomingReport.each do |x|
#DATA PROCESSING STUFF HERE...
end
#DELETE 'incoming_report' FROM CACHE
Rails.cache.delete("incoming_report")
#ID NUMBERS OF ENTRIES ARE HELD TO PASS TO summary CONTROLLER ACTION
Rails.cache.write("ids",#newReportIDArray)
redirect_to report_summary_path
end
#AFTER DATA IS ENTERED...
def summary
#newReportIDs = Rails.cache.read("ids")
#DELETE 'ids' FROM CACHE
Rails.cache.delete("ids")
end
For the most part, this works very well, but if users cross each other during any of these actions, it's disastrous. For instance, if one user hits the submission action while another user is busy logging in, the incoming_report object of the second user will overwrite that of the first user... and so on and so forth.
The main source of this problem is the fact that users log in after they have clicked the form submit button - the app must be structured that way - and the data can't be held in local/session storage objects because it includes base64 strings and is too long to employ those methods in certain browsers. Holding this data on the server seemed like the best course of action until I noticed this issue...
Is there any way that I can prevent users from getting in each other's way in the controllers? Or have I made a pretty fatal error?
Editing to show solution
Ultimately, using session variables instead of the Rails cache saved the day. Here's the reconfigured controller to show the solution...
def submission
#incomingReport = ActiveSupport::JSON.decode(params[:report])
#newReportIDArray = Array.new
#incomingReport.each do |x|
#DATA PROCESSING STUFF HERE
#newReportIDArray.push(#new_report.id)
end
session[:ids] = #newReportIDArray
respond_to do |format|
format.json do
render json: {
success: 200
}.to_json
end
end
end
def approval
#reportIDs = session[:ids]
#reportIDs.each do |x|
#new_report = Report.find(x)
#new_report.user_id = current_user.id
#new_report.save
end
redirect_to report_summary_path
end
def summary
#reportIDs = session[:ids]
end
Many thanks to the two folks who helped me on this and showed me the ways of the session variable.
I ran into a problem with the session variable being 'lost' between controller actions - Rails / Devise - updating session variables between controller actions
I would suggest that you store the initial information into the database. From there I would either save the id in a cookie or session variable. You would then be able to retrieve the information from the database in the approval controller. Another option I would consider would be to generate a guid as an id and pass that along to the sign in page as a query string parameter. Store the guid in a hidden input tag, under the form tag. Then when the user submits their credentials, you can also pass the guid. At that point you log them in. If successful, pull the guid from the posted data and then retrieve the database information with the guid.
Wade

Rails Mongoid How can I validate new entry into the database to prevent duplicate?

I am having an issue with my Rails app, that if a user clicks submits on a form, then stops the browser from proceeding (KILL_CURSOR), if they resubmit the form it will create a duplicate entry in the database. I've tried installing javascript to disable input after submit, but it doesn't work in this use case.
Is there a way for me to validate whether a form is a resubmit even it it has the same authenticity token?
Here is my controller:
def create
#location = Location.new(params[:location])
#location.user_id = current_user.id
if #location.save
#refresh window to show new location
render :js => "window.location.reload();"
#check that form fields have been completed properly
else
respond_to do |format|
format.js
end
end
First, it is a good idea to disable the submit button using javascript. Not only this meets your need, but also it gives users a good feedback of their actions. But you can't rely on that, because the javascript might be disabled in web browser.
You could check the database to see whether there's already a duplicated document before inserting a new one. The point is that the definition of duplication is based on your business logic. For example, two requests about the same location from the same user in 5 minutes are duplicated. However, in extreme cases, two or more concurrent requests may lead to duplicated documents, because they may check the database at the same time, seeing nothing, then insert.
In the database layer, unique index in MongoDB could be helpful if some fields of data themselves defines the duplication and should be unique. For example, a given user at a given location should occur at most once in database. Building a unique index (user_id, location._id) can guarantee that constrain.
db.collection.ensureIndex( { user_id: 1, location._id: 1 }, { unique: true } )
Note unique index is not allowed in sharding unless it is the shard key. Because there is no easy way to guarantee that among many machines, if it's not the shard key.

how to forbidden page cache on rails

i'm design a shopping cart,there are two web page,
first is checkout page,and the second is order_success page
if user use the go back button on webbrowser then it will go back to checkout page after user have go to order_success page.
so i want to find some way to forbidden let use go back,
is there some way on rails to archieve this?
"i know this but user will use the go back button,if there some cache in client,
user still could see something & re-submit, this is not what I expect" [was that the meaning?]
If you want to prevent the user resubmit the same request, you may add a hidden field (just something random) and store it in a session after the request has been processed:
if params[:token] == session[:used_token]
render_already_processed_notice
return
end
if #cart.save
session[:used_token] = params[:token]
....
If you use a cart ID in a request, then you just may use the status of the Cart model:
#cart = Cart.find(params[:id])
render_some_notice and return if #cart.done?
....
#cart.done = true
#cart.save
Personally I would not create a checkout and order_success pages. I would just create a single page - cart status, and depending on the model status I would just display different content.
If some action may be executed only once (like closing the cart or finalizing the transaction) there is no problem: render_something and return if #cart.already_closed?
On pages, where multiple submits are possible, but not always welcome (like adding a product to the cart - the user can add two identical products) you may do two things:
1) Use the mentioned token, which will detect when the user just pressed F5, and ask him whether the action really should be done twice, or
2) Just accept the requests, but always provide the user with methods to 'rollback' the actions (allow him to delete products from the cart), and ensure that the cart content will be verified before final acceptance.
clear shopping cart
redirect user to new action
After a purchase is successful you should clear the shopping cart and then redirect the user to a new action.
In your order_controller.rb
def create
#shopping_cart = nil
redirect_to some_path
end

Resources