Upgrade to rails 4.1.8 and my api broke - ruby-on-rails

I haven't had the time to go through .6 and .7 as well as .8 in depth to make sure that I wasn't missing something, but today I decided to update my application, which is mostly an API with some front end work.
I updated the application to rails 4.1.8 and I got a lot of errors in my specs, in fact anything where the api had to be authenticated it blew up.
My controllers that use an api have a method called restrict_api_access which is done via
def restrict_api_access
authenticate_or_request_with_http_token do |token, options|
#api_key = ApiKey.find_by(api_key: token)
end unless current_user
end
Now you can log into the app and manage things, in that case we still use the same API that you would access say via a terminal or out side app. so the unless current_user basically looks like like:
def current_user
if (#api_key)
#current_user = User.find_by!(id: #api_key.xaaron_users_id)
else
super
end
end
Ans then the super of this current user basically states - are you logged in or not.
So one of the tests that are failing is set up like such:
context "Create new tag" do
before(:each) do
#tag = FactoryGirl.build(:tag, blog_id: #blog.id)
set_auth_header(#user)
end
it "should create a tag for a post" do
post :create, :blog_id => #blog.id, tag: {name: #tag.name, description: 'asdasddsa'}
json = JSON.parse(response.body)
expect(json['tag']['name']).to eql #tag.name
end
end
#user is set up else where. But I can assure you that a user is being passed in and is created.
The other interesting method here is set_auth_header(#user) this method looks like:
# Create a random api key
def create_api_key(user)
ApiKey.create!(:xaaron_users_id => user.id, :api_key => SecureRandom.hex(16))
end
# Set authentication headers for the API use
def set_auth_header(user)
key = create_api_key(user)
request.env['HTTP_AUTHORIZATION'] = "Token #{key.api_key}"
end
You can see we create an API key with the user, we then set the request environment variable to have that api key.
So before 4.1.8 (I was on 4.1.5) this worked fine, all my tests would pass life was grand. Now, with this particular test, I get:
1) Api::V1::TagsController Create new tag should create a tag for a post
Failure/Error: json = JSON.parse(response.body)
JSON::ParserError:
757: unexpected token at 'HTTP Token: Access denied.
'
# ./spec/controllers/api/v1/tags_controller_spec.rb:112:in `block (3 levels) in <top (required)>'
Essentially I am denied access. I have traced this through to the restrict_api_access to the line: authenticate_or_request_with_http_token which holds, as a value: HTTP Token: Access denied.
Is there something that changed in 4.1.8 that would be causing all my api that requires authentication to break?

You're computing your authorization header manually here, which might be wrong:
request.env['HTTP_AUTHORIZATION'] = "Token #{key.api_key}"
There's an ActionController method that does this for you:
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials(key.api_key)
Try that instead.

Related

How do I bypass recaptcha verification in rails controller w/ RSPEC?

In my rails UsersController - users#sign_up action, I perform verification to ensure the user has a valid recaptcha v3 token before moving on to the rest of the controller logic. If the recaptcha verification fails then the controller returns and responds with an error message. However, my rspec tests are failing because I am unsure how to mock / bypass the verification in the controller.
spec/requests/auth_spec.rb:
RSpec.describe "Authentication Requests", type: :request do
context "sign up user" do
it "fails to sign up a user without email address" do
headers = { :CONTENT_TYPE => "application/json" }
post "/api/v1/sign_up", :params => { :email => nil, :password => "password123"}.to_json, :headers => headers
expect(response.header['Content-Type']).to include('application/json')
expect(response_body_to_json).to eq({"error"=>"Failed to create user"})
end
end
end
The test is failing when I post to /api/v1/sign_up because there are missing params for the recaptcha token. As far as I understand, it isn't possible to mock a recaptcha v3 token. Therefore it would be preferable to have verify_recaptcha return true for the rspec test.
controllers/api/v1/users_controller:
def sign_up
# Rspec fails here with missing params error
return if !verify_recaptcha('sign_up', recaptcha_params[:token])
#user = User.new(user_credential_params)
if #user.valid?
# Handle success/fail logic
end
end
private
def user_credential_params
params.permit(:email, :password)
end
def recaptcha_params
params.permit(:token)
end
controllers/concerns/users_helper.rb:
def verify_recaptcha(recaptcha_action, token)
secret_key = Rails.application.credentials.RECAPTCHA[:SECRET_KEY]
uri = URI.parse("https://www.google.com/recaptcha/api/siteverify?secret=#{secret_key}&response=#{token}")
response = Net::HTTP.get_response(uri)
json = JSON.parse(response.body)
recaptcha_valid = json['success'] && json['score'] > 0.5 && json['action'] == recaptcha_action
if !recaptcha_valid
render :json => { :error_msg => 'Authentication Failure' }, :status => :unauthorized
return false
end
return true
end
Can I stub / mock the verify_recaptcha method that comes from the users_helper concern to return true? Is there a better way to accomplish this?
I did due diligence before asking this question and I found this post: mocking/stubbing a controller recaptcha method with rspec in rails.
This was the answer for that post:
allow(controller).to receive(:verify_recaptcha).and_return(true)
The above didnt work for me because individual had their verify_recaptcha method inside of ApplicationController.rb (which seems a little dirty in my opinion). Given that my verify_recaptcha method is inside of a concern, I am not sure how to access the concern via Rspec.
You can try adding UserController.expects(:verify_recaptcha).returns(true) to your test.
This will bypass the recaptcha or Just try finding where the verify_recaptcha method exists and then write controller or class name before the expect method in
UserController.expects(:verify_recaptcha).returns(true)

get details of user through github api gem in rails

i am using the "github_api" gem in my project to get user data through github
and hear is my controller got this task
class SocialController < ApplicationController
def index
end
def authorize
address = github.authorize_url redirect_uri: 'http://localhost:3000/social/callback'
redirect_to address
end
def callback
puts params
authorization_code = params[:code]
access_token = github.get_token authorization_code
access_token.token
puts Github::Client::Repos.new.list
end
private
def github
#github ||= Github.new client_id: 'f11661f7a9ba943', client_secret: '08aa35ed997b162de257c'
end
end
and i am calling it through this link in my views
<%=link_to "gitouath", controller: "social", action: "authorize" %>
by this i am able to authorize the github profile but i need some other details like his name, profile and projects but i am not able to figure it out, please tell me if there is a way to get those details
I'm not familiar with the github_api gem, so I'll give a generic answer: the API docs are your friends and contain everything you need to know, it's a good idea to get familiar with reading them.
To get you started, the user name and profile information is available via the GET /user endpoint and the list of repositories (I assume this is what you mean by "projects") can be found via the GET /user/repos endpoint.

Use API to authenticate in Rails

I currently have a Rails application that is connected to an existing SQL database. I am using Devise for my user management, however the pre-existing User table in the database uses a very customized password encryption method.
There is a web service I can connect to that passes a JSON object with the login information to authenticate whether it is valid or not, and I have to manage my own session and everything after that.
I attempted to follow "Railscast #250", and combine it with Devise and some Stack Overflow searches, but things are not going very well.
This is what I have now, but it isn't doing anything, and I just don't feel like I am on the right track with this.
class SessionsController < Devise::SessionsController
def new
super
end
def create
post_params = {
"RuntimeEnvironment" => 1,
"Email" => params[:session][:email],
"Password" => params[:session][:password]
}.to_json
user_params = RestClient.post 'http://some.ip/WebServices', post_params, :content_type => "json"
user = User.authenticate(user_params)
if user
session[:user_id] = user.user_id
redirect_to root_path
else
flash.now.alert = "Invalid Username or Password"
render "new"
end
end
end
This is the JSON Object returned if there is a successful login:
{"Success":true,"ErrorMessage":"","ResponseString":"","LoginResultData":{"FailMessage":"","ResultCode":0,"User":{"AccountCompleteFlag":1,"CreationDtime":"\/Date(1430848539000-0400)\/","DeleteFlag":0,"Email":"john#doe.com","FailedPasswordCount":1,"HistoricalFlag":0,"IsDirty":false,"IsAdminFlag":0,"IsSiteAdminFlag":0,"LastLoginDtime":"\/Date(1447789258000-0500)\/","NameFirst":"Ttest","NameLast":"test","Password":"TRQt3d2Z7caDsSKL0ARVRd8nInks+pIyTSqp3BLxUgg=","PasswordLockDtime":"\/Date(-62135578800000-0500)\/","PasswordLockFlag":0,"PasswordResetCode":"","PasswordResetStatus":0,"Phone":"1-X-5555555555-","RegistrationSource":"Registration","UserId":100029,"UserType":1,"PhoneInfo":{"AreaCode":"555","CountryCode":"X","Extension":"","FirstThree":"555","InternationalPhoneNumber":"","IsDirty":false,"IsInternational":false,"LastFour":"5555"}}}}
And what is returned for a failed one:
{"Success":true,"ErrorMessage":"","ResponseString":"","LoginResultData":{"FailMessage":"Invalid email address","ResultCode":1,"User":null}}
Is there a way where I can use Devise's session management while connecting to the API?
You can still authenticate through Devise using the email and password that the user provided. The RestClient would just be like a double check: just make sure that there are no routes that the user can authenticate through besides going through the RestClient. You can check this by doing rake routes.
For checking whether the result code was valid, you can do some JSON parsing as follows:
authentication_response = RestClient.post 'http://some.ip/WebServices', post_params, :content_type => "json"
json_authentication_response = JSON.parse(authentication_response)
result_code = json_authentication_response["LoginResultData"]["ResultCode"]
if result_code == 0
# Authenticate
else
# Don't authenticate
end

Ruby on rails .authentication failing

I'm building an API for a web app I'm developing, and the following code I'm trying to use for API authentication/login is returning false on the authorization.
In my API user controller I have:
def login
if params[:user]
# Find the user by email first
#user = User.where(email: params[:user][:email]).first
if !#user
respond_with nil
else
#auth = #user.authenticate(params[:user][:password])
if #auth
respond_with #user
else
respond_with #auth
end
end
end
end
It is always responding with #auth, which is false, even when valid email and passwords are being provided. It has no problem pulling the user info from my Mongo db.
I guess I'm just not clear on what .authenticate does. According to a railscast.com video I watched, it should compare that users password digest with the password entered. When a valid password is provided for the user, #auth is always false.
This method was actually working fine, the test data in the database wasn't what i thought it was..

REST Client for Ruby Gives ActionController::InvalidAuthenticityToken

I have a RESTful Rails application with a resource called "Foo". I'm trying to use REST Client to do a put:
resource = RestClient::Resource.new 'http://localhost:3000/foos/1', :user => 'me', :password => 'secret'
resource.put :name => 'somethingwitty', :content_type => 'application/xml'
But my app raises:
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'
It seems like my app isn't getting the message that this is an XML request and that the AuthenticityToken should be ignored. Maybe I'm not using REST Client correctly. Any ideas on why I'm getting the exception?
Try putting an :only => [:update, :delete, :create] on the protect_from_forgery line in your application controller.
More info: http://ryandaigle.com/articles/2007/9/24/what-s-new-in-edge-rails-better-cross-site-request-forging-prevention
Use something like:
resource.put '<foo><name>somethingwitty</name></foo>', :content_type => 'application/xml'
I think you need to make two changes;
(a) Use the rails routing to tag this as an XML request
(b) Use HTTP Basic Authentication to authenticate the request.
This means changing your URL above to include the username, password like this
me:secret#localhost:3000/foos/1.xml
also note .xml bit
I guess that somewhere on your server-side you have code that authenticates in-bound requests via a before filter. This needs to work something like this ...
#
# If you haven't authenticated already then you are either
# reqirected to the logon screen (for HTML formats) or
# the browser prompts you. You are always allowed to pass
# the username/password in the URL
#
def login_required
#current_user = valid_session?
unless #current_user
if params["format"]
#
# If you specify a format you must authenticate now
#
do_basic_authentication
else
display_logon_screen
end
end
end
#
# Ask Rails for the login and password then authenticate as if this
# were a new login.
#
def do_basic_authentication
user = authenticate_with_http_basic do |login, password|
User.authenticate(login, password)
end
if user
current_user(#current_user = user)
else
request_http_basic_authentication
end
end
That's from our own app and is triggered by a before_filter in ApplicationController.
Also, I don't think you need the :content_type => 'application/xml'. What I normally do is just call post or put directly like this ..
response = RestClient.post URI.encode(url), :record => args
where the url contains the basic authentication and the ".xml"
Happy coding
Chris
Since your application is a Rails app, it might be easier to use ActiveResource for the client.
Something like:
require 'active_resource'
class Foo < ActiveResource::Base
self.site = 'http://localhost:3000/'
end
foo = Foo.new(:name => 'somethingwitty')
foo.save
You can read up on how to do the authentication on the rdoc site.

Resources