I am working on rails json webservices. I am using warden for authentication in that, its working fine with html format , but in json its not working. because passed parameter is not working in config/intializers/wrden.rb file .below is the code
config/intializers/warden. rb
def authenticate!
Rails.logger.info '!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
Rails.logger.info params['emailID']
user = User.find_by_emailID(params['emailID'])
if user && user.authenticate(params['password'])
success! user
else
fail "Invalid email or password"
end
end
here is log of this
Parameters: {"emailID"=>"xyz#gmail.com", "password"=>"123456", "session"=>{"emailID"=>"xyz#gmail.com", "password"=>"123456"}}
(0.2ms) BEGIN
!!!!!!!!!!!!!!!!!!!!!!!!!!!!
nil
User Load (12.2ms) SELECT "users".* FROM "users" WHERE "users"."emailID" IS **NULL** LIMIT 1
(0.3ms) COMMIT
Completed in 276ms
Well, I just ran into this, too. This GitHub thread fixed it for me:
https://github.com/hassox/warden/issues/84
Basically, tell Warden to use Rails' request object instead of whatever it's using by default. You can use the rails-warden gem or mix this into Warden:
module Warden::Mixins::Common
def request
#request ||= ActionDispatch::Request.new(#env)
end
end
Related
Rails mailer instances do not have any context about the request. However this is needed to service multiple hosts. I was hoping to invoke url_for for the mailer, but I am confused to both the placing of it AND how it should be constructed (the api documentation provides examples for controllers, not mailers).
The form submission has a hidden_field :host, value: #site.host which percolates to the request
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"some#quack.com", "host"=>"localhost"}
environments/development.rb has config.action_mailer.default_url_options commented out.
initializer for devise has config.parent_mailer = 'DeviseMailer'
The users/passwords_controller.rb has been edited
class Users::PasswordsController < Devise::PasswordsController
def create
puts params[:host]
super
end
and mailers/devise_mailer.rb sets
class DeviseMailer < ActionMailer::Base
layout 'mailer'
before_action :set_mailers_url_host
def set_mailers_url_host
puts 'host'
puts params
ActionMailer::Base.default_url_options[:host] = params[:user][:host]
end
I did not expect the mailer to know the parameter as it is designed to inherit from < ActionMailer::Base
However, the log is indicating that the password controller generated is not being invoked. the 'host' string is being put, then an empty line indicates the mailer know nothing of the params
Processing by Devise::PasswordsController#create as HTML
[...]
↳ app/controllers/application_controller.rb:284:in `get_departments'
User Load (2.5ms) SELECT "users"...
User Load (1.2ms) SELECT "users"...
TRANSACTION (1.0ms) BEGIN
User Update (1.7ms) UPDATE "users" SET "reset_password_token" = $1, "reset_password_sent_at" = $2 WHERE "users"."id" = $3 [["reset_password_token", "..."], ["reset_password_sent_at", "..."], ["id", 45]]
TRANSACTION (6.1ms) COMMIT
host
Devise::Mailer#reset_password_instructions: processed outbound mail in 0.6ms
Completed 500 Internal Server Error in 745ms (ActiveRecord: 81.6ms | Allocations: 203970)
NoMethodError (undefined method `[]' for nil:NilClass):
app/mailers/devise_mailer.rb:8:in `set_mailers_url_host'
The error is expected given params[:user][:host] is an unknown entity to the mailer. The bypassing of the passwords controller, not.
Also attempted: commenting out the devise_mailer before_action and adding to the application_controller.rb, where #site is set in before_action :set_site :
def default_url_options
{ host: #site.host, locale: I18n.locale }
end
While this is the most succinct way of dealing with the case, that fails with error ActionView::Template::Error (Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true)
Why are the checks (via puts) in the passwords controller not being invoked?
How should url_for thus establish the required string based on params[:user][:site]
This is a way to solve your issue but I'm not quite satisfied with the beauty of this. 🤔
around_action :change_host
def change_host
default_options = Rails.application.routes.default_url_options
new_options = { host: param[:user][:host], port: :thing }
Rails.application.routes.default_url_options = new_options
Rails.application.configuration.action_mailer.default_url_options = new_options
yield
Rails.application.routes.default_url_options = default_options
Rails.application.configuration.action_mailer.default_url_options = default_options
end
Given all the moving parts, a quick review:
comment out config.action_mailer.default_url_options
submission of param in form for host
no need for DeviseMailer class
setting default_url_options in application_controller not necessary
url_for is uncalled for
First element of answer: get the routing established correctly:
devise_for :users, controllers: { passwords: 'users/passwords' }
Second element of solution (preliminary - works in development on remote server), modify the devise passwords controller.
def create
ActionMailer::Base.default_url_options[:host] = params[:user][:host]
super
end
I'm trying to request the json pages for multiple subreddits and take the title and link from each page for a college project. here's the code in question:
require 'rufus-scheduler'
require 'json'
require 'httparty'
ENV['TZ'] = 'Europe/Dublin'
scheduler = Rufus::Scheduler::singleton
scheduler.every '12h00m', :first_at => Time.now + 10 do
array_of_subreddits = ["pics", "memes", "funny", "aww", "memes",
"birdswitharms"]
array_of_subreddits.each do |category|
sleep 10 #wait 10 seconds between each request
#response = JSON.parse(HTTParty.get("http://reddit.com/r/#{category}/.json?limit=25").body)
#response['data']['children'].each do |data|
#link = data['data']['url']
#title = data['data']['title']
#category = category
Pic.create([{:title => "#{#title}", :link => "#{#link}", :category => "#{#category}"}])
end
end
end
this works perfectly sometimes, it will run through each one and end as it should. more often than not however it will give me this message after after one or two passes:
NoMethodError (undefined method `[]' for nil:NilClass):
app/controllers/home_controller.rb:17:in `block in index'
app/controllers/home_controller.rb:9:in `each'
app/controllers/home_controller.rb:9:in `index'
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_source.erb (4.8ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.2ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.2ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (66.2ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/_markup.html.erb (0.4ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.3ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.3ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.5ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/console.js.erb within layouts/javascript (51.6ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/main.js.erb within layouts/javascript (0.3ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.5ms)
Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/index.html.erb (124.8ms)
Creating client classes is a much better way to work with httparty:
class RedditClient
include HTTParty
format :json
base_uri "http://reddit.com/r/"
def self.get_category(category, *opts)
opts.reverse_merge(limit: 25)
get("/#{category}.json", opts)
end
end
This way HTTParty handles JSON parsing for us and does not attempt to an convert an empty response. Its also much easier to test separately.
However you should still check if the response was successful before trying to use it:
#response = RedditClient.get_category(category)
if #response.success?
attrs = #response['data']['children'].map do |child|
{
category: category,
link: child['data']['url'],
title: child['data']['title']
}
end
Pic.create!(attrs)
else
# log it or raise some sort of error
end
Note that you where passing an array containing a single hash to .create. You can instead pass an array of hashes and it will insert the records in a single SQL insert statement.
When you get errors like this, you should always dump the actual response so you can inspect it. The fact you got a error for a nil with code doing stuff like ['data']['children'] means Id guess you got a JSON response, but one that is missing one of the first items (e.g. ['data'] returned nil).
Dont just assume every request is successful, many things can make a HTTP fail. Its possible that you get a valid JSON response back, just not the one you are expecting, for example an error message which would have told you the problem.
Also even with a 10 second delay, you may be hitting a rate limit (never tested Reddit personally), but read the rules
Many default User-Agents (like "Python/urllib" or "Java") are drastically limited to encourage unique and descriptive user-agent strings.
This kind of errors are most common in ruby or rails. Can be handled in multiple ways. As #Stefan suggested you can use any of the bellow.
Most simply like this
response = HTTParty.get('http://reddit.com/r/#{category}/.json?limit=25')
if response.success?
response_body = response.body
# continue
end
or
response = HTTParty.get('http://reddit.com/r/#{category}/.json?limit=25')
case response.code
when 200
puts "Good!"
# Continue your parsing
when 404
puts "NOT FOUND!"
when 500...600
puts "ERROR #{response.code}"
end
or
begin
HTTParty.get('http://reddit.com/r/#{category}/.json?limit=25')
rescue HTTParty::Error
# HTTParty errors like Not found
rescue StandardError
# StandardError like Timeout
else
# continue
end
I have been trying to follow https://github.com/plataformatec/devise/wiki/How-To:-Override-confirmations-so-users-can-pick-their-own-passwords-as-part-of-confirmation-activation in order to allow users on my app to supply only an email address when they sign up, and then be prompted for the remainder of their information after they confirm their email. But the approach seems to be slightly broken or I don't fully understand it.
In the instructions, in the confirmations_controller there is this method:
def with_unconfirmed_confirmable
original_token = params[:confirmation_token]
confirmation_token = Devise.token_generator.digest(User, :confirmation_token, original_token)
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
if !#confirmable.new_record?
#confirmable.only_if_unconfirmed {yield}
end
end
If I leave it as is, I always get a "Confirmation Token is Invalid" error. It appears to be due to the new token being generated and not found. You can see what I mean in the log:
Started GET "/users/confirmation?confirmation_token=9835abdff3d03d0a29e1c5a640c6a22f1ed6289b4cf696ed514ba183aad49caa" for 127.0.0.1 at 2013-11-12 07:39:42 -0700
Processing by ConfirmationsController#show as HTML
Parameters: {"confirmation_token"=>"9835abdff3d03d0a29e1c5a640c6a22f1ed6289b4cf696ed514ba183aad49caa"}
ESC[1mESC[36mUser Load (0.3ms)ESC[0m ESC[1mSELECT "users".* FROM "users" WHERE "users"."confirmation_token" = '98e17d2ea3cc3fcba5cab7d37bd9a865fc2e318372cb293b541b8a05b46f
e4a3' LIMIT 1ESC[0m
But if I change the method to use the original_token instead of the generated confirmation_token, it all works. What am I missing? Should I be worried about not using the derived token?
Turns out the error was because my app was on an older version of devise that inadvertently got upgraded to devise 3.1. Thanks to this post: Upgrading to devise 3.1 => getting Reset password token is invalid
I updated the mailer to send the correct token and now all is working.
I am trying to save a form with rails, its a simple one, and everytime I try to send a name with a space (like john smith) I dont actually get an error, it returns succcess, but does not save anything, when I try johnsmith then it works.
I checked on my model and I have this
validates :first_name, :allow_blank => true, :format => { :with => /\A[a-zA-Z]+\z/, :message => "Only letters allowed" }
validating only letters, but accepting spaces, still, when I try, no success.
At my controller I have something like this.
name = params[:name].to_s
and later
#user.atributes = { :weight => weight, :name => name ... and so on
at the end I only make a #user.save
Any idea how to avoid this problem? I do want to accept spaces on the names, but without getting into security problems.
Thanks
result of the post in my console
Started POST "/users/custom" for 192.168.1.21 at 2013-05-21 17:51:06 -0600
Processing by UsersController#custom as JS
Parameters: {"name"=>" new user", "lastname"=>" my last name", "mail"=>"newuser#gmail.com", "sex"=>"0" ... n so on}
User Load (0.1ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 6 LIMIT 1
(0.1ms) BEGIN
(0.3ms) UPDATE `users` SET `first_name` = ' new user', `ssn` = 0, `updated_at` = '2013-05-21 23:51:06' WHERE `users`.`id` = 6
(1.2ms) COMMIT
Rendered users/custom.html.erb within layouts/application (0.1ms)
Completed 200 OK in 16ms (Views: 11.0ms | ActiveRecord: 1.7ms)
Your regex is wrong. You're accepting only letters, whitespace is not a letter. Try something like this: \A[a-zA-Z,\s]+\z
BTW rubular is pretty cool tool, if you need to test your regexps.
http://rubular.com/
About the validation message not showing up:
If you're doing asynchronous request, you'll need to make your validation on the client side. There are some gems which can help you with this, e.g. client side validations gem.
Also, if you're using jQuery.ajax() you can use its callback to perform an action after the call (notify user of success or do whatever with DOM you want to):
$.ajax({
url: "http://some/url.com",
// rest of your ajax call
}).done(function (data) {
console.log("Your data:", data); // this will be run after the async. call
});
If you need to debug things like this try pry. Just add to your gemfile:
group :test, :development do
gem 'pry', '~> 0.9.12'
end
Run bundle install, restart server and then you can add:
binding.pry
wherever you want to stop code execution and inspect current state of your app enviroment in the console.
In your case it would be somewhere at the end of UsersController#custom method. You can then check in your terminal value of #user. Methods #user.valid? and #user.errors will tell you if your #user object is valid and show you an array with all validation errors associated with the object.
I'm currently following along with this railscast and for my specific situation am running into a Faraday timeout error on the callback from omniauth.
Currently I'm using a rails application as an API and backbone as a javascript front-end (on the same application)
I decided I wanted to lock down the API with OAuth and provided a custom strategy for Omniauth to access the API as a client as well as Doorkeeper to handle the authorization logic
module OmniAuth
module Strategies
class Twiddle < OmniAuth::Strategies::OAuth2
option :name, :twiddle
option :client_options, {
site: "http://localhost:3001",
authorize_path: "/oauth/authorize"
}
uid do
raw_info["id"]
end
info do
{
firstName: raw_info["firstName"],
lastName: raw_info["lastName"],
email: raw_info["email"]
}
end
def raw_info
#raw_info ||= access_token.get('/api/v1/user').parsed
end
end
end
end
I included the custom strategy like this:
require File.expand_path('lib/omniauth/strategies/twiddle', Rails.root)
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twiddle, id, secret # Omitting the actual ones for obvious reasons
end
I am currently using these gems in my bundle
# OAuth
gem 'oauth2'
gem 'omniauth'
gem 'omniauth-oauth2'
gem 'omniauth-facebook'
gem 'doorkeeper'
Here is where I authenticate and attempt to retrive the proper access token (and also where I get stuck)
def loginParse
if ( user = User.authenticate( params[:email], params[:password] ) )
session[:user_id] = user.id
redirect_to '/auth/twiddle/'
else
render :controller => "authentication", :action => "loginIndex", :notice => "Incorrect credentials"
end
end
Here is the routing from the routes.rb
# Oauth urls
match '/auth/twiddle/callback', to: "authentication#connectAPI"
match "/auth/facebook/callback", to: "authentication#loginSocialMedia"
The application never is able to render the connectAPI action, getting COMPLETELY stuck at this point (given by the server logs)
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Doorkeeper::Application Load (0.2ms) SELECT `oauth_applications`.* FROM `oauth_applications` WHERE `oauth_applications`.`uid` = '' LIMIT 1
CACHE (0.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Doorkeeper::AccessToken Load (0.3ms) SELECT `oauth_access_tokens`.* FROM `oauth_access_tokens` WHERE `oauth_access_tokens`.`revoked_at` IS NULL AND `oauth_access_tokens`.`application_id` = 1 AND `oauth_access_tokens`.`resource_owner_id` = 1 ORDER BY created_at desc LIMIT 1
(0.1ms) BEGIN
Doorkeeper::AccessGrant Load (0.2ms) SELECT `oauth_access_grants`.* FROM `oauth_access_grants` WHERE `oauth_access_grants`.`token` = '' LIMIT 1
SQL (1.1ms) INSERT INTO `oauth_access_grants` (`application_id`, `created_at`, `expires_in`, `redirect_uri`, `resource_owner_id`, `revoked_at`, `scopes`, `token`) VALUES (1, '2012-08-08 03:10:31', 600, 'http://localhost:3001/auth/twiddle/callback', 1, NULL, '', '')
(1.4ms) COMMIT
Redirected to http://localhost:3001/auth/twiddle/callback?code=a
Completed 302 Found in 12ms (ActiveRecord: 3.7ms)
(twiddle) Callback phase initiated.
Many of the uids/important information have been omitted from the log.
Finally this error is given:
Faraday::Error::TimeoutError (Timeout::Error):
I hope I have been thorough in my explanation of this problem.
I don't know why exactly the application seems to be freezing at the callback initiated part of omniauth. I have tried updating bundler as a few other stackoverflow questions have pointed me to but it is not working.
Perhaps my understanding of OAuth2 is a bit murky.
If anyone can help me, I would greatly appreciate it
I'm not sure this applies to you been here was my scenario:
Problem
An app with data that our internal OAuth server wants
An OAuth server with little, to no data on it
We want to offload the authentication portion of App1 to App2 without moving data
Solution
App1 uses App2 as the Authentication server
App2 uses App1 for user data
Problem from this solution
Deadlock - App1 is waiting for an OAuth response from App2, but to complete
that response App2 must wait for a response from App1.
Final observation
In development mode on Rails (with WebBrick) you can't run multi-threaded, so the request
may never be allowed to complete.
My solution
My solution was to install puma and add to
config/environments/development.rb:
if ENV["THREADS"]
config.threadsafe!
end
Then when you start the server you'd do THREADS=1 rails s Puma to test your
OAuth stuff.
OR
Or your scenario is completely different and you're actually not communicating between
your services. Is your extra_info (like Github's /user) endpoint functioning on
the OAuth server? Is your callback actually doing anything?
I hope this helps!