I am trying to implement Omniauth with Devise in Rails API with NuxtJS framework.
I did auth module connexion and user account creation with Omniauth method but i would like understand how redirect the user afer signin/signup, i am Rails developer and beginner with NuxtJS.
BACKEND
User model oauth registration method:
def self.from_facebook(auth)
where(uid: auth.uid, provider: auth.provider).first_or_create do |user|
user.email = auth.info.email
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.password = Devise.friendly_token[0, 20]
user.provider = auth.provider
user.uid = auth.uid
Client.create(user: user)
end
end
Registration controller:
# frozen_string_literal: true
module Overrides
class RegistrationsController < DeviseTokenAuth::ApplicationController
before_action :set_user_by_token, only: [:destroy, :update]
before_action :validate_sign_up_params, only: :create
before_action :validate_account_update_params, only: :update
skip_after_action :update_auth_header, only: [:create, :destroy]
def create
build_resource
unless #resource.present?
raise DeviseTokenAuth::Errors::NoResourceDefinedError,
"#{self.class.name} #build_resource does not define #resource,"\
' execution stopped.'
end
# give redirect value from params priority
#redirect_url = params.fetch(
:confirm_success_url,
DeviseTokenAuth.default_confirm_success_url
)
# success redirect url is required
if confirmable_enabled? && !#redirect_url
return render_create_error_missing_confirm_success_url
end
# if whitelist is set, validate redirect_url against whitelist
return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?
# override email confirmation, must be sent manually from ctrl
resource_class.set_callback('create', :after, :send_on_create_confirmation_instructions)
resource_class.skip_callback('create', :after, :send_on_create_confirmation_instructions)
if #resource.respond_to? :skip_confirmation_notification!
# Fix duplicate e-mails by disabling Devise confirmation e-mail
#resource.skip_confirmation_notification!
end
if #resource.save
if params[:farmer]
Farmer.create(
user: #resource
)
else
Client.create(
user: #resource
)
end
yield #resource if block_given?
unless #resource.confirmed?
# user will require email authentication
#resource.send_confirmation_instructions({
client_config: params[:config_name],
redirect_url: #redirect_url
})
end
if active_for_authentication?
# email auth has been bypassed, authenticate user
#client_id, #token = #resource.create_token
#resource.save!
update_auth_header
end
render_create_success
else
clean_up_passwords #resource
render_create_error
end
end
def update
if #resource
if #resource.send(resource_update_method, account_update_params)
yield #resource if block_given?
render_update_success
else
render_update_error
end
else
render_update_error_user_not_found
end
end
def destroy
if #resource
#resource.destroy
yield #resource if block_given?
render_destroy_success
else
render_destroy_error
end
end
def sign_up_params
params.permit(
:first_name,
:last_name,
:email,
:cellphone,
:phone,
:password,
:password_confirmation,
:birthdate
)
end
def account_update_params
params.permit(*params_for_resource(:account_update))
end
protected
def build_resource
#resource = resource_class.new(sign_up_params)
#resource.provider = provider
# honor devise configuration for case_insensitive_keys
if resource_class.case_insensitive_keys.include?(:email)
#resource.email = sign_up_params[:email].try(:downcase)
else
#resource.email = sign_up_params[:email]
end
end
def render_create_error_missing_confirm_success_url
response = {
status: 'error',
data: resource_data
}
message = I18n.t('devise_token_auth.registrations.missing_confirm_success_url')
render_error(422, message, response)
end
def render_create_error_redirect_url_not_allowed
response = {
status: 'error',
data: resource_data
}
message = I18n.t('devise_token_auth.registrations.redirect_url_not_allowed', redirect_url: #redirect_url)
render_error(422, message, response)
end
def render_create_success
render json: {
status: 'success',
data: resource_data
}
end
def render_create_error
render json: {
status: 'error',
data: resource_data,
errors: resource_errors
}, status: 422
end
def render_update_success
render json: {
status: 'success',
data: resource_data
}
end
def render_update_error
render json: {
status: 'error',
errors: resource_errors
}, status: 422
end
def render_update_error_user_not_found
render_error(404, I18n.t('devise_token_auth.registrations.user_not_found'), status: 'error')
end
def render_destroy_success
render json: {
status: 'success',
message: I18n.t('devise_token_auth.registrations.account_with_uid_destroyed', uid: #resource.uid)
}
end
def render_destroy_error
render_error(404, I18n.t('devise_token_auth.registrations.account_to_destroy_not_found'), status: 'error')
end
private
def resource_update_method
if DeviseTokenAuth.check_current_password_before_update == :attributes
'update_with_password'
elsif DeviseTokenAuth.check_current_password_before_update == :password && account_update_params.key?(:password)
'update_with_password'
elsif account_update_params.key?(:current_password)
'update_with_password'
else
'update_attributes'
end
end
def validate_sign_up_params
validate_post_data sign_up_params, I18n.t('errors.messages.validate_sign_up_params')
end
def validate_account_update_params
validate_post_data account_update_params, I18n.t('errors.messages.validate_account_update_params')
end
def validate_post_data which, message
render_error(:unprocessable_entity, message, status: 'error') if which.empty?
end
def active_for_authentication?
!#resource.respond_to?(:active_for_authentication?) || #resource.active_for_authentication?
end
end
end
Omniauth callbacks controller:
def facebook
#user = User.from_facebook(request.env["omniauth.auth"])
# NOTE: redirection here
end
FRONTEND
Stategie:
facebook: {
client_id: 'CLIENT_ID',
userinfo_endpoint: 'https://graph.facebook.com/v2.12/me?fields=about,name,picture{url},email,birthday',
redirect_uri:'http://localhost:3000/omniauth/facebook',
scope: ['public_profile', 'email', 'user_birthday']
}
Login method:
facebookLogin () {
this.$auth.loginWith('facebook')
.then((response) => {
this.$toast.success({
title: 'Connexion réussie',
message: 'Vous vous êtes bien connecté.',
position: 'bottom center',
timeOut: 3000
})
})
.catch(() => {
this.$toast.error({
title: 'Erreur',
message: 'L\'email ou le mot de passe ne sont pas valides. Vérifiez votre saisie.',
position: 'bottom center',
timeOut: 8000
})
})
.finally(() => this.$wait.end('signing in'))
}
A couple of things...
Omniauth callbacks controller is missing the redirect information (is that why that note is there?). If you're using Devise, it should say something like sign_in_and_redirect #user underneath the #user = ... line.
Devise comes with built in routes. To use them, you must include something like, devise for :users in your routes.rb file. Check out the "Devise_for magic" section on this page to see an example of these built in routes. Note that you have to have some Devise models configured for this to work.
Run rake routes to see if the routes you have defined are what you're expecting.
If you can't figure it out, I also created a project using Omniauth and devise. You can view my code here.
Related
I've been stuck for days and searching but I cannot find a correct solution to logout from a devise session using JWT. I had a front made with react and everything works fine on login and searching, but when I logout the page if I don't make a refresh I can't login. I leave the code from devise session controller along side the application controller, route and my middeware build to use redux with my front (I'm working with React to). Thanks in advance and I you need something else, let me know.
Devise::SessionsController
# frozen_string_literal: true
class Api::SessionsController < Devise::SessionsController
respond_to :json, :html
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
# def create
# super
# end
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
private
def revoke_token(token)
# Decode JWT to get jti and exp values.
begin
secret = Rails.application.credentials.jwt_secret
jti = JWT.decode(token, secret, true, algorithm: 'HS256', verify_jti: true)[0]['jti']
exp = JWT.decode(token, secret, true, algorithm: 'HS256')[0]['exp']
user = User.find(JWT.decode(token, secret, true, algorithm: 'HS256')[0]['sub'])
sign_out user
# Add record to blacklist.
time_now = Time.zone.now.to_s.split(" UTC")[0]
sql_blacklist_jwt = "INSERT INTO jwt_blacklist (jti, exp, created_at, updated_at) VALUES ('#{ jti }', '#{ Time.at(exp) }', '#{time_now}', '#{time_now}');"
ActiveRecord::Base.connection.execute(sql_blacklist_jwt)
rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
head :unauthorized
end
end
def respond_with(resource, _opts = {})
render json: resource
end
def respond_to_on_destroy
token = request.headers['Authorization'].split("Bearer ")[1]
revoke_token(token)
request.delete_header('Authorization')
render json: :ok
end
end
ApplicationController
class ApplicationController < ActionController::API
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user
protected
def configure_permitted_parameters
added_attrs = %i[username email password password_confirmation remember_me]
devise_parameter_sanitizer.permit(:sign_up, keys: added_attrs)
devise_parameter_sanitizer.permit(:account_update, keys: added_attrs)
end
private
def authenticate_user
if request.headers['Authorization'].present?
token = request.headers['Authorization'].split("Bearer ")[1]
begin
jwt_payload = JWT.decode(token, Rails.application.credentials.jwt_secret).first
#current_user_id = jwt_payload['sub']
rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
head :unauthorized
end
end
end
def authenticate_user!(options = {})
head :unauthorized unless signed_in?
end
def current_user
#current_user ||= super || User.find(#current_user_id)
end
def signed_in?
#current_user_id.present?
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, skip: %i[registrations sessions passwords]
namespace :api do
devise_scope :user do
post 'signup', to: 'registrations#create'
post 'login', to: 'sessions#create'
delete 'logout', to: 'sessions#destroy'
get 'login', to: 'sessions#create'
end
resources :notes
resources :searches
get 'get_places', to: 'searches#get_places'
end
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
middleware.js
import * as constants from './constants';
import axios from 'axios';
import { logoutUser } from './actions/authActionCreators'
export const apiMiddleware = ({ dispatch, getState }) => next => action => {
if (action.type !== constants.API) return next(action);
dispatch({ type: constants.TOGGLE_LOADER });
const BASE_URL = 'http://localhost:3001';
const AUTH_TOKEN = getState().user.token;
if (AUTH_TOKEN)
axios.defaults.headers.common['Authorization'] = `Bearer ${AUTH_TOKEN}`;
const { url, method, success, data, postProcessSuccess, postProcessError } = action.payload;
console.log('AUTH_TOKEN '+AUTH_TOKEN);
console.log('url '+url);
axios({
method,
url: BASE_URL + url,
data: data ? data : null,
headers: {
'Content-Type': 'application/json', 'Accept': '*/*'
}
}).then((response) => {
dispatch({ type: constants.TOGGLE_LOADER });
if (success) dispatch(success(response));
if (postProcessSuccess) postProcessSuccess(response);
}).catch(error => {
dispatch({ type: constants.TOGGLE_LOADER });
if (typeof(error.response) === "undefined") {
console.warn(error);
postProcessError('An error has ocurred');
} else {
if (error.response && error.response.status === 403)
dispatch(logoutUser());
if (error.response.data.message) {
if (postProcessError) postProcessError(error.reponse.data.message);
}
}
})
};
I am building a React - Rails API application. Following a tutorial, I intentionally did not instantiate Rails as an API, rather it's the full framework. So, according to the the tutorial giver, I should have access to session and not have to download any gems.
Problem: When I refresh the page the session should keep the user logged in. It is not. Instead of returning with: { logged_in: true, user: {...} }, it is returning with { logged_in: false } (the else conditional in session's logged_in method). The #current_user from the concern file is returning as nil, more specifically, its session[:user_id] is returning nil, so the conditional is failing.
I'm out of ideas, I've been messing with this for over an hour and still no luck. If anything else is needed please let me know.
Rails: Sessions Controller
class SessionsController < ApplicationController
include CurrentUserConcern
def create
user = User.find_by(email: params["user"]["email"])
.try(:authenticate, params["user"]["password"])
if user
session[:user_id] = user.id
// binding.pry shows session[:user_id] = 4 (or whatever the id is)
render json: {
status: :created,
logged_in: true,
user: user
}
else
render json: { status: 401 }
end
end
// Method in question:
def logged_in
// #current_user == nil
if #current_user
render json: {
logged_in: true,
user: #current_user
}
else
render json: {
logged_in: false
}
end
end
def logout
reset_session
render json: { status: 200, logged_out: true }
end
end
Rails: controllers/concerns/current_user_concern.rb
module CurrentUserConcern
extend ActiveSupport::Concern
included do
before_action :set_current_user
end
def set_current_user
if session[:user_id]
// binding.pry shows session[:user_id] = nil
#current_user = User.find_by(id: session[:user_id])
end
end
end
React: App.js
// Method that is interacting with Rails
checkLoginStatus = () => {
axios
.get('http://localhost:3001/logged_in', { withCredentials: true })
.then((response) => {
// Does not enter the conditions RESPONSE = data.logged_in: false
if (response.data.logged_in && this.state.loggedInStatus === 'NOT_LOGGED_IN') {
this.setState({ loggedInStatus: 'LOGGED_IN', user: response.data.user });
} else if (!response.data.logged_in && this.state.loggedInStatus === 'LOGGED_IN') {
this.setState({ loggedInStatus: 'NOT_LOGGED_IN', user: {} });
}
})
.catch((error) => console.log('hello error.', error));
};
you need to add the following config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore to config/application.rb
I have a rails backend api application integrated with auth0 service that only verifies validity of auth_token received from frontend application. After securing all backend api endpoints all my tests fail with a result "Not Authenticated", which is how it should be. However I cannot figure out how to get through the authentication and to not require it for rspec tests. Here are my classes:
projects_controller_spec.rb
require "rails_helper"
RSpec.describe Api::V1::ProjectsController, :type => :controller do
describe 'GET /api/v1/organizations/1/projects' do
let!(:organization) { create(:organization_with_projects) }
before { get :index, params: { organization_id: organization } }
context 'when authorized' do
it 'should return JSON objects' do
expect(json['projects'].count).to equal(3)
end
it { expect(response).to have_http_status(:ok) }
it { expect(response.content_type).to include('application/json') }
end
describe 'POST /api/v1/organizations/1/projects' do
let!(:organization) { create(:organization) }
let(:project) { organization.projects.first }
before { post :create, params: { organization_id: organization, project: attributes_for(:project) } }
context 'when authorized' do
it { expect(response).to have_http_status(:created) }
it { expect(response.content_type).to include("application/json") }
it { expect(json).to eq(serialized(project)) }
end
end
end
application_controller.rb
class ApplicationController < ActionController::API
include Pundit
include Secured
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
private
def record_not_found(error)
render json: { error: error.message }, status: :not_found
end
end
concerns/secured.rb
module Secured
extend ActiveSupport::Concern
included do
before_action :authenticate_request!
end
private
def authenticate_request!
# Create user if not existing
pundit_user
auth_token
rescue JWT::VerificationError, JWT::DecodeError
render json: { errors: ['Not Authenticated'] }, status: :unauthorized
end
def http_token
if request.headers['Authorization'].present?
request.headers['Authorization'].split(' ').last
end
end
def auth_token
JsonWebToken.verify(http_token)
end
def pundit_user
User.create_from_token_payload({token: auth_token[0], organization_id:
request.parameters['organization_id']})
end
end
lib/json_web_token.rb
require 'net/http'
require 'uri'
class JsonWebToken
def self.verify(token)
JWT.decode(token, nil,
true, # Verify the signature of this token
algorithm: 'RS256',
iss: 'https://xxx.auth0.com/',
verify_iss: true,
aud: Rails.application.secrets.auth0_api_audience,
verify_aud: true) do |header|
jwks_hash[header['kid']]
end
end
def self.jwks_hash
jwks_raw = Net::HTTP.get URI("https://xxx.auth0.com/.well-known/jwks.json")
jwks_keys = Array(JSON.parse(jwks_raw)['keys'])
Hash[
jwks_keys
.map do |k|
[
k['kid'],
OpenSSL::X509::Certificate.new(
Base64.decode64(k['x5c'].first)
).public_key
]
end
]
end
end
It looks like I found a solution by adding the following line into every controller spec file:
before { allow(controller).to receive(:authenticate_request!).and_return(true) }
My application has three different user types (i.e. Devise models): SuperAdminUser, AdminUser and StandardUser. Every user, regardless of type, has a preferences page where they will be able to update a number of attributes (e.g. :email, :password, :username, etc.), with two of the attributes requiring the user to enter their current password to update (:email, :password), while the remaining attributes will not require the current password to update.
Now, in my ideal world, the preference updates will utilize AJAX so that only the attribute changed will refresh in the page rather than an entire page reload. Currently I have this working for all of the attributes that don't require the user to enter their current password, but I've been unable to get it to work with :email and :password. Oddly enough, I have it working in a previous prototype, but that had only one Devise user model, not three, and that single model didn't have it's own registration controller overriding the Devise registration controller, which the three user models in the current app do, although I don't see anything in these overriding registration controllers that would be impacting the issue here.
The only error message I'm getting is my own, on the preferences page:
Sorry, but we were unable to update your password.
The problem appears to be that the :current_password param value is not being captured from the form. The console log statement shows it as a blank string, while the :new_password and :new_password_confirmation values are captured without problem. I haven't been able to determine why this is happening. As an alternate approach, I also tried to mimic as closely as I could the change password example in the Devise wiki (the last example) (here: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password), but to no avail. Any suggestions would be greatly appreciated.
Additionally, I guess an important question to ask is if should I be using AJAX with the password attribute. That is, is it inherently insecure passing these password values via AJAX? If so, is there a way to do it securely (e.g. encrypted)?
Here is my relevant code:
app/controllers/super_admin_users/preferences_controller.rb
class SuperAdminUsers::PreferencesController < ApplicationController
include ApplicationHelper
before_action :authenticate_super_admin_user!
.
.
.
def change_password
#super_admin_user = SuperAdminUser.find_by(id: current_super_admin_user.id)
if resource.update_with_password(params.permit(:password,
:password_confirmation,
:current_password))
sign_in resource_name, self.resource, bypass: true
status_message = 'Your password has been updated successfully!'
else
status_message = 'Sorry, but we were unable to update your password.'
end
respond_to do |format|
format.json { render json: { status_message: status_message } }
end
end
def password_section_partial
#change_password_status_message = params[:change_password_status_message] || ""
render 'super_admin_users/preferences/password/password_section_partial'
end
.
.
.
end
end
app/views/super_admin_users/preferences/password/_change_password_form.html.haml
= form_tag super_admin_user_prefs_change_password_path, id: 'prefs-change-password-form' do
.prefs-label-and-input-wrapper
#prefs-new-password-label-wrapper.prefs-input-label-wrapper
= label_tag 'password', 'New Password', id: 'prefs-new-password-label',
class: 'prefs-input-label'
#prefs-new-password-input-wrapper.prefs-input-wrapper
= password_field_tag 'password', nil, autofocus: true,
autocomplete: 'off',
id: 'prefs-new-password-input',
class: 'prefs-input'
.prefs-label-and-input-wrapper
#prefs-new-password-confirmation-label-wrapper.prefs-input-label-wrapper
= label_tag 'password_confirmation', 'New Password Confirmation', id: 'prefs-new-confirmation-password-label',
class: 'prefs-input-label'
#prefs-new-password-confirmation-input-wrapper.prefs-input-wrapper
= password_field_tag 'password_confirmation', nil, autocomplete: 'off',
id: 'prefs-new-password-confirmation-input',
class: 'prefs-input'
.prefs-label-and-input-wrapper
#prefs-current-password-label-wrapper.prefs-input-label-wrapper
= label_tag 'current_password', 'Current Password', id: 'prefs-current-password-label',
class: 'prefs-input-label'
#prefs-current-password-input-wrapper.prefs-input-wrapper
= password_field_tag 'current_password', nil, autocomplete: 'off',
id: 'prefs-current-password-input',
class: 'prefs-input'
#prefs-change-password-form-buttons-wrapper
#prefs-change-password-form-submit-btn-wrapper
= link_to 'Update', 'javascript:;', id: 'prefs-change-password-form-submit-btn', class: 'btn btn-sm btn-success btn-submit'
-#%input{ id: 'prefs-change-password-form-submit-btn', class: 'btn btn-sm btn-success', type: 'submit', value: 'Update' }
#prefs-change-password-form-cancel-btn-wrapper
= link_to 'Cancel', 'javascript:;', id: 'prefs-change-password-form-cancel-btn', class: 'btn btn-sm btn-secondary btn-cancel'
app/views/super_admin_users/preferences/password/_change_password_status_message.html.haml
#prefs-change-password-status-message.prefs-status-message= change_password_status_message
app/views/super_admin_users/preferences/password/_password_section.html.haml
#prefs-password-section-header.prefs-section-header
#prefs-password-headline-wrapper
.prefs-section-headline Password
#prefs-password-edit-btn-wrapper
= link_to 'Edit', 'javascript:;', id: 'prefs-password-edit-btn', class: 'btn btn-sm btn-primary btn-edit'
#prefs-change-password-form-wrapper.prefs-form-wrapper.no-display
= render partial: 'super_admin_users/preferences/password/change_password_form'
#prefs-change-password-status-message-wrapper.prefs-status-message-wrapper.no-display
app/views/super_admin_users/preferences/password/password_section_partial.js.haml
$('#prefs-change-password-status-message-wrapper').empty();
$('#prefs-change-password-form-wrapper').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
- if #change_password_status_message.present?
$('#prefs-change-password-status-message-wrapper').append("#{ escape_javascript(render(partial: 'super_admin_users/preferences/password/change_password_status_message', locals: { change_password_status_message: #change_password_status_message })) }");
$('#prefs-change-password-status-message-wrapper').removeClass('no-display');
$('#prefs-password-edit-btn').removeClass('no-display');
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include ApplicationHelper
include DeviseParamSanitizerOverrides
def get_current_user_type
respond_to do |format|
format.json { render json: { current_user_type: resource_name.to_s } }
end
end
def get_current_environment
respond_to do |format|
format.json { render json: { current_environment: Rails.env } }
end
end
protected
def devise_parameter_sanitizer
if resource_class.to_s == 'SuperAdminUser'
SuperAdminUser::ParameterSanitizer.new(SuperAdminUser, :super_admin_user, params)
elsif resource_class.to_s == 'AdminUser'
AdminUser::ParameterSanitizer.new(AdminUser, :admin_user, params)
elsif resource_class.to_s == 'StandardUser'
StandardUser::ParameterSanitizer.new(StandardUser, :standard_user, params)
end
end
end
app/controllers/concerns/devise_param_sanitizer_overrides.rb
module DeviseParamSanitizerOverrides
extend ActiveSupport::Concern
class SuperAdminUser::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
permit(:sign_up, keys: [:email, :first_name, :last_name, :username])
permit(:sign_in, keys: [:email, :username])
permit(:account_update, keys: [:current_password, :email, :first_name, :last_name, :time_zone, :username])
end
end
.
.
.
end
app/helpers/application_helper.rb
module ApplicationHelper
def resource
if super_admin_user_signed_in?
#super_admin_user ||= SuperAdminUser.new
elsif admin_user_signed_in?
#admin_user ||= AdminUser.new
elsif standard_user_signed_in?
#standard_user ||= StandardUser.new
end
end
def resource_name
if super_admin_user_signed_in?
:super_admin_user
elsif admin_user_signed_in?
:admin_user
elsif standard_user_signed_in?
:standard_user
end
end
def resource_class
if super_admin_user_signed_in?
SuperAdminUser
elsif admin_user_signed_in?
AdminUser
elsif standard_user_signed_in?
StandardUser
end
end
def devise_mapping
if super_admin_user_signed_in?
#devise_mapping ||= Devise.mappings[:super_admin_user]
elsif admin_user_signed_in?
#devise_mapping ||= Devise.mappings[:admin_user]
elsif standard_user_signed_in?
#devise_mapping ||= Devise.mappings[:standard_user]
end
end
def resource_authenticated_root
if super_admin_user_signed_in?
authenticated_super_admin_user_root
elsif admin_user_signed_in?
authenticated_admin_user_root
elsif standard_user_signed_in?
authenticated_standard_user_root
end
end
end
app/assets/javascripts/preferences.js
var currentUserType;
var currentEnvironment;
getCurrentUserType('/get_current_user_type'
).done(function(getCurrentUserTypeResponse) {
currentUserType = getCurrentUserTypeResponse.current_user_type;
});
getCurrentEnvironment('/get_current_environment'
).done(function(getCurrentEnvironmentResponse) {
currentEnvironment = getCurrentEnvironmentResponse.current_environment;
});
$(document).ready(function() {
$('#prefs-password-edit-btn').click(function (e) {
$('#prefs-password-edit-btn').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
$('#prefs-change-password-form-wrapper').removeClass('no-display');
});
$(function() {
return $('body').on('click', '#prefs-change-password-form-submit-btn', function() {
$('#prefs-change-password-form-wrapper').addClass('no-display');
changePassword('/' + currentUserType + 's/preferences/change_password?password=' + $('#prefs-new-password-input').val() +
'&password_confirmation=' + $('#prefs-new-password-confirmation-input').val() +
'¤t_password=' + $('#prefs-current-password-input').val()
).done(function(changePasswordResponse) {
$.ajax({url: '/' + currentUserType + 's/preferences/password_section_partial?change_password_status_message=' + changePasswordResponse.status_message});
});
});
});
$(function() {
return $('body').on('click', '#prefs-change-password-form-cancel-btn', function() {
$('#prefs-change-password-form-wrapper').addClass('no-display');
$('#prefs-change-password-form').trigger('reset');
$('#prefs-password-edit-btn').removeClass('no-display');
});
});
function getCurrentUserType(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
if (currentEnvironment === 'development') {
alert('AJAX Get Current User Type Error');
}
})
}
function getCurrentEnvironment(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
alert('AJAX Get Current Environment Error');
})
}
function changePassword(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
if (currentEnvironment === 'development') {
alert('AJAX Change Password Error');
}
})
}
config/routes.rb
Rails.application.routes.draw do
get '/get_current_user_type', to: 'application#get_current_user_type'
get '/get_current_environment', to: 'application#get_current_environment'
.
.
.
devise_for :super_admin_users, controllers: { registrations: 'super_admin_users/registrations' }
authenticated :super_admin_user do
root to: 'super_admin_users/dashboard#index', as: 'authenticated_super_admin_user_root'
end
as :super_admin_user do
get 'super_admin_users/preferences/password_section_partial', to: 'super_admin_users/preferences#password_section_partial',
as: :super_admin_user_password_section_partial
get 'super_admin_users/preferences/change_password', to: 'super_admin_users/preferences#change_password',
as: :super_admin_user_prefs_change_password
end
end
I am new to writing test cases and I cant figure out the scenarios of writing tests. For example there are too many if else conditions in controller how would I write cases for these conditions. Below is my registration controller. I am using rails minitest framework for rails 3.2.1 version.
def create
invitation_token = params["invitation_token"]
#Check if the user exists yet based on their e-mail address.
user = User.find_by_email(params[:user][:email])
omni = session[:omniauth] || params[:omniauth]
theme_id = nil
theme_layout_id = nil
theme_style_id = nil
begin
omni = JSON.parse omni if omni
rescue => e
# if we got here, the omni is invalid!!
return redirect_to '/login'
end
#Did we find a user yet? If not, perform the following.
if user.nil? && !invitation_token.present?
client = Client.find_or_create_by_name(name: params[:user][:username])
#p client.errors
if client.present?
user = User.new
app_url = ApplicationUrl.find_by_domain_url(request.host_with_port)
user.apply_omniauth(omni)
user.email = params[:user][:email]
user.username = params[:user][:username]
user.client_id = client.id
#Assign the user/client the Free plan by default.
plan = ClientPlan.find_or_create_by_client_id(client_id: client.id, plan_id: 1, plan_billing_cycle_id: 1, start_date: Date.today, is_paid: 1, isactive: 1)
#Set the client settings to the defaults for a Free (non business plan) user.
ClientSetting.create(client_id: client.id, is_billboard_enabled: 0, is_tweetback_enabled: 0, is_conversations_enabled: 0)
#Set the client environment link.
ClientsEnvironment.create(environment_id: environment.id, client_id: client.id)
unless params[:user][:theme_id].nil?
theme_id = params[:user][:theme_id]
puts "theme id: " + theme_id.to_s
end
unless params[:user][:theme_layout_id].nil?
theme_layout_id = params[:user][:theme_layout_id]
puts "theme layout id: " + theme_layout_id.to_s
end
unless params[:user][:theme_style_id].nil?
theme_style_id = params[:user][:theme_style_id]
puts "theme style id: " + theme_style_id.to_s
end
#Create an application for the client.
Application.find_or_create_by_client_id(
client_id: client.id,
name: params[:user][:username],
callback_url: "#{request.host_with_port}",
application_url_id: app_url.id
)
#Create the default feed for the client.
Feed.find_or_create_by_client_id(
client_id: client.id,
name: 'My Feed',
token: SecureRandom.uuid,
theme_id: theme_id,
theme_style_id: theme_style_id,
theme_layout_id: theme_layout_id
)
if user.save
#Remember me?
if params[:remember_me]
user.remember_me!
end
client = user.client
client.update_attribute(:owner_user_id, user.id)
schedule_reminder_email(user)
#Create the users Profile
Profile.find_or_create_by_user_id(
user_id: user.id,
fullname: params[:user][:fullname],
username: params[:user][:username]
)
record_event_profile(user,params[:user][:fullname],params[:remember_me])
end
end
elsif user.nil? && invitation_token.present?
user = User.new
invite = Invite.find_by_token(invitation_token)
if invite.present?
client = invite.client
user.apply_omniauth(omni)
user.email = params[:user][:email]
user.username = params[:user][:username]
user.client_id = client.id
user.can_curate = false
user.can_publish = false
if user.save
#Remember me?
if params[:remember_me]
user.remember_me!
end
#Create the users Profile
Profile.find_or_create_by_user_id(
user_id: user.id,
fullname: params[:user][:fullname],
username: params[:user][:username]
)
record_event_profile(user,params[:user][:fullname],params[:remember_me])
invite.update_attributes({invite_accepted_at: Time.now, name: user.profile.try(:fullname)})
end
else
return redirect_to root_path
end
else
#If a user already exists for the email address then this must just be a new social network account for this user.
token = omni['credentials']['token']
token_secret = ""
user.relatednoise_authentications.create!(
provider: omni['provider'],
uid: omni['uid'],
token: token,
token_secret: token_secret
) if user.present?
end
#Create an entry in Socialnetworkaccounts for this user to associate them to their social login/account.
create_sna(omni, user)
#SignupNotifier.init_notify(user).deliver
begin
ApiConnector.new("#{API_URL}/notify_us/#{user.id}")
rescue => e
Airbrake.notify_or_ignore(e, {})
end
unless user.new_record?
session[:omniauth] = nil
session[:omniauth_auth] = nil
#reset_invite_token
end
session[:user_id] = user.id
record_event_signup(user)
back_if_coming_from_wix(params[:wix_appid], user)
sign_in_and_redirect user if !params[:wix_appid].present?
end
so far i have written this. Not sure if this is the way to write test cases.
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
fixtures :users
def setup
#params = {"omniauth"=>"{\"provider\":\"twitter\",\"uid\":\"167003011\",\"credentials\":{\"token\":\"167003011-ZqnlBsCZlFjymanQ6gQ2ggD7a2tAESuUVlygw0WN\",\"secret\":\"idVWQgR79HOKmZfuNtVtxvzWzGH5plJlxdEksxyuHgH5S\"}}","user"=>{"fullname"=>"Tommy", "email"=>"Tom#moody.com", "username"=>"tommy", "theme_id"=>"", "theme_style_id"=>"", "theme_layout_id"=>""}}
#invite = invites(:arvind_invite)
end
def test_new
get :new
assert_response :success
end
def test_create_for_client_plan
assert_difference ->{ ClientPlan.count }, +1 do
post :create, #params
end
end
def test_create_for_client_setting
assert_difference ->{ ClientSetting.count }, +1 do
post :create, #params
end
end
def test_create_for_client_environment
assert_difference -> {ClientsEnvironment.count}, +1 do
post :create, #params
end
end
def test_create_for_application
assert_difference -> {Application.count}, +1 do
post :create, #params
end
end
def test_create_for_user
assert_difference -> {User.count}, +1 do
post :create, #params
end
end
def test_create_for_feed
assert_difference -> {Feed.count}, +1 do
post :create, #params
end
end
def test_create_for_profile
assert_difference -> {Profile.count}, +1 do
post :create, #params
end
end
def test_create_for_sna
assert_difference -> {Socialnetworkaccount.count}, +1 do
post :create, #params
end
end
def test_create_for_user_with_invitation
assert_difference -> {User.count}, +1 do
post :create, #params.merge({invitation_token: #invite.token})
end
end
end
This is my test helper file.
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
include Devise::TestHelpers
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
def host_with_port
#request.host_with_port = "localhost:3000"
end
# Add more helper methods to be used by all tests here...
end