Authenticate user in controller and then redirect - ruby-on-rails

I have the following code under projects_controller.rb
class Admin::ProjectsController < ApplicationController
before_filter :require_login
def require_login
while (adminLogin != 'username')
redirect_to admin_login_path and return
end
end
And this under application_controller.rb
class ApplicationController < ActionController::Base
# Return the admin user
def adminLogin
#email = params[:email]
#password = params[:password]
return #email
end
end
I am trying to get the email in that form and pass it to the projects controller so when the email is defined, the admin can log in. When I press the submit button on the form I can see the right email being sent to the projects controller by using <%= debug #email %> in the form, but the page redirects to login again. How can I then go to /projects?
[UPDATE]:
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
# Return the admin user
def redirect_unless_admin
#email = params[:email]
password = params[:password]
if (#email == 'username')
redirect_to admin_projects_path
else
redirect_to admin_login_path
end
end
end
and I require this method in my projects_controller.rb. This is just breaking it, redirecting too many times

You should be able to get by with this in application_controller:
class ApplicationController < ActionController::Base
def redirect_unless_admin
email = params[:email]
password = params[:password]
if params[:email].present? && email == 'username'
redirect_to admin_projects_path
else
redirect_to root_path
end
end
end
and this in admin/projects_controller:
class Admin::ProjectsController < ApplicationController
before_filter :redirect_unless_admin
end
That way the redirect_unless_admin method is available to any controller that inherits application_controller. You can customize the logic any way you need to figure out which redirect to use, but this should be a good starting point.

If you need to call adminLogin from ProjectsController, it should be defined either in ApplicationController or in a common parent class which derives from ApplicationController.

Related

Rails and Devise: How to redirect user back to the requested Page after successful login?

I am using the idiom described in https://guides.rubyonrails.org/v2.3/action_controller_overview.html#other-ways-to-use-filters
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter do |controller|
redirect_to new_login_url unless controller.send(:logged_in?)
end
end
Now if the signing in process is successful, how
can I examine if it was, and b)
how can I re-redirect the user to the requested controller action?
How do I do this login-process via AJAX and JSON ?
EDIT: Also I get the following Error Message
uninitialized constant ApplicationController::LoginFilter
When I use the more elaborate solution suggested in 6.2 Other Ways to Use Filters instead of the one above such that my Controller looks like this
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_return_path, LoginFilter
def set_return_path
return if devise_controller?
session['user_return_to'] = request.url unless current_user
end
class LoginFilter
def self.filter(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in"
controller.redirect_to controller.new_login_url
end
end
end
end
Thanks
von Spotz
You can add a before_action in the application_controller.rb where you save the requested page url:
class ApplicationController < ActionController::Base
before_action :set_return_path
def set_return_path
return if devise_controller?
session['user_return_to'] = request.url unless current_user
end
end
And then redirect the user to this url after the successful sign-in:
class SessionsController < Devise::SessionsController
def after_sign_in_path_for(resource)
return root_url if session['user_return_to'].blank?
session['user_return_to']
end
end

undefined method `attributes' for nil:NilClass - Wicked/Devise gems

I'm trying to implement Wicked gem with Devise as I want users to go through different steps in order to complete their profiles. I'm a complete newbie so I would appreciate if you can give me a suggestion on what could be the problem.
The error I'm getting is this one and it shows when I try to continue from "Personal" to "Style" step. I guess it's a problem with saving the data:
NoMethodError in OnboardingController#update
undefined method `attributes' for nil:NilClass
**#user.attributes(user_params)**
These are my registration and onboarding controllers:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
'/onboarding/personal'
end
def after_update_path_for(resource)
registration_steps_path
end
def new
super
end
def create
super
end
def update
super
end
def update_resource(resource, params)
if resource.encrypted_password.blank? # || params[:password].blank?
resource.email = params[:email] if params[:email]
if !params[:password].blank? && params[:password] == params[:password_confirmation]
logger.info "Updating password"
resource.password = params[:password]
resource.save
end
if resource.valid?
resource.update_without_password(params)
end
else
resource.update_with_password(params)
end
end
end
and
class OnboardingController < ApplicationController
include Wicked::Wizard
steps :personal, :stylefirst
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.attributes(user_params)
render_wizard #user
end
end
With Devise, current_user is nil if no user is logged in. So your problem is that you're assigning #user = current_user on your update action without verifying a user is signed in.
If you want to ensure the update action is only available to signed in users then use the authenticate_user! helper action provided by Devise:
class OnboardingController < ApplicationController
before_filter :authenticate_user!, only: [:edit, :update]
# ...
end
The authenticate_user! helper method will redirect the user to the sign in page if they're not logged in. If the user successfully signs in, current_user will be set and they will be redirected back to the page the originally tried to access.

pundit_user: undefined method `current_user' for #<User:0x007fcefbc2b150>

I have two layouts Admin and Domain. And I don't need any extra configuration in Admin layout. but if user tries to access Domain layout they must be in their valid domain.
This means that, I need to customize all of my Domain policy to include both current_user as well as current_domain. I found this can be done with UserContext and pundit_user... so here is what I have done:
application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def pundit_user
UserContext.new(current_user, current_domain)
end
def after_sign_out_path_for(resource)
root_path
end
def current_domain
#current_domain ||= Domain.where(name: requested_domain).first
end
helper_method :current_domain
private
def requested_domain
return request.env["SERVER_NAME"]
end
def user_not_authorized
# reset_session
flash[:alert] = "You are not authorized to perform this action"
redirect_to(request.referrer || root_path)
end
end
Note that, when I access Admin layout, current_domain will be nil and if I visit any routes of Domain layout, then current_domain will set to currently accessing domain.
user_context.rb
class UserContext
attr_reader :current_user, :current_domain
def initialize(current_user, current_domain)
#current_user = current_user
#current_domain = current_domain
end
end
PROBLEM
Suppose I have this policy:
user_policy.rb
class UserPolicy < ApplicationPolicy
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def index?
binding.pry # debugging
current_user.admin? ||
current_user.domain == current_domain
end
private
def current_user
# return user.is_a?(User) ? user : user.current_user
user.current_user
end
def current_domain
# return user.is_a?(User) ? nil : user.current_domain
user.current_domain
end
end
when application runs current_user and current_domain must available in UserPolicy as per documentation(https://github.com/elabs/pundit#additional-context).
But I am getting
undefined method `current_user' for #<User:0x007fcefbc2b150>
That means, still I have user object in it, not user.current_user and user.current_domain
Please let me know, if you need further description. What am I missing here?
It was my own dumb mistake.
PROBLEM
I had a before_filter call in domain/base_controller.rb something like:
class Domain::BaseController < ApplicationController
before_action :authenticate_user!
before_action :domain_exists?
before_action :verify_domain!
private
def verify_domain!
# PROBLEM: this line was updating pundit_user again to user object
raise Pundit::NotAuthorizedError unless DomainConsolePolicy.new(current_user, current_domain).authorized?
end
def domain_exists?
if current_domain.blank?
redirect_to root_path, alert: 'Domain that you provided is not valid or is permanently removed!'
end
end
end
SOLUTION:
I have used headless policy for this because now I have both current_user and current_domain set with pundit_user in application_controller
domain/base_controller.rb
class Domain::BaseController < ApplicationController
before_action :authenticate_user!
before_action :domain_exists?
before_action :verify_domain!
private
def verify_domain!
# SOLUTION
authorize :domain_console, :has_access?
end
def domain_exists?
if current_domain.blank?
redirect_to root_path, alert: 'Domain that you provided is not valid or is permanently removed!'
end
end
end
policy/domain_console_policy.rb
class DomainConsolePolicy < Struct.new(:user, :domain_console)
def has_access?
user.current_user.admin? ||
user.current_user.domain_id == user.current_domain.id
end
end
Thanks

Couldn't find User with 'id'=true Omniauth - rails 4

I am implementing omniauth for twitter and I have run into an error "Couldn't find User with 'id'=true" the error is pointing to the application controller current_user metho. Heere is my current_user method:
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
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
and here is my User model:
class User < ActiveRecord::Base
def self.find_or_create_by_auth(auth_data)
user = where(provider: auth_data[:provider], uid: auth_data[:uid]).first_or_create
user.update(name: auth_data[:info][:name])
end
end
and finally the SessionController is below:
class SessionsController < ApplicationController
def create
#user = User.find_or_create_by_auth(request.env["omniauth.auth"])
session[:user_id] = #user
redirect_to products_path, notice: "logged in as "
end
def destroy
session[:user_id] = nil
redirect_to root_path, notice: "Goodbye!!"
end
end
When I trying to log in the error pops and i can't get past login to load my index page.
I think your problem should be solved with following modification in User.find_or_create_by_auth method:
def self.find_or_create_by_auth(auth_data)
# all previous code
# you should return user from here
# your current code returned true of false
user
end
Also you should save #user.id in session, not full #user object:
session[:user_id] = #user.id
I finally solved this, the issue was that i had deleted the previous authenticated twitter user in the db and i was trying to authenticate with the same credentials again on the app.
so what i did is create a new twitter app and use different keys to authenticate into my rails App... hoep this explains it thanks

Code in each controller method

I have a controller and every method of it starts with the following code:
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
return
end
I'm just wondering if it is possible to avoid code duplication in this case ?
Yes, use a before_filter
class YourController < ApplicationController
before_filter :check_user
def check_user
..
end
end
Absolutely.
class MyController < ApplicationController
before_filter :ensure_logged_in
# actions here.
def ensure_logged_in
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
end
end
end
You shouldn't need to worry about the 'return', as rails will bail out of the filter pipeline once the redirect happens.
To avoid duplication you just need to add before_filter in every controller where you want to check user authentication.
class SomeController < ApplicationController
before_filter :authenticate_user
end
then add your user authentication logic in application controller something like this,
class ApplicationController < ActionController::Base
private
def current_user
#current_user ||= UserData.find_by_login(session[:cuser]) if session[:cuser]
end
helper_method :current_user
def authenticate_user
redirect_to({:controller=> 'user_data', :action=> 'login'}, :alert => "Not authorized") if current_user.nil?
end
end
You can use current_user helper method in every controller to get current user.
Try to use before filter. This should be fine

Resources