ActiveAdmin + CanCan + AASM event switcher with AJAX - ruby-on-rails

As an admin I have a specific role
I want to see and switch event for object
Depends on my role
Inspired by activeadmin_addons and its Enum Integration I want to make similar functionality for AASM by letting diffent admin users change events depending on their abilities/roles for specific events/statuses in model.

Taken from here, please see this link for additional files you need
Prequestites:
Gem: ActiveAdmin,
Gem 'active_admin_role', both are installed and working AdminUser model with current_admin_user setup (or similar to your app).
Tested with Rails 5.1.3.
After you finish and deploy/run server you must "Reload" Permissions in admin and enable "event_update" for manager or other than "super_admin" roles.
Smaller addons you'll need to do:
(in addition to below attached files)
In your AdminUser model add:
include CanCan::Ability
include ActiveAdminRole::CanCan::Ability
In your table_for (is where you render columns of data):
column 'Our Status' do |auction|
render 'admin/auctions/event_change', auction: auction
end
In initializers/active_admin.rb or whenever you want
ActiveAdmin::ResourceController.class_eval do
protected
def current_ability
# Match to your current admin user
#current_ability ||= Ability.new(current_admin_user)
end
end
also make sure your config:
config.authorization_adapter = ActiveAdmin::CanCanAdapter
config.authorization_adapter = ActiveAdmin::CanCanAdapter
config.cancan_ability_class = 'Ability'
Pardon me if I forgot something, let me know if you have any question or problem !

Related

Roles/permission security of home rolled version versus gem in RoR web app

I'm about to launch my first website and I'm concerned about security. The top two role/permission gems I keep seeing come up are cancancan and pundit for RoR web apps. Is there a significant advantage of those packages versus my own home rolled version?
To give the run-down of my version: I have a user, role, and user_role model, where user_role is the join-table to contain the many-to-many relationship. I assign/change roles via the roles_controller with
def change
#role = Role.find(params[:id])
#user = User.find params[:user_id]
# if user is assigned to be a student, delete all other roles
if #role.name == 'student'
#user.roles.each do |role|
#user.roles.delete Role.find(role.id)
end
end
# if user is assigned to be a teacher, delete student (if present)
if #role.name == 'teacher' && #user.has_role?('student')
#user.roles.delete Role.where(name: 'student')
end
#user.roles << #role if request.patch?
#user.roles.delete #role if request.delete?
redirect_to #user
end
And the controller has before_action :admin_user so that only admins can perform these actions. In addition, I have various before_actions for each role within different controllers based on roles, logged in users, and correct users. Are there some possible security issues I need to be aware of with this implementation?
The main advantage is that they are not your home rolled one-off. CanCanCan and Pundit have been used in scores of apps and have been battle tested and have a huge community both working on them and eying them for flaws - can you honestly say you will be doing the same for your solution down the line?
Also CanCanCan and Pundit are authorization libraries - they can be used to build role based authorization - but roles are not a built in part of either.
If you want a gem which is role library take a look at Rolify which makes it pretty simple to create roles which are linked to users and resources. It can be used with CanCanCan, Pundit or any other authorization lib.

How do I modify devise so that Pins are read only on Rails 4.2.4

I am a junior developer building my first web application for a customer using Rails 4.2.4, Devise for authentication and Pins scaffolding for the listing of the customers products for sale.
Everything is working, users can signup and login and then create, read, update and destroy pins.
Problem is, I don't want users to be able to create, update or destroy the pins on my index.html.erb page. (only view such as this is customer sales products)
If you look at the image attached. When you click on the ladies products BTN on the Home page it takes you to the index.html.erb where the pins are listed.
NB: I still want users to be able to signup and log in through DEVISE, but only to update their address details for shipping and not change the pins content. The pins CRUD content should only be created by the customer.
How should I approach this.
How should I approach this
You're looking at authorization...
Authentication is the process of verifying who you are (login).
Authorization is the process of verifying that you have access to something (permission).
--
Devise is authentication (user is logged in), authorization is a different matter.
Ryan Bates (RailsCasts) made a very good video & tutorial about it - he made a gem called CanCanCan which is now the bulwark of Rails authorization; Pundit is another.
What you're asking is how to give permission to users who are of a certain type to perform certain actions. The answer is to use either CanCanCan or Pundit to evaluate whether the user will have the credentials to CRUD a Pin:
#Gemfile
gem "cancancan"
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user) #-> looks for "current_user" from devise
user ||= User.new # guest user (not logged in)
if user.customer?
can :manage, Pin
else
cannot :manage, Pin
end
end
end
#app/controllers/pins_controller.rb
class PinsController < ApplicationController
load_and_authorize_resource
end
The above will only allow users who are set as customer to manage the Pin. Since you've been scant with model / controller code, the above the most specific I can be about it.

