0

I need to upload external directory references (json file) to my users data. It's a good opportunity to learn how to create an API and use cURL, but I still meet at least one issue.

I start with a single element, but I plan to pass a json data file at the end of the story. Typical elements to load look like this:

{"name": "Test", "id": "35", "external_id": "X-001"}

I went through the cURL manual and several StackOverflow posts to finally build this API:

Created a dedicated route in routes.rb

match '/API/user_directory', to: "users#set_external_reference", via: :post

Added a method to the users controller

def set_external_reference
  puts "Loaded parameters:"
  puts params
  if target_user = User.find(params[:id])
    target_user.update_attributes(external_directory_id: params[:external_id])
    render json: {"Response": "OK"}, status: 200
  else
    render json: {"Response": "not OK"}, status: 500
  end
end

Workaround Devise authentication requirement and CanCanCan authorisation in the users controller

class UsersController < ApplicationController
# Check for active session 
  before_action :authenticate_user! unless ->{:action == 'set_external_reference'}
  load_and_authorize_resource except: :set_external_reference

Workaround CSRF in the application controller

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception unless -> { request.format.json? }

I'd be glad to hear from you if this is a good approach, or if it exposes the web site to security threards.

But the issue raises when I try to run the cURL request:

curl --noproxy localhost -d "{"name": "Test", "id": "35", "external_id": "X-001"}" -H "Accept: application/json" -H "Content-type: application/json" http://localhost/API/user_directory > error.html

The following error is raised when trying to parse request parameters:

ActionDispatch::Http::Parameters::ParseError in UsersController#set_external_reference

767: unexpected token at '{name: Test, id: 35, external_id: X-001}'

At this point, I can't find a clue to this issue. Can you provide some help? Thanks a lot!

user1185081
  • 1,898
  • 2
  • 21
  • 46
  • 1
    I think the payload isn’t a valid JSON. Try something like following "{\"name\":\"Test\",\"id\":\"35\",\"external_id\":\"X-001\"}". Or validate your JSON payload to check if it's valid. – Farhad Ajaz Apr 29 '21 at 10:27
  • Both {"name": "Test", "id": "35", "external_id": "X-001"} and "{\"name\":\"Test\",\"id\":\"35\",\"external_id\":\"X-001\"}" are accepted by validators. But "{"name": "Test", "id": "35", "external_id": "X-001"}" is not. As the first is the format of my input file, I'll pass the parameter in a file, such as: -d @external_ids.json – user1185081 Apr 29 '21 at 11:32
  • I initially tried -d '{"name": "Test", "id": "35", "external_id": "X-001"}', but this would raise a URL error. – user1185081 Apr 29 '21 at 11:33
  • What is the output of this line `puts params` in `set_external_reference` method – Farhad Ajaz Apr 29 '21 at 11:55
  • {"controller"=>"users", "action"=>"set_external_reference", "user"=>{}} – user1185081 Apr 29 '21 at 13:28
  • This answer might help you. https://stackoverflow.com/questions/49053954/testing-post-actiondispatchhttpparametersparseerror-765 Suggesting to remove `'Content-Type' => 'application/json'` also your params are empty so it won’t find any target_user. You can try sending curl data with as string values like ` curl -X GET -d "user[id]=1" -d "user[name]=Test" -d "user[external_id]=1212"` – Farhad Ajaz Apr 29 '21 at 13:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/231745/discussion-between-user1185081-and-farhad-ajaz). – user1185081 Apr 29 '21 at 13:47

1 Answers1

1

I recommend that you avoid the JSON syntax problems and escaping needed for the shell by putting the JSON input into a file. If the file is named params.json then use -d @params.json to pass it from a file.

As for authentication, I'm not sure this is a good idea either but you might find a session key and pass it in the cookie header. If you are using database sessions (which is a good idea) then it will be the value in the column for the session in your database. If not then use dev tools and get your session from the browser.

Marlin Pierce
  • 9,931
  • 4
  • 30
  • 52
  • Chatting with @Farhad Ajaz took us to the same conclusion. Thank you both. – user1185081 Apr 29 '21 at 14:17
  • Authentication is based on Devise gem, the session token is not easy accessible I think ... but with dev tools, isn't it? – user1185081 Apr 29 '21 at 14:18
  • It seemed nearly solved from those comments but was missing the suggestion of a data file which is a cleaner way to handle the necessary shell escaping and should have a write up for posterity. I also needed 10 points for my RoR silver badge. – Marlin Pierce Apr 29 '21 at 14:20
  • The session key is passed to the web server as a cookie. The cookies are visible with dev tools for the browser. Database sessions will make this more available. (Also sessions are managed in the rack layer which Devise merely uses.) – Marlin Pierce Apr 29 '21 at 14:21