Rails 4: Update Model Column on Destroy - ruby-on-rails

This is for a ticketing system.
When you close a ticket, theres a column of resolution. The user types in the resolution, ie, "this ticket was resolved by xyz". The column is of type textfield, not a string. So you go to close it and type in some sort of resolution. But that field does not get 'updated' when the ticket is deleted.
Summary: update the column of resolution on the Ticket model when the ticket is destroyed. Reasoning: the resolution has to be passed to email (via Sendgrid) and SMS (via Twilio). Currently, it'll pass the default value of resolution (whatever that value may be when the ticket is created).
In the initial ticket creation form, I have resolution as a hidden field like so:
<%= f.hidden_field :resolution, :value => "No Resolution Provided" %>
What I've tried:
In the ticket.rb model:
before_destroy { self.update_attribute(:resolution, "a hardcoded value here") }
So sure that works but that isn't reading from the form, just a hardcoded value. Correct me if I'm wrong but what I'm attempting to do should be done in the controller, correct?
I've tried a before_action but haven't had much success. This method does not work when used with a before_action:
def update_resolution
#ticket = Ticket.find(params[:id])
#ticket_res = #ticket.resolution
#ticket_res.update_attribute(params[:resolution])
end
The above creates a redirect loop.
What's the best way to go about this? Any and all input is appreciated.

