Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding rolify for role based access constraints #254

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ gem 'jquery-rails' # necessary for jquery_ujs w/data-method="delete" etc
gem 'uglifier'
gem 'non-stupid-digest-assets' # support vendored non-digest assets

gem 'rolify'

group :test, :develop, :ci do
gem 'pry'
gem 'jasmine', '2.0.1'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ GEM
rest-client (1.6.8)
mime-types (~> 1.16)
rdoc (>= 2.4.2)
rolify (3.4.0)
rubyzip (0.9.9)
sass (3.2.19)
sass-rails (4.0.3)
Expand Down Expand Up @@ -299,6 +300,7 @@ DEPENDENCIES
pry-byebug
quality-measure-engine!
rails (~> 4.1.2)
rolify
rubyzip
sass-rails (~> 4.0.2)
simplecov
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/admin_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ def toggle_privilidges(username, role, direction)

if user
if direction == :promote
user.update_attribute(role, true)
user.add_role(role)
render :text => "Yes - <a href=\"#\" class=\"demote\" data-role=\"#{role}\" data-username=\"#{username}\">revoke</a>"
else
user.update_attribute(role, false)
user.remove_role(role)
render :text => "No - <a href=\"#\" class=\"promote\" data-role=\"#{role}\" data-username=\"#{username}\">grant</a>"
end
else
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/patients_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def set_filter_params
@quality_report = QME::QualityReport.find(params[:quality_report_id])
authorize! :read, @quality_report
@query["provider.npi"] = {"$in" => @quality_report.filters["providers"]}
elsif current_user.admin?
elsif current_user.has_role?( :admin )
else
@query["provider.npi"] = current_user.npi
end
Expand Down
9 changes: 8 additions & 1 deletion app/controllers/api/providers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ def index
EXAMPLE
def show
provider_json = @provider.as_json
provider_json[:parent] = Provider.find(@provider.parent_id) if @provider.parent_id
if @provider.parent && can?(:read, @provider.parent)
provider_json[:parent] = Provider.find(@provider.parent_id) if @provider.parent_id
else
#clean these out so the ui does npt attempt to render them
provider_json[:parent] =nil
provider_json[:parent_id]=nil
provider_json[:parent_ids]=nil
end
provider_json[:children] = @provider.children if @provider.children.present?
render json: provider_json
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/logs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def index
end

where = {}
where[:username] = current_user.username unless current_user.admin?
where[:username] = current_user.username unless current_user.has_role?( :admin )

start_date = date_param_to_date(params[:log_start_date])
if start_date
Expand Down
18 changes: 13 additions & 5 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ def initialize(user)

user ||= User.new

if user.admin?
if user.has_role?(:admin)
can :manage, :all
# can [:create,:delete], Record
# can :manage, Provider
# can [:read, :recalculate,:create, :deleted], QME::QualityReport
# can [:create,:delete], HealthDataStandards::CQM::Measure
elsif user.staff_role?
elsif user.has_role?(:staff)
can :read, HealthDataStandards::CQM::Measure
can :read, Record
can :manage, Provider
Expand All @@ -39,12 +39,20 @@ def initialize(user)
patient.providers.map(&:npi).include?(user.npi)
end
can [:read,:delete, :recalculate, :create], QME::QualityReport do |qr|
provider = Provider.by_npi(user.npi).first
provider ? (qr.filters || {})["providers"].include?(provider.id) : false
providers = Provider.with_role(:staff, user)
provider_ids = []
providers.each do |p|
provider_ids.concat p.descendants_and_self.collect(&:id)
end
!(provider_ids & (qr.filters || {})["providers"]).empty?
end
can :read, HealthDataStandards::CQM::Measure
can :read, Provider do |pv|
user.npi && (pv.npi == user.npi)
ret = false
pv.ancestors_and_self.each do |p|
ret = true if user.has_role?(:staff, p)
end
ret
end
can :manage, User, id: user.id
cannot :manage, User unless APP_CONFIG['allow_user_update']
Expand Down
16 changes: 16 additions & 0 deletions app/models/role.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Role
include Mongoid::Document
has_and_belongs_to_many :users
belongs_to :resource, :polymorphic => true

field :name, :type => String

index({
:name => 1,
:resource_type => 1,
:resource_id => 1
},
{ :unique => true})

scopify
end
27 changes: 23 additions & 4 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class User

include ActiveModel::MassAssignmentSecurity
include Mongoid::Document

