Restricting Query Results with CanCan - ruby

I want to restrict access to report models according to roles via CanCan. Specifically, I want :admin roles to manage all, :expert roles create, read and edit all reports ascribed to their team and delete only their own, and user roles to create, read, edit, and delete their own reports.
#reports = report.pending_approval.by_team(current_user.team_id)
Based on the following abilities, and authorize_resource in all relevant controllers, I expected the above query to only return reports the user has created -- but it returns all the reports for a given team. I want the query to return all "pending" reports belonging to a particular team if the current_user is an :expert and is assigned to the team in question.
Does anyone know why it returns all reports belonging to a team, regardless of whether the current_user is an expert or not? Should I modify the query to check for (:expert) role? To limit access to the team expert only, should I modify the query to check for team membership and role in the query or CanCan ability?
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role? :admin
can :manage, :all
elsif user.role? :expert
can :read, Report, :user_id => user.id, :submitted => false
can :create, Report
can :update, Report, :user_id => user.id, :submitted => false
can :destroy, Report, :user_id => user.id, :submitted => false
else
can :read, Report, :user_id => user.id, :submitted => false
can :create, Report
can :update, Report, :user_id => user.id, :submitted => false
can :destroy, Report, :user_id => user.id, :submitted => false
end
end
end

The query you gave has no CanCan methods in it, so it doesn't know about your restrictions.
Try using accessible_by to scope the relation:
Report.accessible_by(Ability.new(current_user)).pending_approval # ... etc

Related

What is :manage, :all doing in Ruby?

I have a basic authorization class in a Rails application which looks like this:
class Ability
include CanCan::Ability
def initialize(user)
if user
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard
if user.admin?
can :manage, :all
else
can :manage, [Agreement, Attachment, Contact, Deadline, Event, Image, Photo, Project, Submission, Talk]
can :update, User, id: user.id
end
end
# Current user cannot delete his account
cannot :destroy, User, id: user.id
end
end
Now, I get an unauthorized error when trying to access the dashboard with a simple user, but once I put can :manage, :all for a simple user condition it is misteriouslly let through and see the dashboard.
What is :manage, :all having more than :manage, [All_my_tables] and why is my user not let in using this way?
Here is the answer, I just need to to :manage, :all for a simple user and then override the permissions.
class Ability
include CanCan::Ability
def initialize(user)
#Check if the user is logged in
if user
#Grant access to the dashboard
can :access, :rails_admin
can :dashboard
can :manage, :all
#Simple user permissions set here
if !user.admin?
alias_action :create, :update, :destroy, to: :cud
can :manage, :all
cannot :cud, User
cannot :destroy, [Agreement, Submission]
end
end
can :update, User, id: user.id #User can edit his/her own account
cannot :destroy, User, id: user.id #User cannot delete his/her own account
end
end
Thanks for the down votes, but this question has been well researched before coming here

Accessing more than one active records in action controller in rails 3

suppose I have 2 models: 1. user and 2. userProfile
we have one-to-one relationship between 2 models.
Then,I would like to create Usercontroller and create user with fields such as username and password in user and other details like address, mobile number,etc in userprofile.
user_id is a foreign key in user_profile
Then I created new.html.erb in user a view that render all above fields in a same form.
Then how I can save the data in two different tables user and userprofile respectively.
what I tried is
1. has_one :user_profile in user.rb and belongs_to :user in user_profile.rb
2. accepts_nested_attributes_for :user_profile in user.rb
user_controller:
def create
#user = User.new(params[:user])
if #user.save
#userprofile = #user.create_user_profile(params[:user_profile])
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
3. Then I got error the only userid is created in user_profile and also update run to make user_id nil which can't be! so raising error:
Unknown column 'user_profiles.' in 'where clause': UPDATE `user_profile`
SET `user_id` = NULL WHERE `user_profiles`.`` IS NULL
so what's wrong and how to troubleshoot this?
If you're using accepts_nested_fields_for then you don't need to manage saving the UserProfile records yourself. ActiveRecord will take care of it for you.
To get this to work you need to:
Make sure you have accepts_nested_fields_for :user_profile in your User model
Update your app/views/users/_form.html.erb to include form elements for the UserProfile, e.g.:
<%= f.fields_for :user_profile, #user.user_profile do |profile_form| %>
<%= profile_form.text_field :address %>
<%= profile_form.text_field :mobile_number %>
<% end %>
Update your UsersController#new action to build the UserProfile for the User, e.g.
def new
#user = User.new
#user.build_user_profile
(...)
end
For more information see the Rails API docs covering Active Record Nested Attributes.

Declarative Authorization - how to set acces for all actions in controller?

I use Declarative Authorization gem and in my authorization_rules.rb I have following rule:
role :admin do
has_permission_on :users, :to => [manually set up all actions in this controller]
end
Exist any more elegant way to set up the access for all actions in the respective controller? I tried these ways
role :admin do
has_permission_on :users, :to => :all
end
role :admin do
has_permission_on :users
end
But nothing works me. Is there any better way to set up all actions in the controller than slavishly type every single action?
Actions are public methods on a controller class. Code below is untested but should get you started.
c = MyController
actions = []
c.public_methods(false).each { |m| actions << m.to_sym }
role :admin do
has_permission_on :users, :to => actions
end
This is what I do:
has_permission_on :controller, :to => [:all]

