Have different view based on type of user - ruby-on-rails

I have a user model that has a few different types, i.e. there is a user attribute that is set as either a usertype1, usertype2, or usertype3. How can I route the user to a different page based on the user type? I am using devise for authentication.

If you want to redirect the user after logging in then check this devise wiki. The specific code is
# app/controllers/application_controller.rb
def after_sign_in_path_for(resource)
if usertype1?
some_url
elsif usertype2?
another_url
elsif usertype3?
some_other_url
end
end
But if what you're looking for is more generic then that you probably want to define roles and give users access to certain parts of the site based on their roles - also known as authorization. For that I'd recommend using cancan. You can also watch the railscast episode if you want more info.
Edit:
See also this so question

Related

ActiveAdmin - Allow non-logged-in users to view but not edit resources

I have a Rails app that I'm using to display database records. Until I have the time/knowledge/resources to create a home-brewed interface for viewing the database records (with pagination and advanced search/sort functionality), I've opted to settle for ActiveAdmin to handle these tasks for me.
The database needs to be publicly viewable, and I really don't want to force casual users to create user accounts, so the obvious choice seems to be to disable authentication altogether. However, I only want for admin users (i.e.; me - not unregistered users) to be able to edit the database records, preferably through the ActiveAdmin interface.
Is there an easy way to accomplish this (disable create/edit/delete for unregistered users but allow them for admins)?
ActiveAdmin lets you customise its permissions by providing a custom AuthorizationAdapter. This has an authorized? method that determines whether a user can perform an action. Here's an AuthorizationAdapter should allow logged-in admins to do anything, but others can only read data:
class AdminOnlyEditAdapter < ActiveAdmin::AuthorizationAdapter
def authorized?(action, subject = nil)
:read == action || (user && user.admin?)
end
end
Then configure ActiveAdmin to use your new class in config/initializers/active_admin.rb:
config.authorization_adapter = "AdminOnlyEditAdapter"

Cancan : User can see its backoffice show view

(Using rails 4, Cancan 1.6.10)
Hello all,
I have two UsersController: one for the front, the other under /backoffice for the back, and both are using the same User model.
I defined abilities for a "normal user" like this :
can(:manage, User) do |u|
u == user
end
whereas an admin user will be able to manage all users:
can :manage, User
I also have two "show" views, one in the front: /users/:id and one in the backoffice: /backoffice/users/:id
My problem is that a limited user can today see its profile through the backoffice URL, as he "can Manage this user". (And of course this is not acceptable)
I know this should not be too difficult to correct, but what solution would you use?
There seems to be a way to namespace the abilities. See the Wiki entry of the continued project CanCanCan here: https://github.com/CanCanCommunity/cancancan/wiki/Authorization-for-Namespaced-Controllers
What it basically does is override the Ability class to pass a namespace that is determined by the ApplicationController. The Ability class has two different rulesets depending on the namespace it is initialized with.

Using Devise to auto login a user on a multi tenancy site with subdomains

In my app a customer registers on mysite.com. Once registration is complete, they are given a site such as customer.mysite.com. I'm using Devise and would like to log the customer into their site immediately. I'm using multitenancy as explained in the RailsCast here. I'm not quite sure how to go about this. The standard solution of adding sign_in to an after_sign_up_path_for def isn't working. I'm assuming it's trying to log the customer into mysite.com, not customer.mysite.com. I'm including my after_sign_up_path_for def so you can see what I'm trying with no success. The resource in my Devise implementation is User and a user has a Site.
RegistrationsController:
def after_sign_up_path_for(resource)
# Site.current_id = resource.site.id
sign_in resource, bypass: true
"http://#{resource.site.host}.#{ENV['BASE_HOST']}/sites/#{resource.site.id}/edit"
# edit_site_url
end
Any help is appreciated.
I had the same issue and solved it the following way:
Create a new model (I called it LoginKey) that contains the user_id and a random SHA1 key.
When the user is authenticated at the parent domain (for example: mydomain.com/users/sign_in), a new LoginKey is created and the user is redirected to the corresponding subdomain to an action that I called login_with_key (for example: user_subdomain.mydomain.com/users/login_with_key?key=f6bb001ca50709efb22ba9b897d928086cb5d755322a3278f69be4d4daf54bbb)
Automatically log the user in with the key provided:
key = LoginKey.find_by_login_key(params[:key])
sign_in(key.user) unless key.nil?
Destroy the key:
key.destroy
I didn't like this solution 100%, I tried out a lot of different approaches that do not require a db record to be created, but always faced security concerns, and I think this one is safe.