Rails 4 - secure way to give some users admin privileges?

I have a Rails app where I want some users to be super users with special privileges, like being able to access menus where they can edit content of the site. I'm not 100% sure that my implementation is secure, or the Rails way. It looks like this:
I have created an environment variable using the Figaro gem where I store the email-address of the original site creator:
ENV["site_manager_email"]
As I understand it, Figaro's application.yml works like Rails secrets.yml. My user model have a site_manager BOOLEAN attribute. The original site manager should be able to add other site managers by setting this property to true. In my user model I have a method to check whether users are site managers or not, it looks like this:
def is_site_manager?
self.email.eql?(ENV["site_manager_email"]) || self.site_manager == true
end
In my views where I have content that only site managers should be able to see I use the above method like this:
- if current_user.is_site_manager?
SUPER SECRET CONTENT THAT ONLY SITE MANAGERS SHOULD SEE
In my controllers I'm also planning to add a before action filter to only give access to some actions.
So my question is, is this a secure way to do it? If not, what should I change?
And, is there a more Rails way to do it? If so, how does it look?
What are you using for authentication? Devise?
For admins you can look into roles-based authorization and CanCan:
https://github.com/ryanb/cancan
There is a RailsCast that will show how to set it up:
http://railscasts.com/episodes/192-authorization-with-cancan
Once you install it and add a .role column to your Users you can create Ability.rb in your app/models that will look like something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role? :administrator
can :manage, :all
else
can :read, :all
can :create, Product
end
end
end

Require scope in activeadmin resource

I am using activeadmin and need to figure out how to require a scope to only show records pertaining to the current user.
I also have other scopes that users can select, but those scopes need to be "pre-scoped" so to speak so that only records belonging to that user are available at any given time.
I hope this makes sense. I'm fairly new to all of this, so i'm not real sure where to start. Thanks in advance for any help.
Did you try scoping with scope_to :current_user ?
AA has some examples with docs . Here they are
http://activeadmin.info/docs/2-resource-customization.html#scoping_the_queries
current_user is helper method to get currently logged in user (currend_admin_user is default I think)
code from AA initializer
# This setting changes the method which Active Admin calls
# to return the currently logged in user.
config.current_user_method = :current_user
If you had some kind of metod in your model that use your logged in user you can do something like that
controller do
def scoped_collection
Post.some_method(current_user)
#or for example Post.select(current_user.visible_posts_columns) ... etc
end
end

Rails Authorization on Web Services

My rails app is pretty much a front-end to several web services. I persist my User model, and that's about it. I need to add authorization to my web app, using Devise for authentication. I've noticed CanCan and acl9 seem to work mostly on instances of ActiveRecord models. Would CanCan or acl9 still fit my needs? Any tips on using either of these libraries in my situation?
Should I look for something that works more on actions instead of instances?
Also, these are both Role based systems, and I'm thinking of using a permission based system. Would they still be a good fit?
I can't speak for acl9. However, the cancan wiki does claim that "It is easy to make your own [model] adapter if one is not provided." https://github.com/ryanb/cancan/wiki/Model-Adapter In other words, even though you're not using ActiveRecord, you might still be able to use cancan.
Then again, if you're not planning on having roles, your ability definitions in cancan might be a little redundant looking, eg.:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :create, Widget if user.has_permission(:create_widgets)
can :delete, Widget if user.has_permission(:delete_widgets)
can :herp, Derp if user.has_permission(:herp_derp)
end
end
It would be great if you could use cancan just for its controller action authorization methods, but I don't know if that's possible. Good luck.
Just to (finally :) answer for acl9.
Acl9 is composed of two very separate pieces, the Access Control Subsystem which is all the authorizing stuff you put in your controller, and the Role Subsystem which is setting/checking/removing roles from an authenticated user.
The only thing that the Access Control Subsystem ever calls is current_user.has_role?( role, obj=nil). So, the Role Subsystem has zero dependency on ActiveRecord, associations, database, etc. There is a helper (acts_as_authorization_subject) which adds an ActiveRecord-dependent has_role? method to a class, but that's entirely optional and you're free to implement your own has_role? method (which can also fallback to calling super to get the acl9 one) and implement your access checks however you please. So, you said that you do persist your user model, but let's say you want a role for your user to be the admin of a school, but that school is a web service call into some remote system.
## in your model
class User < ActiveRecord::Base
def has_role? role, obj=nil
role == :admin &&
obj == school &&
school[:admin] == id # <-- just making up how we know we're the admin of the remote school
end
end
def school
#school ||= School.get_by_user_id(id)
end
end
## in your controller
class SomeController < ApplicationController
before_action :set_school
access_control do
allow :admin, of: :school
end
private
def set_school
#school = School.get_by_id(params[:school_id])
end
end

Resources