Mocking google with omniauth in rails integration test - ruby-on-rails

I have a sample project, where user can login with their google account, Now i want to write a test to verify that user has successfully logged in with his google account. But i don't know how to verify this thing in my test file ?
This is what i have tried now but its not working
OmniAuth.config.mock_auth[:google] = OmniAuth::AuthHash.new({
:provider => 'google',
:uid => '1337',
:info => {
'name' => 'JonnieHallman',
'email' => 'jon#test.com'
}
request.env["devise.mapping"] = Devise.mappings[:user]
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google]
I am supposing that after this my page content would change but they are same as before

You can follow the guide on how to do integration tests with Omniauth https://github.com/intridea/omniauth/wiki/Integration-Testing
basically you'll have something like this in your spec/rails_helper.rb
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:xing] = OmniAuth::AuthHash.new({
:provider => 'google',
:uid => '123545',
:info => {
:name => "Test",
:email => "test#test.com"
},
:credentials => {
:token => "token",
:secret => "secret"
}
# etc.
})
And then have a login_helper that does something like
def login
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:google]
visit root_path
click_link 'loginBtn'
end

Related

Why ldap bind is failing on server?

I'm authenticating against LDAP server in my rails application,
the code below is working locally but not on the server.
On the server it throws Net::LDAP::BindingInformationInvalidError (Invalid binding information) when trying to login in the app but works through the console
I'm pretty new to Ruby and can't figure out the proper way to debug it... I know the LDAP configuration is right because i can authenticate and bind from the console or on my local development environment.. I tried to pass :verbose => true to the LDAP constructor but without effect...
require 'net/ldap'
require 'devise/strategies/authenticatable'
module Devise
module Strategies
class LdapAuthenticatable < Authenticatable
def authenticate!
if params[:user]
ldap = Net::LDAP.new :host => 'XX.XX.XX.XX',
:port => 636,
:connect_timeout => 5,
:base => 'CN=Configuration,DC=internal,DC=XX,DC=XX',
:encryption => {
:method => :simple_tls
},
:auth => {
:method => :simple,
:username => ENV['LDAP_USER'],
:password => ENV['LDAP_PASSWORD']
}
result = ldap.bind_as(:base => "OU=Users,OU=XX,DC=XX,DC=XX,DC=XX",
:filter => "(userPrincipalName=#{email})",
:password => password,
)
if result
user = User.find_by(email: email)
success!(user)
else
return fail(:invalid_login)
end
end
end
def email
params[:user][:email]
end
def password
params[:user][:password]
end
end
end
end
Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)
SOLVED
turned out it was the ENV variables that were not read.
Maybe that account is not authorized? Sounds like the problem is in the binding configuration: base => "OU=Users,OU=XX,DC=XX,DC=XX,DC=XX"
More information from other users who encountered this error:
https://gitlab.com/gitlab-org/gitlab-ce/issues/21937
LDAP groups authentication fails: Invalid Binding Information

Oauth minitest error, callback link giving an error

