Code Monkey home page Code Monkey logo

strong-params-basics's Introduction

Strong Params Basics

What are Strong Params?

To understand the goal of strong params, let's pretend that you run a pharmacy. What would happen if you let all prescription orders come through without checking for valid prescriptions, driver licenses, etc.? (Spoiler alert: you'd probably end up in jail.) It would be criminal to run a pharmacy without verifying that orders were legitimate. In the same way, Rails wanted to shore up some security vulnerabilities. Since Rails 4+, developers are required to whitelist the parameters that are permitted to be sent to the database from a form.

Setup

To prevent confusion, in previous lessons I manually turned off the strong parameter requirement. Let's discover first why strong params were created and then work with them.

Why Strong Params

In the Rails app in this lesson there is our blog application with Strong Params disabled. Create a new Post by going to /posts/new. Once you have created that post, go ahead and edit it at /posts/1/edit. You'll notice there is no Description field! In this case, I don't want the user to be able to modify the description of a post once it's been created. This happens in all kinds of different cases. You wouldn't want a bank user to be able to edit their account number or balance, would you? But! balance is still a field on the account class. In this case, description is still an attribute for the Post class. Let's see if a user could "hack" our form to be able to modify the description.

1. Right click and inspect the page

2. Find the input for title. It should look something like this: <input type="text" value="asdferwer" name="post[title]" id="post_title">

3. Right click on the input and choose "Edit as HTML"

4. Add the following new Description field:

<br />
<label>Description:</label>
<br />
<input
  type="text"
  value="malicious description"
  name="post[description]"
  id="post_description"
/>

5. Click somewhere else and look! a wild description field appears.

6. Now type in some message into the new field.

7. Click submit and you'll notice that the description has been updated. What a nefarious hack!

That is the problem that strong params were created to fix. We want to make sure that when users submit a form we only let the fields we want get by.

Code Implementation

Let's enable Strong Params. To do this, open up config/application.rb and delete the line that says: config.action_controller.permit_all_parameters = true. Now restart your rails server and navigate to localhost:3000/posts/new. Once there fill out the form and click submit. You'll see we get the following ForbiddenAttributesError:

ForbiddenAttributesError

What this means is that Rails needs to be told what parameters are allowed to be submitted through the form to the database. The default is to let nothing through.

The same error would occur if you were trying to update a record. So how do we fix this? Let's update the create and update methods to look like the code below:

# app/controllers/posts_controller.rb

def create
  @post = Post.new(params.require(:post).permit(:title, :description))
  @post.save
  redirect_to post_path(@post)
end

def update
  @post = Post.find(params[:id])
  @post.update(params.require(:post).permit(:title))
  redirect_to post_path(@post)
end

If you go back to the web browser and click refresh you'll see everything is working for both the create and update actions. Running the Rspec tests reveals that our specs are now passing again as well. You'll notice that our update only has a :title in the permit method. This is because, given our forms, we only want the title to be submittable! If you go and do your nefarious hack again, it won't work. Thwarted!!

Permit vs. Require

What is the deal with #permit vs #require? The #require method is the most restrictive. It means that the params that get passed in must contain a key called "post". If it's not included then it fails and the user gets an error. The #permit method is a bit looser. It means that the params hash may have whatever keys are in it. So in the create case, it may have the :title and :description keys. If it doesn't have one of those keys it's no problem: the hash just won't accept any other keys.

DRYing up Strong Params

The code we wrote above is great if you only have a create method in your controller. However, if you have a standard CRUD setup you will also need to implement the same code in your update action. In our example we had different code for create and update, but generally you have the same items. It's a standard Rails practice to remove code repetition, so let's abstract the strong parameter call into its own method in the controller:

# app/controllers/posts_controller.rb

def create
  @post = Post.new(post_params)
  @post.save
  redirect_to post_path(@post)
end

def update
  @post = Post.find(params[:id])
  @post.update(post_params)
  redirect_to post_path(@post)