Setting Pundit role for user from Devise Registrations New View / Controller

I have both Pundit and Devise setup and working correctly in my rails app. However I am unsure about how to let the user decide their role when signing up.
At the moment:
I have a URL param which is passed to the Devise new view.
In the form_for I set a hidden field called role to the value of the param.
This works.
But I am concerned that a malicious user could change this param to say "Admin" and now they are an admin.
How should I handle this? I don't want to put a restriction in the model as that will cause issues when I want to create an admin. Should I override the devise registrations controller to put a check in there?
You don't need to override Devise's RegistrationsController for what you're trying to do.
If you want admins to be able to create users that have an arbitrary role set, you could simply use your own controller. Devise still makes it easy to create a user yourself, so you'll just have to make a controller handling this. Of course, don't forget to protect it using Pundit so only admins can use this functionality.
This approach still works if you use the Confirmable module. As no confirmation e-mail will be sent on user creation, though, you'll either have to call user.confirm! after saving the model to immediately unlock the account, or manually send the confirmation e-mail using user.send_confirmation_instructions.
Edit:
This Pundit policy may or may not work for what you're trying to do. You will have to override the create action of Devise's RegistrationsController here in order to use Pundit's authorize method. For dryness' sake, you should also move the roles list elsewhere, perhaps into the model.
class UserPolicy < Struct.new(:current_user, :target_user)
def create?
registration_roles.include?(target_user.role) if current_user.nil?
end
private
def registration_roles
%w(RED BLU Spectator)
end
end
After a fair amount of googling I have an answer. First stick some validation in your model for the roles Active Record Validations Guide: See 2.6 inclusion: validator option
After this your roles are validated to ensure they are correct, you could of course have a lookup table as well. Then you have two options:
Use a conditional before_save Callback for new records. Then check if the new record has the role your protecting and if so raise an error. To catch later (in an overridden devise controller (see second option).
Override the Devise registrations controller See this SO question. And put some checks in a completely overridden create action. Use the session to store the url param passed to the new action (also needs to be completely overridden). Then if the create action fails and redirects to new you still have access to the role in the session (as the param will be cleared from the URL unless you manipulate it).
So either way you need to override the registrations controller, its just a case of how much.
I suspect there is a way to do this with just Pundit. But I have yet to be able to get it to work.

Rails + Devise: How to restrict a user from editing records that do not belong to him

I'm using Rails, Devise and Mongoid.
A user can only have one project (has_one :profile) but all users (and non authenticated users) can see a list of projects (I have this working). When a user is logged in they see "edit and delete" buttons next to the projects (via wrapping those buttons in <% if user_signed_in? %>). However, a signed in user sees these buttons next to all project and can edit or delete them all.
How do I restrict a logged on user to only be able to edit only his project?
As a bonus, is it possible to show specific content or html around the project that belongs to the signed in user (such as text that says "this is your project" or an additional class around the project's html)?
CanCan is great when you have multiple users being able to modify the same resource, and you need to keep track of who has which permissions. But if it's strictly the case that only the user who owns the project can modify it, CanCan is probably overkill.
Hide the links in the view as suggested by iouri, and then in your controller do something like:
def edit
if current_user != #project.user
# Redirect them to an error page
else
# Render the view
end
end
Better yet, create a method like:
def user_owns_project?
#project.user == current_user
end
Then set up a before filter for edit, update and destroy to redirect to the error page if the user doesn't own the project.
EDIT: Your before filter will also ned to find the project and set #project. CanCan takes care of this for you too with load_and_authorize_resource, but I'd still avoid using it unless you need, or expect to need, fine-grained permissions control.
Devise is to control "authentication", this should not to be your responsibility.
You want to control "authorizations", for that CanCan is better.

Resources