Rails/Devise google_oauth2 to TWO google workspaces - ruby

My company just merged with another company
I have an online webapp.
Users at my company login with devise SSO google_oauth2
I need to allow users from the new company get access through their business units entirely separate google account.
How do I do this?
In devise.rb
I have tried:
config.omniauth :google_oauth2, c1[:client_id], c1[:client_secret]
config.omniauth :google_oauth2, c2[:client_id], c2[:client_secret]
It will only recognize users from one account
I then tried extending the google outh strategy into a new strategy
config.omniauth :google_oauth2, c1[:client_id], c1[:client_secret]
config.omniauth :google_oauth2_c2, c2[:client_id], c2[:client_secret]
I created a class to support it and added to devise.rb with require_relative. I also added to the User model as
devise :database_authenticatable, :registerable,
:recoverable, :rememberable,
:omniauthable, omniauth_providers: [:google_oauth2, :google_oauth2_c2]
I also added a second button to the HTML template
<form class="button_to" method="post" action="/user/auth/google_oauth2">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<%= image_submit_tag "/signin.png", width: 184, alt: "Sign in with Google", data: { turbo: false } %>
</form>
<form class="button_to" method="post" action="/user/auth/google_oauth2_c2">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<%= image_submit_tag "/signin.png", width: 184, alt: "Sign in with Google", data: { turbo: false } %>
</form>
This gives me:
Could not authenticate you from GoogleOauth2 because "Csrf detected". on the original company. (I am creating a distinct per_form_csrf_token)
and
Not found. Authentication passthru. on the latter
The latter error seems to be because devise cannot find the route.
Any help truly appreciated

Related

simple-form not looking up i18n translations