I am trying to write tests for OmniAuth users and after setting up my test_helper, I am running into and a bad URI error.Sharing the details below:
test_helper.rb
# OmniAuth auth mock for testing
def setup_omniauth_mock (user)
OmniAuth.config.test_mode = true
OmniAuth::AuthHash.new ({
'provider' => 'google',
'uid' => '123545',
'user_id' => '2',
'first_name' => 'X',
'last_name' => 'XYZ',
'email' => 'xxyz#example.com',
'image' => 'https://lh3.googleusercontent.com//photo.jpg',
'oauth_token' => 'abcdef12345',
'oauth_expires_at' => DateTime.now,
})
OmniAuth.config.add_mock(:google, OmniAuth::AuthHash.new)
get '/auth/":google"/callback'
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:google]
get '/auth/:google/callback'
end
The error I am getting:
test_validating_a_Google_OAuth_user#SessionsControllerTest (0.49s)
URI::InvalidURIError: URI::InvalidURIError: bad
URI(is not URI?): http://www.example.com:80/auth/":google"/callback
test/test_helper.rb:42:in `setup_omniauth_mock'
Now I followed the documentation here [Oauth Integration Testing][1]
[1]: https://github.com/omniauth/omniauth/wiki/Integration-Testing but I think there is something I am doing wrong.
Can someone please help me guide through this.
Thank you!
J.
I actually resolved it by cleaning things a bit.
My test_helper.rb now:
# OmniAuth auth mock setup for testing
setup do
OmniAuth.config.test_mode = true
Rails.application.env_config["omniauth.auth"] =
OmniAuth.config.mock_auth[:google]
end
#teardown OmniAuth mock setup
teardown do
OmniAuth.config.test_mode = false
end
#Google OAuth mock
def google_oauth2_mock (user)
OmniAuth.config.mock_auth[:google]
OmniAuth::AuthHash.new ({
'provider' => 'google_oauth2',
'uid' => '123545',
'user_id' => '2',
'first_name' => 'X',
'last_name' => 'XXYZ',
'email' => 'xxyzjam#example.com',
'image' => 'https://lh3.googleusercontent.com/photo.jpg',
'oauth_token' => 'abcdef12345',
'refresh_token' => '12345abcdef',
'oauth_expires_at' => DateTime.now,
})
end
I put the routes in the individual tests, and that allowed me to run the tests suite smoothly.
Hope I am able to save you some time and frustrations.

Omniauth Facebook doesnt return the extra data

I tried with everything that show up in google or here but doesn't work
its just show me the basic info (name,id,photo)
here s my code for omniauth.rb:
provider :facebook, 'key', 'key', {:scope => 'email',:info_fields => 'email', :client_options => {:ssl => {:ca_file => Rails.root.join("cacert.pem").to_s}}}
and this is how i call it:
auth = request.env["omniauth.auth"]
session[:omniauth] = auth

Getting (omniauth-facebook) and (omniauth-twitter) work

I'm using:
Ruby on Rails 4
devise 3.0.3
omniauth (1.1.4)
omniauth-facebook (1.4.1)
omniauth-twitter (1.0.0)
I recently set up my omniauth-facebook and everything works fine. Now i want to add omniauth-twitter but somehow i mess things up, pretty bad.
1.) To set up my Omniauth-Facebook i did this (in a nutshell):
gem 'omniauth'
gem 'omniauth-facebook'
2.) Added the columns "provider" and "uid" to my User model.
3.) Next, i declared the provider in my config/initializers/devise.rb:
require "omniauth-facebook"
config.omniauth :facebook, "App_ID", "App_Secret",
{:scope => 'email,offline_access',
:client_options => {:ssl => {:ca_file => 'lib/assets/cacert.pem'}},
:strategy_class => OmniAuth::Strategies::Facebook}
4.) I edited my Model User.rb
# Facebook Settings
def self.find_for_facebook_oauth(auth, signed_in_resource = nil)
user = User.where(provider: auth.provider, uid: auth.uid).first
if user.present?
user
else
user = User.create(first_name:auth.extra.raw_info.first_name,
last_name:auth.extra.raw_info.last_name,
facebook_link:auth.extra.raw_info.link,
user_name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20])
end
end
and added the attributes to devise:
:omniauth_providers => [:facebook]
5.) I edited the routes:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
THE END
Although this worked perfectly for Facebook, i tried for hours now to get this working for Twitter, and i just cant figure it out.
If someone, who has experience in this, or just knows the solution could help me set this up, i would be very thankful :)
Thank you guys, and sorry for the long Post.
In Addition
Twitter does not provide an :email Attribute so i have to Split up my User Registration Process i guess ?
My Twitter action in my User Model
# Twitter Settings
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth[:provider], :uid => auth[:uid]).first
unless user
user = User.create(:first_name => auth[:name],
:user_name => auth[:screen_name],
:provider => auth[:provider], :uid => auth[:uid],
:password => Devise.friendly_token[0,20]
)
end
user
end
# build auth cookie hash for twitter
def self.build_twitter_auth_cookie_hash data
{
:provider => data.provider, :uid => data.uid.to_i,
:access_token => data.credentials.token, :access_secret => data.credentials.secret,
:first_name => data.name, :user_name => data.screen_name,
}
end
I had to migrate a confirmable for Users -> How To: Add :confirmable to Users
My Form's Problem, (At Least im getting to this poing now :) )
To fix your problem with the email you could just set a dummy mail, or add a second step where the user adds his/her email.
Dummy mail:
class User < ActiveRecord::Base
before_create :set_dummy_mail, if self.provider == "twitter"
private
def set_dummy_mail
self.email = "#{self.name}_email#example.com"
end
end
Or the second step option:
Modify your controller to redirect to an add email step if the provider is twitter and the email is blank. Maybe you also have to modify your validations to allow email blank on create if the provider is twitter.
UPDATE: I did it like following:
Gemfile:
gem "devise"
gem "omniauth"
gem "omniauth-facebook"
gem "omniauth-twitter"
I used:
devise version 2.2.3
omniauth 1.1.4
omniauth-facebook 1.3.0
omniauth-twitter 0.0.17
If you are using different versions, you maybe must change a few things..
routes.rb:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
devise_scope :user do
post "account/create" => "users/accounts#create"
end
app/models/user.rb
class User < ActiveRecord::Base
# allow email blank for first create
validates_format_of :email, :with => Devise.email_regexp, :allow_blank => true, :if => :email_changed?
# facebook find method
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(:first_name => auth.extra.raw_info.first_name,
:last_name => auth.extra.raw_info.last_name,
:facebook_link => auth.extra.raw_info.link,
:user_name => auth.extra.raw_info.name
:provider => auth.provider,
:uid => auth.uid, :email => auth.info.email,
:password => Devise.friendly_token[0,20]
)
user.confirm!
end
user
end
# twitter find method
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth[:provider], :uid => auth[:uid]).first
unless user
user = User.create(:first_name => auth[:first_name], :user_name => auth[:user_name],
:provider => auth[:provider], :uid => auth[:uid],
:password => Devise.friendly_token[0,20]
)
end
user
end
# build auth cookie hash for twitter
def self.build_twitter_auth_cookie_hash data
{
:provider => data.provider, :uid => data.uid.to_i,
:access_token => data.credentials.token, :access_secret => data.credentials.secret,
:first_name => data.screen_name, :user_name => data.name,
}
end
end
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# callback action for facebook auth
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
# callback action for twitter auth
def twitter
data = session["devise.omniauth_data"] = User.build_twitter_auth_cookie_hash(request.env["omniauth.auth"])
user = User.find_for_twitter_oauth(data)
if user.confirmed? # already registered, login automatically
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect user, :event => :authentication
elsif !user.email?
flash[:error] = "You must add an email to complete your registration."
#user = user
render :add_email
else
flash[:notice] = "Please confirm your email first to continue."
redirect_to new_user_confirmation_path
end
end
end
app/views/users/omniauth_callbacks/add_email.html.erb
<%= form_for(#user, :as => "user", :url => account_create_path, :html => {:class => "form-inline"}) do |f| %>
<%= f.email_field :email, :placeholder => User.human_attribute_name(:email), :class => "input-medium" %>
<%= f.submit "Finish registration", :class => "btn btn-small" %>
<% end %>
app/controllers/users/accounts_controller.rb
class Users::AccountsController < ApplicationController
def create
data = session["devise.omniauth_data"]
data[:email] = params[:user][:email]
user = User.find_for_twitter_oauth(data)
user.email = data[:email]
if user.save
flash[:notice] = I18n.t "devise.registrations.signed_up_but_unconfirmed"
redirect_to root_path
else
flash[:error] = I18n.t "devise.omniauth_callbacks.failure", :kind => data[:provider].titleize, :reason => user.errors.full_messages.first
render "users/omniauth_callbacks/add_email"
end
end
end
Maybe you have to modify the one or other part of my solution..you also could refactor the two methods in the user model (find_for_facebook_auth, find_for_twitter_auth) to work with one dynamic method. Try it out and let me know, if you still have problems. If you find any typo, please also let me know.. Also you should write tests to check everything within your system.
Add this in gem file
gem 'omniauth-twitter'
do bundle and restart the server
after this add your app_id and secret key in config/initializer/devise.rb
require "omniauth-twitter"
config.omniauth :twitter, "app_id", "secret_key"
edit your user model
:omniauth_providers => [:facebook,:twitter]
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
Add new controller file in
app/controller/user/omniauth_callbacks_controller.rb
def twitter
#user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
else
session["devise.twitter_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
Add this link in your view file
<%= link_to 'sign in with twitter', user_omniauth_authorize_path(:twitter) %>
Changes required in user model as twitter do not return user's email id:
Create a migration file to allow null values in user's email column:
class ChangeEmailToNullInUser < ActiveRecord::Migration
def up
change_column :users, :email, :string, :null=>true
end
def down
end
end
after this you also need to override user model validations so add this in user.rb
def email_required?
false
end
Note: Before performing this you should create your app on twitter and give proper callback path. Its is important as after authentication from twitter the controller would come back to the path which you specify in your app on twitter.
let me know if you have any problem.
Mattherick has a good solution for the email but I couldn't get the before_create to work. A callback doesn't play nice with conditional if statements because anything after the comma is meant to be an options hash. Therefore:
before_save :set_dummy_email, if self.provider == "twitter"
Popped me errors.
This how I fixed this:
before_save :set_dummy_email
def set_dummy_email
self.email ||= "#{self.name}-CHANGEME#example.com"
end
This will only set an email if one is not given by the provider (ie: Twitter).
And then for a more 'universal' way of setting attributes (so you don't need a unique strategy:
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.name = auth.info.name || auth.info.nickname
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email if auth.info.email
user.save
end
end

How to login with declarative_authorization in my rspec tests without going through the interface

At present, the following works when I call it from my Rspec tests:
def login_as(role)
#role = Role.create! :name => role
#virginia = User.create!(
:username => "Virginia",
:password => "password",
:password_confirmation => "password",
:email => "example#example.com")
#assignment = Assignment.create! user_id: #virginia.id, role_id: #role.id
visit login_path
fill_in "user_session_username", :with => #virginia.username
fill_in "user_session_password", :with => #virginia.password
click_on "submit_user_session"
end
Given that I've tested the interface already, I thought it might speed things up to not hit the interface hundreds of times just because many of my examples only make sense when a user has a role and is logged in. So, I tried this:
def login_as(role)
#role = Role.create! :name => role
#virginia = User.create!(
:username => "Virginia",
:password => "password",
:password_confirmation => "password",
:email => "example#example.com")
#assignment = Assignment.create! user_id: #virginia.id, role_id: #role.id
#user_session = UserSession.create! :username => #virginia.username, :password => #virginia.password
end
However, that version is not logging me in. Any thoughts?
To be clear, I'm not asking how to test declarative_authorization. I'm asking how to quickly login when I'm testing something else. For example, I use login_as(role) to test product#delete (an action only accessible to admins) like this:
login_as "admin"
visit product_path(#search)
click_link "delete_product"
page.current_path.should == products_path
page.should_not have_content "Retail Site Search"

Resources