end

private

def post_params
  params.require(:post).permit(:title, :description)
end

Now, both our create and update methods in the posts controller can simply call post_params. This is a very helpful method since if you duplicated the strong parameter call in both the create and update methods you would need to change both method arguments every time you change the database schema for the posts table... and that sounds like a bad way to live. However, by creating this post_params method we can simply make one change and both methods will automatically be able to have the proper attributes whitelisted.

Hm, but didn't we say above that we only wanted to permit updates to :title in the update action? We can make sure that we meet that requirement with a slightly fancy splat:

# app/controllers/posts_controller.rb

def create
  @post = Post.new(post_params(:title, :description))
  @post.save
  redirect_to post_path(@post)
end

def update
  @post = Post.find(params[:id])
  @post.update(post_params(:title))
  redirect_to post_path(@post)
end

private


# We pass the permitted fields in as *args;
# this keeps `post_params` pretty dry while
# still allowing slightly different behavior
# depending on the controller action. This
# should come after the other methods

def post_params(*args)
  params.require(:post).permit(*args)
end

Test this out in the browser and you can see that you can now create and update posts without any errors. And you will also notice that all of the Rspec tests are still passing.

strong-params-basics's People

Contributors

annjohn avatar bacitracin avatar danielseehausen avatar dependabot[bot] avatar drakeltheryuujin avatar franknowinski avatar heavenlyboheme avatar ihollander avatar jmburges avatar jordanhudgens avatar lizbur10 avatar maxwellbenton avatar onyoo avatar paulnicholsen27 avatar perpepajn25 avatar pletcher avatar rrcobb avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

strong-params-basics's Issues

Route Order issue in Controller

Firstly, routes are not included. Furthermore, the edit method in the controller being placed last breaks the app. It needs to be above the post_params method.

Please add info about what params.require is actually doing. Too magicky, needs explanation.

https://stackoverflow.com/questions/18424671/what-is-params-requireperson-permitname-age-doing-in-rails-4

check that out.

"The params in a controller looks like a Hash, but it's actually an instance of ActionController::Parameters, which provides several methods such as require and permit.

"The require method ensures that a specific parameter is present, and if it's not provided, the require method throws an error. It returns an instance of ActionController::Parameters for the key passed into require.

"The permit method returns a copy of the parameters object, returning only the permitted keys and values. When creating a new ActiveRecord model, only the permitted attributes are passed into the model.

"It looks a lot like the whitelisting that was formerly included in ActiveRecord models, but it makes more sense for it to be in the controller."

Two Errors not Covered in ReadMe Instructions

In PostsController the update method should be as follows:

def update
    @post = Post.find(params[:id])
    @post.update(params.require(:post).permit(:title, :description))
    redirect_to post_path(@post)
  end

You also are required to create a new migration to add post status. This was also not covered in the ReadMe.

It would be helpful if these tasks were completed in the ReadMe, or at least mentioned as work that is left to do. I keep thinking that I've incorrectly completed the directions in the ReadMe.

post_params method incorrect?

The post_params method allows both the title and description to be updated, but per the lab the update method is only supposed to allow the title to be updated. If only the title is allowed to be updated, I think think post_params should be:

def post_params(*args)
  params.require(:post).permit(*args)
 end

with the permitted fields being passed in as arguments. (ie updated method below)

def update
  @post = Post.find(params[:id])
  @post.update(post_params(:title))
  redirect_to post_path(@post)
end
def create
  @post = Post.new(post_params(:title, :description))
  @post.save
  redirect_to post_path(@post)
end

Routes not included

The lab was created without routes. I added resources :posts to make the example work to be able to follow along.

Be more specific with the page inspector

Old: Find the input for title. it should look like this: <input type="text" value="asdferwer" name="post[title]" id="post_title">
Suggested new: Find the input for title. it should look like this: <input type="text" value="asdferwer" name="post[title]" id="post_title">(attributes might not be in same order)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.