simple-form is simply not looking up i18n translations. I was trying it on the placeholders.
simple_form.en.yml
en:
simple_form:
placeholders:
company:
name: "Company name"
The form:
<%= simple_form_for #company,
url: company_path(#company) do |f| %>
<div class="form-group">
<%= f.text_field :name, class: "form-control" %>
</div>
I've debugged using i18n-debug, and it didn't seem to attempt to look up
en.simple_form.placeholders.company.name
at all. Other non simple-form i18n lookups are triggering just fine.
Did I miss a step to 'turn on' i18n for simple-form? I thought it works right out of the box.
Looks like you are using text_field - a Rails form helper. Simple form supports it but it is not implemented in simple form (i.e. falls back to Rails). Change it to f.input :name and it should work.

Calling RSpec sleep

I'm writing a feature test right now that is trying test an edit action. It's a bit strange to me because it's not seeing what I expect when I run RSpec normally but when I use save_and_open_page command it shows that what I'm expecting is actually there in the browser. I think this is a selenium issue but I'm new to rspec and I'm not sure. I've been suggested to use "Sleep" or "Pause" but I'm not sure how to use these methods and I can't find any good docs on it. I'm wondering if someone can show me a valid way to use this on my current code?
TEST
require "rails_helper"
RSpec.feature "Edit 'Bounce Back' Message", :type => :feature do
given!(:group) { Group.create!(name: "Group A", response: "You are now subscribed for updates") }
scenario "Staff can see the response messages" do
visit "/groups"
user = FactoryGirl.create(:user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
expect(page).to have_content("You are now subscribed for updates")
expect(page).to have_content("Group a")
end
scenario "Staff can edit the response messages" do
visit "/groups/#{group.id}/edit"
user = FactoryGirl.create(:user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
expect(page).to have_content("You have now subscribed for updates")
end
end
I hope this is enough info please let me know if you need more. Thank You!
I've also tried using rspec_wait but it still is sending an error. here it is
<div class="container text-center">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 well">
<%= form_for #group do |form| %>
<div class="form-group">
<%= form.label :body, "Edit The Response:", class: "message_label"%>
<%= form.text_field :response, class: "form-control", placeholder: "New things are happening!" %>
</div>
<%= form.submit "Update", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
The currently accepted answer for this question is wrong. There is no need to use rspec-wait with Capybara matchers since they already have waiting/retrying behavior built in. You can adjust how long they wait/rety for using Capybara.default_max_wait_time, or passing a wait: <time in seconds> option to the matcher.
From reading the question and the attached image I assume the "Staff can see the response messages" test is passing but the "Staff can edit the response messages" test fails. If so it's most likely because on the edit page the "You have now subscribed for updates" string is actually the content of a field (textbox etc) rather than text content of the page. have_content is used for checking for text node content on the page, to test for a field with a given value you would use something like
expect(page).to have_field('Response', with: 'You have now subscribed for updates')
That assumes there is a label associated with the field with text of "Response", if not you can pass the id of the field, name, etc. or as of Capybara 2.7 you can pass nil and it will just look for any field with the given value - all depends exactly what you are needing to test for.
expect(page).to have_field(nil, with: 'You have now subscribed for updates')

Rails 4.1.1 Devise auth signup and update account buttons don't work

I'm developing an application that uses the devise gem to provide authentication for the user model. A while ago I decided to implement an Admin class also using devise, instead of putting an admin:boolean onto the existing user model. After I did this, for some reason the standard user sign up and update account buttons stopped working.
The only thing I changed on the standard devise user forms was to add a link to 'admin login' on the sign in view (which still worked) to the devise admin sign in page. Both the sign in pages worked.
Now here's the thing, both the sign up and update account buttons worked after a page refresh.
I have since removed the Admin class and resorted to using a boolean on the User model, but the problem persists. Additionally, the problem is present when I run locally in dev mode, on a staging production environment on heroku and on our AWS Ubuntu testing instance.
I have been pulling my hair out for like three weeks over this, has anyone experienced a similar error?
Help. Please.
Using:
Rails 4.1.1
Devise 3.2.4
twitter-bootstrap-rails 2.2.8 (not using bootstrap themes on devise views though)
Code:
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :username %><br />
<%= f.text_field :username, :autofocus => true %></div>
<div><%= f.label :firstname %><br />
<%= f.text_field :fname %></div>
<div><%= f.label :lastname %><br />
<%= f.text_field :lname %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :mobile %><br />
<%= f.number_field :mobile %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<p>You must read and accept My Company's <%= link_to "Terms and Conditions", '/home/terms' %> to register.</p>
<%= f.label :tos_acceptance, "I accept" %>
<%= check_box( :user, :tos_acceptance, {}, true, false )%></div><br>
<div><%= f.submit "Sign up" %></div>
<%= render "devise/shared/links" %>
<% end %>
NB: I'm certain this is a view error. I've also inspected the elements using chrome console, there is apparently no difference between the non refreshed button (doesn't work) and the refreshed button (which works). :/
Fixed this, looks like the standard devise views were conflicting with bootstrap3 (or something).
Using the gem 'rails_layout' and running
$ rails generate layout:devise bootstrap3
rebuilt the view files, all I needed to do was add my custom fields back in and it worked perfectly.

Devise with multiple models, bootstrap modal with ajax login

I have 2 kinds of users - nurses and patients, and want to setup login such that there are two buttons on the welcome/landing page which open up a bootstrap modal with the login form for each type of user. I'd like the login form to send an AJAX request so that if there are errors, they are displayed in the modal itself.
I'm using Devise for authentication, and have setup 2 models for nurses and patients. Initially, I setup the modal to load the url for the new_nurse_session_path on clicking the button, modified the default devise login form to send AJAX requests, used a custom SessionsController to handle new sessions and send JSON replies back, and then have JS code which catches the reply. This worked (though with some issues as below) was pretty slow since it was loading the entire page from nurses/sessions/new.html.erb along with the header, navbar, etc.
Questions
While the JS code was catching the AJAX request when the entire sign in page was opened in the modal (as per code below), if I opened the page directly, the AJAX request wasn't processed even though the server was sending the correct JSON back - is there something I'm doing wrong here that might be causing this problem?
To avoid the entire new session page from being loaded in the modal, I followed the instructions here https://github.com/plataformatec/devise/wiki/How-To:-Display-a-custom-sign_in-form-anywhere-in-your-app to put the form in the modal. The issue I'm facing here is how to create the helper so that it defines the resource and Devise mappings according to whether it's a nurse or patient trying to login. If I create separate custom forms and modals for each one, I face the same issue with handling AJAX requests as in Q1. Would really appreciate help on what I might be doing wrong here.
If I'm going down a rabbit hole here, would really appreciate suggestions on implementing the above functionality in a better way! Thanks!
Code used
Login form, nurses/sessions/new.html.erb:
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name),
:html => {:id => "sign_in_nurse"}, :format => :json,
:remote => true) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<% if devise_mapping.rememberable? -%>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<div><%= f.submit "Sign in" %></div>
<% end %>
<%= render "nurses/shared/links" %>
Coffeescript to handle AJAX reply, nurses.js.coffee:
$("form#sign_in_nurse").bind "ajax:success", (e, data, status, xhr) ->
if data.success
window.location.replace(data.redirect)
else
alert('failure!')
SessionsController:
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "sessions#failure")
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render :json => {:success => false, :errors => ["Login failed."]}
end
end
Code to generate modal:
<li><%= link_to image_tag('i_am_nurse.png', :size => "130x130"),
new_nurse_session_path , :data => { :target => "#ajax-modal", :toggle => "modal"} %></li>
Bootstrap modal div:
<div id="ajax-modal" class="modal hide fade" tabindex="-1">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<div class="modal-body-content"></div>
<div class="ajax-loader"></div>
</div>
<div class='modal-footer'>
<button type="button" data-dismiss="modal" class="btn">Close</button>
</div>
</div>​

How to make password change form?

I am using has_secure_password method, how to make change password form and controller action?
I think it must look like:
<%= form_for #parent, :url => update_password_path(#parent) do |f| %>
<%= f.label :old_password %><br />
<%= f.text_field :old_password %><br />
<%= f.label :new_password %><br />
<%= f.text_field :password %><br />
<%= f.label :password_confirmation %><br />
<%= f.text_field :password_confirmation %><br />
<%= f.submit %>
<% end %>
I'm still not sure how to do this the 'correct' by the standards way, but yeah, you would want a form like that. The controller action would check if the old password is valid by doing
user = User.find(session[:user_id]) if session[:user_id]
if user and user.authenticate(params[:old_password])
if params[:password] == params[:password_confirmation]
user.password_confirmation = BCrypt::Password.create(params[:password])
respond_to do |format|
format.html # reset_password_success.html.erb
end
else
redirect_to reset_password_url, notice: "Incorrect Password."
end
else
redirect_to reset_password_url, notice: "Incorrect Password."
end
Make sure you filter the passwords, just like they're filtered when a new user registers.
I don't know if your form will work or not. I find that if I'm stuck on how to create a form the right way, or in a hurry, I just hack it together using regular old HTML or use it as a crutch. Decide this based on your time constraints and past experience.
If the form is working correctly, but there is an error in handling the request, make sure that you really do access the params using params[:old_password] and such. It might be nested inside of another params object like params[:user][:old_password]. You can find out how the parameters are being sent by looking at your rails console, or by looking at the logs. If this is hosted on Heroku, you'll need to ask Heroku for it's logs => https://devcenter.heroku.com/articles/logging
And you can also figure it out by listening to network traffic (dev tools on major browsers or using Fiddler => http://www.fiddler2.com/fiddler2/)
This code is assuming that you're going off of the Agile Web Development with Rails (4th ed.) book. If not, then some adjustments will have to be made.

Resources