Updating a record just prior to deleting it just for some other function that doesn't actually need the record to work doesn't sound like a good way of working to me.
This workflow makes more sense to me:
Submit form to controller for resolved ticket, with resolution text
Create a background email job with details of the resolution to notify interested parties
Create another background twilio job with the SMS detaios to notify interested parties
Destroy the ticket (are you sure you won't ever need it again?)
You should read the Rails Guides on background jobs: http://guides.rubyonrails.org/active_job_basics.html
Whilst not the fastest background job system, delayed job will be the easiest to get started with - https://github.com/collectiveidea/delayed_job

Related

How can I create a route and use a path to enter the first step in a mulistep form with the Wicked gem?

I have tried using the Wicked gem 3 different times over the past 8 years. Each time, I have given up for the same reason. I'm trying again, because if I understand it, I think it will be perfect for my use case.
My main problem is that I don't understand how to actually begin the wizard. With the example used in the gem, it is an after_registration event that already has an associated user object. That is not helpful, nor do I think that example would be helpful in the majority of use cases.
There is another example about building a Product in multiple steps. However, the author fails to adequately explain the routing. From https://github.com/zombocom/wicked/wiki/Building-Partial-Objects-Step-by-Step:
Since Wicked uses our :id parameter we will need to have a route that also includes :product_id for instance /products/:product_id/build/:id. This is one way to generate that route:
resources :products do
resources :build, controller: 'products/build'
end
This also means to get to the create action we don't have a product_id yet so we can either create this object in another controller and redirect to the wizard, or we can use a route with a placeholder product_id such as [POST] /products/building/build in order to hit this create action.
OK, I have no idea what the second part of the sentence means as far as placeholder product_id and that route name of /products/building/build. I spent 2 hours trying that and just moved on to a blank create form.
...we can either create this object in another controller and redirect to the wizard
That's what I'm trying to do upon successful save of the #product object.
redirect_to product_by_interchange_path(#product, :step1)
That doesn't work. raise InvalidStepError if the_step.nil? Says my step is nil. It's not.
redirect_to product_by_interchange_path(#product, step: :step1)
Same thing.
redirect_to product_by_interchange_path(:step1)
That's an exact mirror of the 8 year old example app. But of course #product isn't in a session variable like current_user is, so in this case the error is that there's no Product with an id of :step1.
Please help! I am missing something very, very basic here but I very much need to persist.
OK I have finally figured this out. Here's what I did:
First of all, I changed my controller back to a plain old ApplicationController and used the include include Wicked::Wizard. I don't know if that did anything, but the newer example was laid out like the old.
I was really screwed up by :id. I'm thinking :id is generally my object ID. I had a set_product private method in my controller, and it was failing. When I finally figured out that :id was the actual step itself, that led me to change my path in the redirect.
I changed the redirect from product_by_interchange_path(#product, :select_vehicle) to product_by_interchange_path(:select_vehicle, product_id: #product.id)
I got rid of my set_product. Just while I was trying to eliminate confusion.
I changed my finder calls in the wizard to use :product_id instead of :id.
It works now. I still don't understand how I could have stubbed out a route with a placeholder product_id, that's still a mystery. But this is fine and it works.

Spree Confirmation Emails

So, I want to set up my Spree store such that confirmation emails are not sent when we create orders in the back-end, since we get customers who place orders outside of our store website, which we would like entered into our Spree database nonetheless. The best way we can think of to do this (if you have another way, by all means chime in) is to disrupt this bit of code in the orders model (you can find it at /core/app/models/spree/order.rb):
def finalize!
# lock all adjustments (coupon promotions, etc.)
all_adjustments.each{|a| a.close}
# update payment and shipment(s) states, and save
updater.update_payment_state
shipments.each do |shipment|
shipment.update!(self)
shipment.finalize!
end
updater.update_shipment_state
save!
updater.run_hooks
touch :completed_at
if paid? #THIS CONDITIONAL IS THE BIT WE ADDED
deliver_order_confirmation_email unless confirmation_delivered?
end
consider_risk
end
Now it's just a matter of testing this. The only kind of payment that can be tested through the GUI is check, and I need to see if this trips with a credit card payment that's been authorized/not authorized/captured/not captured (since Spree's documentation on what qualifies as a payment_state of 'balance_due', 'pending' or 'paid' is pretttty bad). So I've been trying to use factory girl's stuff to make an order like that, and see whether or not a confirmation email has been sent. The only thing I can find for testing confirmation emails, however, involves sending one out, and also I can't find anything that would help me build a factory for an order that specific. Am I missing something?

Issue and clarification needed with attr_accessible

There is so much written about the security threat of attr_accessible that I am beginning to wonder if I should even have any attributes in it. Here is the issue. I have a Message model which has the following:
attr_accessible :body,:sender_id,:recipient_id
I do not have the update or edit action in my messages_controller. With the new and create action I am able to create a new message and send it to a recipient. Only users who have logged in and meet certain conditions can message each other. I do that with the help of a before_filter and the conditions work fine. The message is stored and can be viewed by the sender and the recipient. Perfect!
The question I have is that since :body,:sender_id,:recipient_id are included in attr_accessible, can a malicious user somehow change the :body,:sender_id,:recipient_id of the original message? Should I just add these attributes to attr_readonly as well so they cannot be modified once saved?
This question has been haunting me for practically all my models.
can a malicious user somehow change the :body,:sender_id,:recipient_id
of the original message?
This would depend on other things rather than attr_accesible. attr_accesible will only filter which fields are allowed to be updated using mass assignment. Since you say you don't have any update action, then no, there is now way a user can edit a message since you always create a new Message through you create action.
But there is something you need to care about. What is sender_id? If you do have users in your app and they send messages to each others, then sender_id should not be an accessible field, since this will allow users to send messages on behalf of other users. You probably want to keep that field off the attr_accessible list and do something like this:
m = Message.new params[:message] # body and recipient_id
m.sender_id = current_user.id # this is not mass assignment
m.save
.....
Well, it depends on how your are creating your model's instance. If you use:
FooModel.create(params[:foo])
then yes, your are not secure because a logged in user may pass additional parameters to the request even if you don't provide explicitly form fields for those attributes.
So, for your case, anyone posting to your "create" action with sender_id, recipient_id (values in the request) will be able to change them unless you take care about this assignments in your action.

Logic for sending emails to particular User in Rails

This is more like a conceptual question because I have the feeling that I am doing this wrong:
I have to send emails to some Users that satisfy some conditions. What I have right now is the following:
1) A task that basically does this:
users = User.includes(:aptitudes).where({:role => ['Boy','boy','kid'], :aptitudes => {:name =>'Good'} })
users.each do |user|
MyMailer.report(user).deliver
end
2) In the report method of MyMailer, I have several things:
def report(user)
#user = user
#value = #user.value
#travels = #user.travels.where(:end_at)
#rewards = #user.rewards
# More logic depending of values of User.
...
end
What happens is that in the Mailer itself sometimes a particular property or attribute of the object #user does not satisfy for the email to be sent. I am wondering where am I supposed to make sure that all the Users that I pass to the Mailers will be sent an email ? Shall I create a new method in the rake to do those checkings? Or how would you do that?
Generally speaking, you can do as you wish as long as the name is revealing enough of the intentions.
If you gave me this MyMailer object to use, without knowing the implementation, I would expect it to send a report when requested and not filter out of its own whim... eventually deal with other problems (fail to send, delays, retries etc.) but when I give it a user I want that user to be emailed.
So, to answer your question, yes I would either filter out all the users before calling the Mailer, or define a method in the Mailer called something like report_only_those_users that will take care of the filtering.
By the way, you can pass a list of emails to ActionMailer (section 2.3.4), without having to loop and send one at a time.

Prevent modification ("hacking") of hidden fields in form in rails3?

So lets say I have a form for submitting a new post.
The form has a hidden field which specify's the category_id. We are also on the show view for that very category.
What I'm worried about, is that someone using something like firebug, might just edit the category id in the code, and then submit the form - creating a post for a different category.
Obviously my form is more complicated and a different scenario - but the idea is the same. I also cannot define the category in the post's create controller, as the category will be different on each show view...
Any solutions?
EDIT:
Here is a better question - is it possible to grab the Category id in the create controller for the post, if its not in a hidden field?
Does your site have the concept of permissions / access control lists on the categories themselves? If the user would have access to the other category, then I'd say there's no worry here since there's nothing stopping them from going to that other category and doing the same.
If your categories are restricted in some manner, then I'd suggest nesting your Post under a category (nested resource routes) and do a before_filter to ensure you're granted access to the appropriate category.
config/routes.rb
resources :categories do
resources :posts
end
app/controllers/posts_controller
before_filter :ensure_category_access
def create
#post = #category.posts.new(params[:post])
...
end
private
def ensure_category_access
#category = Category.find(params[:category_id])
# do whatever you need to do. if you don't have to validate access, then I'm not sure I'd worry about this.
# If the user wants to change their category in their post instead of
# going to the other category and posting there, I don't think I see a concern?
end
URL would look like
GET
/categories/1/posts/new
POST
/categories/1/posts
pst is right- never trust the user. Double-check the value sent via the view in your controller and, if it does't match something valid, kick the user out (auto-logout) and send the admin an email. You may also want to lock the user's account if it keeps happening.
Never, ever trust the user, of course ;-)
Now, that being said, it is possible to with a very high degree of confidence rely on hidden fields for temporal storage/staging (although this can generally also be handled entirely on the server with the session as well): ASP.NET follows this model and it has proven to be very secure against tampering if used correctly -- so what's the secret?
Hash validation aka MAC (Message Authentication Code). The ASP.NET MAC and usage is discussed briefly this article. In short the MAC is a hash of the form data (built using a server -- and perhaps session -- secret key) which is embedded in the form as a hidden field. When the form submission occurs this MAC is re-calculated from the data and then compared with the original MAC. Because the secrets are known only to the server it is not (realistically) possible for a client to generate a valid MAC from the data itself.
However, I do not use RoR or know what modules, if any, may implement security like this. I do hope that someone can provide more insight (in their own answer ;-) if such solutions exist, because it is a very powerful construct and easily allows safe per-form data association and validation.
Happy coding.

Resources