rolify
after_initialize :build_preferences, unless: Proc.new { |user| user.preferences.present? }
before_save :denullify_arrays
before_create :set_defaults
Expand Down Expand Up @@ -80,7 +80,7 @@ class User
validates :username, :presence => true, length: {minimum: 3, maximum: 254}

def set_defaults
self.staff_role ||= APP_CONFIG["default_user_staff_role"]
self.add_role :staff if APP_CONFIG["default_user_staff_role"]
self.approved ||= APP_CONFIG["default_user_approved"]
true
end
Expand Down Expand Up @@ -128,7 +128,7 @@ def self.by_email(email)
# =============

def grant_admin
update_attribute(:admin, true)
self.add_role :admin
update_attribute(:approved, true)
end

Expand All @@ -137,7 +137,26 @@ def approve
end

def revoke_admin
update_attribute(:admin, false)
self.remove_role :admin
self[:admin]
end

def admin?
self.has_role?( :admin )
end

def staff_role?
self.has_role?( :staff )
end

def provider_root
provider = nil
if self.admin? || self.staff_role?
provider = Provider.root
else
provider = Provider.with_role(:staff, self).first
end
provider
end

end
4 changes: 2 additions & 2 deletions app/views/admin/users.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@
</select>
</td>
<td>
<% if user.admin? -%>
<% if user.has_role?(:admin) -%>
Yes - <a href="#" class="demote" data-role="admin" data-username="<%= user.username %>">revoke</a>
<% else -%>
No - <a href="#" class="promote" data-role="admin" data-username="<%= user.username %>">grant</a>
<% end -%>
</td>
<td>
<% if user.staff_role? -%>
<% if user.has_role?(:staff) -%>
Yes - <a href="#" class="demote" data-role="staff_role" data-username="<%= user.username %>">revoke</a>
<% else -%>
No - <a href="#" class="promote" data-role="staff_role" data-username="<%= user.username %>">grant</a>
Expand Down
8 changes: 7 additions & 1 deletion app/views/home/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
PopHealth.categories = <%= @categories.to_json.html_safe %>;
PopHealth.patientCount = <%= @patient_count %>;
PopHealth.currentUser = new Thorax.Models.User(<%= current_user.to_json({:include => :preferences }).html_safe %>);
PopHealth.rootProvider = new Thorax.Models.Provider(<%= Provider.root.to_json({:include => :children }).html_safe %>, {parse:true});
<%
providerRoot = current_user.provider_root
providerRoot.parent_id = nil
providerRoot.parent_ids = nil

%>
PopHealth.rootProvider = new Thorax.Models.Provider(<%= providerRoot.to_json({:include => :children ,:except => :parent}).html_safe %>, {parse:true});
PopHealth.router = new PopHealth.Router();
Backbone.history.start();
</script>
4 changes: 2 additions & 2 deletions app/views/shared/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
<% if APP_CONFIG['logout_enabled']%>
<li><%= link_to raw('<i class="glyphicon glyphicon-log-out"></i> Logout'), destroy_user_session_path, method: 'delete' %></li>
<% end %>
<% if current_user.staff_role? || current_user.admin? %>
<% if current_user.has_role? (:staff )|| current_user.has_role?( :admin )%>
<li class="divider"></li>
<li role="presentation" class="dropdown-header">Admin</li>
<li><%= link_to raw('<i class="glyphicon glyphicon-plus"></i> Providers'), '/#providers'%></li>
<% if current_user.admin? %>
<% if current_user.has_role?( :admin )%>
<% if (APP_CONFIG['patient_management_enabled']) %>
<li><%= link_to raw('<i class="glyphicon glyphicon-eye-open"></i> Patients'), admin_patients_path%></li>
<% end %>
Expand Down
6 changes: 3 additions & 3 deletions app/views/shared/_top_nav.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
Welcome, <%= current_user.first_name.to_s %>
<span class="sep">|</span> <a href="http://projectpophealth.org/faq.html">help</a>
<% if APP_CONFIG['edit_account_enabled']%><span class="sep">|</span> <%= link_to 'account', edit_user_registration_path(current_user) %> <% end %>
<% if current_user.staff_role? || current_user.admin? %> <span class="sep">|</span> <a id="manage_link" href="javascript:void(0)">manage</a> <% end %>
<% if current_user.has_role?( :staff) || current_user.has_role?(:admin) %> <span class="sep">|</span> <a id="manage_link" href="javascript:void(0)">manage</a> <% end %>
<% if APP_CONFIG['logout_enabled']%><span class="sep">|</span> <%= link_to 'logout', destroy_user_session_path, 'class' => 'log'%> <% end %>

