JSONAPI-Resources / JSONAPI-Resources/jsonapi-resources

[0.9.12] Need to add handle the invalid request

Open
#1,375 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Ruby
Stars
2.3k
Forks
546
PR merge metrics
No merged PRs in 30d

Description

Need to add handle the invalid request
The bug report below shows the necessary cases

begin
  require 'bundler/inline'
  require 'bundler'
rescue LoadError => e
  STDERR.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
  raise e
end

gemfile(true, ui: ENV['SILENT'] ? Bundler::UI::Silent.new : Bundler::UI::Shell.new) do
  source 'https://rubygems.org'

  gem 'rails', require: false
  gem 'sqlite3', platform: :mri
  gem 'byebug'

  gem 'activerecord-jdbcsqlite3-adapter',
      git: 'https://github.com/jruby/activerecord-jdbc-adapter',
      platform: :jruby

  if ENV['JSONAPI_RESOURCES_PATH']
    gem 'jsonapi-resources', path: ENV['JSONAPI_RESOURCES_PATH'], require: false
  else
    gem 'jsonapi-resources', git: 'https://github.com/cerebris/jsonapi-resources', require: false
  end

end

# prepare active_record database
require 'active_record'

class NullLogger < Logger
  def initialize(*_args) end

  def add(*_args, &_block) end
end

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = ENV['SILENT'] ? NullLogger.new : Logger.new(STDOUT)
ActiveRecord::Migration.verbose = !ENV['SILENT']

ActiveRecord::Schema.define do
  # Add your schema here
  create_table :authors, force: true do |t|
    t.string :name
  end

  create_table :books, force: true do |t|
    t.string :text
    t.references :author
  end
end

# create models
class Author < ActiveRecord::Base
  has_many :books
end

class Comment < ActiveRecord::Base
  belongs_to :author
end

# prepare rails app
require 'action_controller/railtie'
# require 'action_view/railtie'
require 'jsonapi-resources'

class ApplicationController < ActionController::Base
end

# prepare jsonapi resources and controllers
class AuthorsController < ApplicationController
  include JSONAPI::ActsAsResourceController
end

class BooksController < ApplicationController
  include JSONAPI::ActsAsResourceController
end

class AuthorResource < JSONAPI::Resource
  attribute :name
  has_many :books, polymorphic: true, class_name: 'Book'
end

class BookResource < JSONAPI::Resource
  attribute :text
end

class TestApp < Rails::Application
  config.root = File.dirname(__FILE__)
  config.logger = ENV['SILENT'] ? NullLogger.new : Logger.new(STDOUT)
  Rails.logger = config.logger

  secrets.secret_token = 'secret_token'
  secrets.secret_key_base = 'secret_key_base'

  config.eager_load = false
end

Rails.application.configure do
  config.hosts << 'example.org'
end

# initialize app
Rails.application.initialize!

JSONAPI.configure do |config|
  config.json_key_format = :underscored_key
  config.route_format = :underscored_key
end

# draw routes
Rails.application.routes.draw do
  jsonapi_resources :authors, only: [:show, :update]
end

# prepare tests
require 'minitest/autorun'
require 'rack/test'

# Replace this with the code necessary to make your test fail.
class BugTest < Minitest::Test
  include Rack::Test::Methods

  def json_api_headers
    { 'Accept' => JSONAPI::MEDIA_TYPE, 'CONTENT_TYPE' => JSONAPI::MEDIA_TYPE }
  end

  def test_relationships_as_array
    author = Author.create!
    json_request = {
      data: {
        id: author.id.to_s,
        type: 'authors',
        relationships: []
      }
    }
    patch "/authors/#{author.id}", json_request.to_json, json_api_headers
    assert last_response.bad_request?
  end

  def test_relationships_as_nil
    author = Author.create!
    json_request = {
      data: {
        id: author.id.to_s,
        type: 'authors',
        relationships: nil
      }
    }
    patch "/authors/#{author.id}", json_request.to_json, json_api_headers
    assert last_response.bad_request?
  end

  def test_attributes_as_nil
    author = Author.create!
    json_request = {
      data: {
        id: author.id.to_s,
        type: 'authors',
        attributes: nil
      }
    }
    patch "/authors/#{author.id}", json_request.to_json, json_api_headers
    assert last_response.bad_request?
  end

  def test_relationship_name_in_attributes
    author = Author.create!
    json_request = {
      data: {
        id: author.id.to_s,
        type: 'authors',
        attributes: {
          books: []
        }
      }
    }
    patch "/authors/#{author.id}", json_request.to_json, json_api_headers
    assert last_response.bad_request?
  end

  private

  def app
    Rails.application
  end
end

Solution:
Need update method verify_permitted_params
https://github.com/cerebris/jsonapi-resources/blob/dc96280141715fa531665faada63e88ce356dc92/lib/jsonapi/request_parser.rb#L583

def verify_permitted_params(params, allowed_fields)
  formatted_allowed_fields = allowed_fields.collect { |field| format_key(field).to_sym }
  params_not_allowed = []

  params.each do |key, value|
    case key.to_s
    when 'relationships'
    raise JSONAPI::Exceptions::TypeMismatch.new(nil) unless value.is_a?(ActionController::Parameters) # ADDED THIS LINE

      value.keys.each do |links_key|
        unless formatted_allowed_fields.include?(links_key.to_sym)
          if JSONAPI.configuration.raise_if_parameters_not_allowed
            fail JSONAPI::Exceptions::ParameterNotAllowed.new(links_key)
          else
            params_not_allowed.push(links_key)
            value.delete links_key
          end
        end
      end
    when 'attributes'
    raise JSONAPI::Exceptions::TypeMismatch.new(nil) unless value.is_a?(ActionController::Parameters) # ADDED THIS LINE
    
    attributes = formatted_allowed_fields - resource_klass._relationships.keys # ADDED THIS LINE
      value.each do |attr_key, attr_value|
        unless formatted_allowed_fields.include?(attr_key.to_sym)
          if JSONAPI.configuration.raise_if_parameters_not_allowed
            fail JSONAPI::Exceptions::ParameterNotAllowed.new(attr_key)
          else
            params_not_allowed.push(attr_key)
            value.delete attr_key
          end
        end
      end
    when 'type'
    when 'id'
      unless formatted_allowed_fields.include?(:id)
        if JSONAPI.configuration.raise_if_parameters_not_allowed
          fail JSONAPI::Exceptions::ParameterNotAllowed.new(:id)
        else
          params_not_allowed.push(:id)
          params.delete :id
        end
      end
    else
      if JSONAPI.configuration.raise_if_parameters_not_allowed
        fail JSONAPI::Exceptions::ParameterNotAllowed.new(key)
      else
        params_not_allowed.push(key)
        params.delete key
      end
    end
  end

  if params_not_allowed.length > 0
    params_not_allowed_warnings = params_not_allowed.map do |param|
      JSONAPI::Warning.new(code: JSONAPI::PARAM_NOT_ALLOWED,
                           title: 'Param not allowed',
                           detail: "#{param} is not allowed.")
    end
    self.warnings.concat(params_not_allowed_warnings)
  end
end

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with verify_permitted_params in lib/jsonapi/request_parser.rb around line 583, then run the inline Minitest BugTest reproducer from the issue. The change is complete when the four malformed PATCH payloads are rejected as bad requests without causing an unintended exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
rails, ruby, sqlite
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.