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

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"

Related

Administratively putting users into groups

There are many users and one administrator. Users can create groups and send/receive requests to join those groups. That system works fine. I am now building separate "super groups" that the admin creates. These "super groups" don't rely on requests, they rely on appointment. The admin selects a user, selects a group, and adds the chosen user into the chosen group. How do I accomplish this?
How do I set it up so that the admin can simply pick a user(on their profile page), pick a group(preferably from a drop-down) and then make the association?
For example, as an admin, I would like to go to the user's profile page and on the profile page there is a drop-down(only admin-accessible) and add the user to the group of my choice. How would I accomplish this? Through a type of form? A boolean field? Could I just add an append method (<<) into my join model create action and have a select-tag for a group and then a create-submit button? I just need to be pointed in the general direction and I think that I can manage.
Of note: I am using a has_many :through association to capture the relationship. An admin has many "super groups" which are created/deleted fine. The "super group" has many members(users). I just need to know how to put users into a group administratively.
UPDATE - What is the best practice for adding a user to a has_many :through association via dropdown?
You are talking about wanting to add a user to a super group FROM the user's profile page. This means you want to pass a super_group_id to the Rails controller, find that super group, and then add the user to that group.
This is one option:
#user = User.find(params[:id]) # or user_id, depending on the controller
#super_group = SuperGroup.find(params[:super_group_id])
#super_group.users << #user
Original Answer:
How are you currently creating and deleting the Super Groups? I assume you have a SuperGroupsController. One way to accomplish this is to have a sub-controller SuperGroups::UsersController with your same RESTful actions (create, update, etc). Those restful actions handle the assigning/removing of the users to groups. This allows you to separate the checking that you need to do to make sure only an admin is taking these actions.
class SuperGroups::UsersController < ApplicationController
before_action :verify_admin # create this method to do the checking
def create
# create a user super group association here
end
def delete
# remove a user from the super group here
end
end
This is apparently a best practice according to DHH. I first used this method after reading this article and I've found this pattern very clean and useful.

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.

Dynamic roles and permissions system in Rails app

I need to create roles based permissions systems in my Rails app. I would be totally happy with CanCan, but the main problem - it has to be dynamic, so that Admin has to be able to assign permissions and to create new roles. The permissions can be simple controller/action restrictions, and can be data related, for example some users can edit only their own profiles, and some of them can edit the profiles of all the users in the particular group. And it would be really nice to allow Admin to create new permissions.
What I'm thinking about is to store in db a controller/action, and some data related restrictions (I'm really confused here about the way to define them). So could you please give me some advice, what would be the best way to organize permissions?
Any thoughts are much appreciated
If you like CanCan, then I think is best to use it. Here is a short tutorial about storing abilities in database so non-programmers can update them:
https://github.com/ryanb/cancan/wiki/Abilities-in-Database
If you really, really want to implement such system yourself. Depending on your needs, I will suggest for you to implement it as simple as possible.
In the case you need only users to have access to modules(certain controllers). You can do:
Store all users permissions in just like serialized fields -> http://apidock.com/rails/ActiveRecord/Base/serialize/class
class User
serialize :permissions, Array
def access_to?(module)
permissions.include? module.to_s
end
end
some check when setting this field would be nice.
Just make a check on top of every controller if the current user have access to this controller(section)
class ApplicationController
private
def self.require_access_to(module)
before_filter do |c|
unless c.send(:current_user).try :access_to?(module)
c.send :render_no_presmissions_page
end
end
end
end
class AdminNewsController
require_access_to :news
end
Of course this is just a start position, from where you can easily evolve.
[EDIT The link given by #RadoslavStankov is a better answer than this.]
You can do it with CanCan easily. Just make a model for permissions (which has_and_belongs_to_many :users), and in your Ability#initialize load the permissions appropriate to the user from the model and say that the user can do them. Then make appropriate user interface for administrating that model.
Something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
user.permissions.each do |permission|
can permission.symbol, permission.scope
end
user.prohibitions.each do |permission|
cannot permission.symbol, permission.scope
end
end
end
where scope returned something like :all for nil scope, or Object.const_get(#scope_name) otherwise... Play with it. Anyway, it's doable.
More complex CanCan things are likely to be more complex (duh) - for example conditional permissions - but that would be a bitch to administer as well, so I think this should be enough for most applications.

Have different view based on type of user

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

Resources