<ul id="manage-menu" class="ui-menu ui-widget ui-widget-content ui-corner-all dialog-menu" role="listbox" aria-activedescendant="ui-active-menuitem">
<% if current_user.staff_role? || current_user.admin? %>
<% if current_user.has_role?(:staff) || current_user.has_role?(:admin ) %>
<li class="ui-menu-item" role="menuitem" data-type="providers">
<a class="ui-corner-all" tabindex="-1" href="<%=providers_path%>">Providers</a>
</li>
<% end %>
<% if current_user.admin? %>
<% if current_user.has_role?(:admin) %>
<li class="ui-menu-item" role="menuitem" data-type="users">
<a class="ui-corner-all" tabindex="-1" href="<%=admin_users_path%>">Users</a>
</li>
Expand Down
1 change: 1 addition & 0 deletions config/initializers/active_model_serializers.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Rolify.orm=:mongoid
ActiveSupport.on_load(:active_model_serializers) do
# Disable for all serializers (except ArraySerializer)
ActiveModel::Serializer.root = false
Expand Down
1 change: 1 addition & 0 deletions config/initializers/mongo.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

MONGO_DB = Mongoid.default_session

# js_collection = MONGO_DB['system.js']
Expand Down
8 changes: 8 additions & 0 deletions config/initializers/rolify.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Rolify.configure do |config|
# By default ORM adapter is ActiveRecord. uncomment to use mongoid
config.use_mongoid

# Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
# Enable this feature _after_ running rake db:migrate as it relies on the roles table
# config.use_dynamic_shortcuts
end
3 changes: 2 additions & 1 deletion lib/hds/provider.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class Provider

include Mongoid::Document
resourcify
field :level, type: String

embeds_many :cda_identifiers, class_name: "CDAIdentifier"
Expand Down
10 changes: 10 additions & 0 deletions lib/tasks/popHealth_users.rake
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ namespace :pophealth do
RakeUserManager.approve ENV
end

task :update_roles => :environment do
User.where(:admin=>true).each do |u|
u.add_role :admin
end
User.where(:staff_role=>true).each do |u|
u.add_role :staff
end

end

class RakeUserManager
def self.grant_admin(env)
user = find_user(env)
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/roles.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

# This model initially had no columns defined. If you add columns to the
# model remove the '{}' from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
one: {}
# column: value
#
two: {}
# column: value
10 changes: 10 additions & 0 deletions test/fixtures/roles/admin_role.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"_id" : "admin_role",
"name" : "admin",
"resource_id" : null,
"resource_type" : null,
"user_ids" : [
"admin1",
"admin2"
]
}
12 changes: 12 additions & 0 deletions test/fixtures/roles/staff_role.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"_id" : "staff_role",
"name" : "staff",
"resource_id" : null,
"resource_type" : null,
"user_ids" : [
"admin1",
"admin2",
"gen1",
"gen2"
]
}
8 changes: 6 additions & 2 deletions test/fixtures/users/admin_user.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{"admin":true,
{
"admin":true,
"agree_license":true,
"approved":true,
"company":null,
Expand All @@ -11,6 +12,9 @@
"npi":null,
"registry_id":null,
"registry_name":null,
"staff_role":true,
"role_ids" :[
"staff_role",
"admin_role"
],
"tin":null,
"username":"adminuser"}
7 changes: 5 additions & 2 deletions test/fixtures/users/admin_user2.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{"admin":true,
{ "_id" : "admin2",
"agree_license":true,
"approved":true,
"company":null,
Expand All @@ -11,6 +11,9 @@
"npi":null,
"registry_id":null,
"registry_name":null,
"staff_role":true,
"role_ids" :[
"staff_role",
"admin_role"
],
"tin":null,
"username":"adminuser2"}
6 changes: 4 additions & 2 deletions test/fixtures/users/generic_user.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{"admin":false,
{"_id" : "gen1",
"agree_license":true,
"approved":true,
"company":null,
Expand All @@ -11,6 +11,8 @@
"npi":null,
"registry_id":null,
"registry_name":null,
"staff_role":true,
"role_ids" :[
"staff_role"
],
"tin":null,
"username":"genericuser"}
6 changes: 4 additions & 2 deletions test/fixtures/users/generic_user2.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{"admin":false,
{"_id" : "gen2",
"agree_license":true,
"approved":true,
"company":null,
Expand All @@ -11,6 +11,8 @@
"npi":null,
"registry_id":null,
"registry_name":null,
"staff_role":true,
"role_ids" :[
"staff_role"
],
"tin":null,
"username":"genericuser2"}
Loading