Trying to get "current user" in Rails - ruby-on-rails

In the /views/layouts directory, how do I get the current user? I am using Devise, but current_user does not work here for some reason, it describes it as an unknown method. I want to do something like:
<% if User.role? == "gen_admin" %>
<li>
<%= link_to('Admin', users ) %>
</li>
<% end %>
I do have a role? method defined in my User model, but I still get this exception:
undefined method 'role?' for #<Class:0x3fcc1e0>
So how can I get the current user, and access its fields at this level of the source tree? Thanks!
Here is the roles? method:
# in User
ROLES = %w[gen_admin teacher_admin student]
def role?(base_role)
ROLES.index(base_role.to_s) <= ROLES.index(role)
end

Your code must account for when users are logged in, and when they are not logged in.
If no user is logged in, then current_user will return nil (as in your case, which you thought was an error on Devise's part).
Your view code must handle this - eg.
<% if current_user.present? && current_user.role?('gen_admin') %>

You have defined your role? method as an instance method. This means that in order to use it you should first create the instance from User class e.g.:
#user=User.new
Now you can call #user.role?
If you want role? to be available through the model User then you should define it as class method an pass in an object for verification
def self.role?(user)
...
end

You'll need to change the code in your view to use current_user rather than User, as well as call the role? method correctly (no ==). If this is used in a view that can be accessed by a non-logged in user, you'll want to confirm that the user is logged in first (so you don't try and call role? on nil, which would raise an exception):
<% if current_user && current_user.role?("gen_admin") %>
<li>
<%= link_to('Admin', users ) %>
</li>
<% end %>

Do you forget to add before_filter :authenticate_user! in your controller? This will ensure that current_user is available in your views

Related

Ruby No Method Error on a Model for Current User when Logged Out

I'm building a rails app that has a lot of things that change based on whether or not the user has completed certain quizzes. Everything works fine when a user is logged in, but I just tried logging out and I got a NoMethodErroron this line of code:
<% if current_user.per_quiz.nil? %>
Basically, if the user has completed per_quiz they go to one page (the edit view), if they haven't they go to a different page (the new view). The only thing that changed was the fact that I logged out, so I can only assume that is what caused the error. Do I need to add some kind of if statement to account for a state in which no user is logged in? How should I fix this error in accordance with Ruby best practices?
You just need to check to see if current_user is set before checking per_quiz. You can handle checking for the login state easily in a view by querying current_user.nil?:
<% if !current_user %>
<p> You must be logged in to do anything useful. Please go login</p>
<% elsif !current_user.per_quiz %>
<p>Cool quiz stuff goes here...</p>
<% else %>
What you probably really want is to have a logged out user go elsewhere, such as the home page or signin page. To do that, you need to do a couple of simple things to your controller. I'm going to assume that the controller is called 'QuizzesController' since you hadn't included your controller code in the question.
Here's how to do it:
class QuizzesController < ApplicationController
# Other devise authentication logic goes here...
before_action :authorize_user!
# Your actions go here...
private
def authorize_user!
if !current_user
redirect_to '/', notice: "You must be logged in to access to this page"
end
end
end
What this does is install a "before_action" handler that will check that the user is logged in before letting them do anything in this controller. If they are not logged in, you can redirect them wherever you wish.
Note that sometimes, only certain actions need this kind of treatment. In that case, you can use an :only or :except option to specify which action(s) are or are not handled. It looks like this:
before_action :authorize_user!, only: [ :new, :edit, :create ]
or like this:
before_action :authorize_user!, except: :list
This will give you greater flexibility in managing the authorization part of the equation, where devise handles the authentication part.
That's because current_user is nil when no user logged in. And if you will cal some method on nil it will throw NoMethodError.
I assume that have code you provided in the view. So, you can check if user is logged in.
<% if user_signed_in? %>
# do smth with user quizzes
<% else %>
# do something else
<% end %>
But the best way is to use before filter in the controller and not allow to anonymous to see that page.
Here is the example application with Rails and devise. https://github.com/RailsApps/rails-devise
Hope that helps.
I'm not sure if this is the "correct" Ruby way to do this, but the way I eventually found was to change it to an if/elsif/else statement:
<% if current_user.nil? %>
...
<% elsif current_user.bal_quiz.nil? %>
...
<% else %>
...
<% end %>
Probably should have figured this out before I posted the question, but I was (and still am) interested to see if there's a better or "more Ruby" way of doing this.

creating rails method for checking object if exists

I am using Devise for authentication in my rails app and I have a _header partial in my layout folder for navbar. I wanna put there a link for Create Profile (user model is created w/ devise, user has_one profile and profile belongs_to user) but only if the user profile doesn't exist yet.
I would like to create a method for this and put the if statement into the view but I can't figure it out where to create the method and how it would look like.
The basic devise method works fine when it comes to checking if user is signed in. I want a similar method that can check if user profile exists.
layout/_header.html.erb
<% if user_signed_in? %>
<% if user.profile(current_user) %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>
So my questions:
Where to put the method (helper/controller/model/appcontroller/etc.)?
How the method would look like?
You can define it in your Helper files (app/helpers/). You can use the application_helper but for a better consistency we will name this file users_helper:
# app/helpers/users_helper.rb
module UsersHelper
def user_has_profile?(user = current_user)
return false unless user.present?
Profile.where(user_id: user.try(:id) || user).exists?
end
end
and use it like this:
# any view
<% if user_signed_in? && !user_has_profile? %>
I would put this in the helpers directory(app/helpers/application_helper.rb) as a method called has_profile?
the method would look like
def has_profile?
current_user.profile.present?
end
then in your view:
<% if user_signed_in? && has_profile? %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>

How to allow Admin user to access customer user objects rails

I have a report object that belongs_to a customer(user_type). I have it setup so that the customer is able to grant or remove permission from an admin to see the customers report. I need to know what steps or direction do I need to take to get the reports viewable to the admin user. The admin user should only have the ability to see reports (show) and see an index of reports that they have access to.
I am thinking maybe I should create a new controller but I am not sure if that is correct or how to do another controller tied to an object that already has a controller.
I highly suggest looking into the CanCanCan gem (the continuation of the CanCan gem which is no longer supported). In this way your code would look something like this in the end (you didn't post any code, so I have to make assumptions about your variables and such):
<% if current_user.admin? %>
<% if can? :view_report, #customer %>
<!-- Render the report to this admin -->
<!-- ............................. -->
<% end %>
<% end %>
And then you would have something like this in your controller to give an admin permission to view the user's report:
def handle_report
can [:show], Report, :user_id => user.id
if current_user.admin?
can :manage, :current_report
end
end
Again, apologies for guessing the variables.

Rails - Using different views for anonymous vs. logged in users: Bad idea?

My admin accounts are user accounts with a simple boolean set to true. Works fine for now, I can control functionality with if statements in the views, example:
<td><% if current_user.admin? || current_user == user %>
<%= link_to "Edit", edit_user_path(user) %>
<% end %></td>
Some resources are off limits to anonymous users, and they get redirected to the login page if they try and select those links. But other resources (like a list of all the articles on the site), I want both those with a session and those without to see. The problem of course is that a user with no session will throw an error, because there is no current_user if you don't have a session. So I decided to divide up the world into the 2 parts. When you hit the index.html.erb for 'articles', this is all that in there:
<% if current_user == nil %>
<%= render "anonindex" %>
<% else %>
<%= render "authindex" %>
<% end %>
My question is, am I making a long term design mistake by doing this? Am I eventually going to need to implement a roles based system, or is it feasible to differentiate user privileges based on boolean operators, and keep the users with no session in a completely separate sandbox? Its working great thus far, but I worry I'm going down a path that will require a total rebuild later.
You don't actually have to check this thing in views. You can check this thing in Controller, and can take the appropriate out there:
class YourController < ApplicationController
before_action :check_user_logged_in, only: [:index, :your_desired_method]
end
And then in check_user_logged_in method, you can see if a user is logged in, send him to the desired place, otherwise redirect him to the log in page.
def check_user_logged_in
redirect_to log_in_path unless current_user
end

how to save HTTP_Basic_Authenticate parameters to a user model

I have a basic application that I want user to be able to access only if they have an access_token I have given them. In this example lets say the token is '131313'
Users without the access_token can only access the index page...
In my User model I have the following columns
name:
access_token:
items:
In my controller I have:
http_basic_authenticate_with name: "??not sure???", access_token: "131313", except: :index
I want verification that the right access token is passed '131313' and then to check the name provided by the user and list all of the items associated to that user.
i.e get redirected to a page that has the following
#user.each do |user|
user.item
I am guessing I will need a controller with something like:
#user = User.find(params[:name])
To set my user instance variable for my view and find them by the name provided. What I am having trouble here is understanding if its possible to do this with HTTP_Basic_Authentication. Is there a way to fetch the values provided(i.e 'name')and compare(i.e .find call) them to the database or even add them to the database(i.e .save call)?
Or would I have to roll out a full authentication system?
You add something like the following to your application_controller.rb:
before_action :authenticate, except: :index
private
attr_reader :current_user
helper_method :current_user
def authenticate
authenticate_or_request_with_http_basic('MyApplication') do |name, token|
#current_user = User.find_by(name: name)
current_user && current_user.access_token == token
end
end
That current_user method returns always the user currently logged in. That means you can that write something like the following in the view without loading an #user again:
<%= current_user.name %>
<% current_user.items.each do |item| %>
<%= item.name %>
<% end %>

Resources