App with Accounts and Users - Devise user can't create additional users

Using Rails 3.1.3 with Devise 1.5.3. My app has accounts and users. Each account can have multiple users. A user can have one role, "user" or "account_admin". Devise signup routes to accounts#new. The account and an initial account_admin user are created simultaneously. Got this working as described here (although things have evolved some since then).
An account_admin signs should be able to create additional users in that account. That's where I'm running into trouble: instead of creating new users, it's just redirecting to the user_path (users#show) page with the message "You are already signed in." (user_path is my overridden after_sign_in_path.)
This article asks a similar question. The answer suggests a separate namespace for admins, but since admins are part of my regular app I don't think that applies.
I've defined a complete UsersController. According to the log, GET "/users/new" renders from my "UsersController#new". However POST "/users" is intercepted by Devise and rendered from "Devise::RegistrationsController#create".
config/routes.rb
devise_for :users
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
end
resources :users
resources :accounts
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource # CanCan
...
def new
# CanCan: #user = User.new
end
def create
# CanCan: #user = User.new(params[:user])
#user.skip_confirmation! # confirm immediately--don't require email confirmation
if #user.save
flash[:success] = "User added and activated."
redirect_to users_path # list of all users
else
render 'new'
end
end
...
end
I've tried overriding the Devise controller, thinking I could tell it to use my users#create action if the user is already signed in. The log tells me it is using my controller ("Processing by RegistrationsController#create as HTML"), but it doesn't seem to execute its code. I've commented out my custom actions and just left in the logger lines, but I don't get my logging messages. And in spite of super being commented out, the behavior doesn't change--it still redirects with "You are already signed in."
config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
logger.info "Custom RegistrationsController: new"
super
end
def create
logger.info "Custom RegistrationsController: create"
# super unless user_signed_in?
# render "users#create"
end
def update
super
end
end
What am I missing? How can I let the account_admin user create additional users?
The main issue is that Devise was intercepting the POST "/users" (and a few other routes). Found this workaround to allow my Users controller handle those routes: change the devise_for to skip registrations, then add back in the routes for which Devise normally defines aliases:
routes.rb
devise_for :users, :skip => [:registrations]
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
get "cancel_user_registration", :to => "devise/registrations#cancel"
post "user_registration", :to => "users#create"
get "new_user_registration", :to => "accounts#new"
get "edit_user_registration", :to => "users#edit"
end
resources :users
resources :accounts
Never figured out why the Devise controller override wasn't working.
A user on this thread pointed out the devise_invitable gem which might be an interesting alternative.
This is how I ended up getting this to work with my setup (Devise tied to my User model -- no separate Editor/Admin model):
class UsermakersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render users_path }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
userParams = params.require(:user).permit(:name, :email)
end
end
From here, when you want to add a new User as another User, simply call the Usermakers#new url in your form, e.g., I have this at the bottom of my Users index page:
<%= link_to 'Add User', new_usermaker_path, id: 'new_user_link' %>
And the Usermakers form looks like:
= simple_form_for #user, url: usermakers_path, html: {multipart: true} do |f|
=f.error_notification errors: true
= f.input :name
= f.input :email
%p
= f.button :submit, 'Create User', class: 'button'
Of course, you'd need to add a dummy new.html.erb file which simply renders _form.html.erb to use this.
Just add the new and create methods to your routes.rb file (whether by resources :usermakers, or more specific routes) and you should be good to go. Hope this helps!

Editing records with SQLite, DataMapper, and Sinatra

I'm in the process of learning Sinatra and DataMapper. To do so, I've been playing with a "customer database" project.
Creating and deleting records is trivial and now I'm working on editing records. So far I've managed to piece together a form in my views and a couple of routes that I thought would edit a record. Here's some code to illustrate my issue:
My edit.erb view: http://gist.github.com/308405
My edit/update routes:
get '/edit/:acct' do
#title = "Edit Client Data"
#client = HE_Backend.get(params[:acct])
erb :edit
end
post '/update/:acct' do
client = HE_Backend.get(params[:acct])
client.attributes = {
:name => params['client']['name'],
:company => params['client']['company'],
:street => params['client']['street'],
:state => params['client']['state'],
:zip => params['client']['zip'],
:phone => params['client']['phone'],
:fax => params['client']['fax'],
:website => params['client']['website'],
:order_date => params['client']['order_date'],
:payment_date => params['client']['payment_date'],
:monthly => params['client']['monthly'],
:setup => params['client']['setup'],
:details => params['client']['details'],
:notes => params['client']['notes'],
:status => params['client']['status'],
}
if client.save
redirect "/show/#{client.acct}"
else
redirect('/list')
end
end
It looks like the "client.save" portion of the route is returning false, because I'm getting redirected to "/list" each time. If I use the #update method rather than #save, DM complains about "dirty records".
Anyone have any ideas as to what I'm doing wrong or can you point me to examples for editing records in SQLite with DataMapper and Sinatra?
Thanks!
This turned out to be a validations issue. If I don't have validations in place and put data types other than what's in my model in those fields, the #save method apparently returns false.

Resources