Check user has order create permission for multiple companies - ruby - ruby-on-rails

The association is as follows
Company has_many :orders
The permissions are set as follows
if #resource.has_cached_role?(:client_admin)
can %i[read create confirm], Order, company_id: resource_company_ids
end
def resource_company_ids
#resource_company_ids ||= Company.where(id: #resource.company_id)
.or(Company.where(parent_id: #resource.company_id))
.pluck(:id)
end
#1) The client admin logging in to a company can create orders for that company and also for its child companies.
#2) Also, there are company which does not have child companies.
When a user is logged in, i need to check if the user has order create permissions for multiple companies(scenario #1)
How to achieve this using cancancan?
Any help would be appreciated.

i think a block conditions maybe help
can %i[read create confirm], Order |order|
resource_company_ids.include?(order.company_id)
end

Related

Setting custom permissions within a "dashboard" area in rails

I'm putting together a side project for a teacher/student type of website where the student will share a dashboard with the teacher. The teacher and student can both upload files and leave comments in the dashboard.
The part that I'm stuck on is the permission. For student, I've set up the index controller to this method
def index
#Homework = Homework.where(:user_id = current_user)
end
With this, I'm able to have the student only see the work that they have, but I'm confused on how to get the teacher to see each individual student's work?
Suggestions? Thanks
Here's a simple solution if you only ever need to support a single class in your application:
def index
if current_user.teacher?
#homeworks = Homework.all
else
#homeworks = Homework.where(user_id: current_user)
end
end
Otherwise, your Homework schema does not seem to be correctly designed. See your query below:
Homework.where(:user_id = <student_id>)
This works to retrieve a student's homeworks, but it does not work to retrieve a teacher's students' homeworks. You may need a class_id for a teacher to see each individual student's work:
Homework.where(:class_id = <class_id>, :user_id = <student_id>)
A Class has_many Teachers and has_many Students. This design will allow you to support multiple classes.
Some more guiding questions:
Is teacher/student both kept in the same User model?
How do you differentiate between teacher/student in your current User model?
Is there a "user_type" column somewhere in User?
What happens if the current_user is of the "teacher" user_type?
For complex user permissions, use CanCanCan: https://github.com/CanCanCommunity/cancancan
Don't use uppercase instance variables. ex: #Homework should be #homework
Check out the gem CanCan. Install it (follow the instructions, you should have to put something in application controller), Then, put in your ability file:
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Homework, user_id: user.id
end
end
Then at the top of your StudentController put
load_and_authorize_resource
And the index action should look like:
#homework = #student.homework
Now, you didn't post your whole controller so this is a much as I can help.
I believe you may have a bigger underlying issue. You have students and teachers has_many homework i read in your comment. Then in your example you use user_id. You are likely overriding your students and teacher ownership of homework. You would need a has_and_belongs_to_many relationship OR you would need a student_id and teacher_id columns on the homework table
Cancan automatically generate a number of instance variables which can make it feel like magic. Watch the free railscasts on cancan the same guy who made the video wrote the CanCan library.

Figuring out relationships for a Document model that can be editted by multiple users

I'm trying to figure out how to set up the relationship/association for a Document model for my rails project. I have a User model and Users can friend each another (friendship model).
Now I want users to be able to invite (give access to) CERTAIN friends to modify and edit these documents similar to Google Docs.
This is my current approach to this problem:
Create a new relationship model called "group", essentially a subset of friends. Document belongs to a user and document belongs to a group. Users can then invite their friends into these group relationships and documents can be accessed/modified through these groups.
Therefore, User has many groups and group belongs to many users.
My question:
Is there a better approach to this problem?
You might consider a "Membership" join table. So:
Group >- Membership -< User
...since you requirement is some users will have abilities others wont (editing). There are all sorts of options from there, for example STI which would give you:
class Membership < ActiveRecord::Base
...general membership functions
end
class Editor < Membership
...editor specific code...
end
class Reviewer < Membership
...reviewer specific code...
end
And a controller class something like
class MembershipController
def create invitation
if invitation.for_editor?
# assuming invitation has one group, and group_memberships method that returns group.memberships
invitation.group_memberships.create! user: current_user
else
invitation.group_memberships.create! user: current_user
end
end
end
As an example, depending on your opinion of STI.
https://github.com/scambra/devise_invitable
might have more ideas.

Devise/CanCan - Restricted Content

Building a rails B2B application that will have various users. I'm pretty clear on restricting access for internal staff using Devise and CanCan but I want to be able to give suppliers and customers their own login as well. Customer will be fairly simple, however, I want to ensure the supplier (label) login enables them to view and amend their own product and sales data only.
The model is roughly:
User (as setup by Devise)
Label [has_many releases]
Release [belongs_to label / has_many products]
Product [belongs_release / has_many tracks]
Track [belongs_product]
I'm guessing I could add in a label_id field on the user model and associate that way but I need internal users (and customers) to have access to view all label data. I also need to allow a label to have many users.
Would it simply be a case of defining a 'label' role via Cancan that enforces the use of a label_id in the the views? If that's the correct approach how do I then lock down the content to that label_id in my controllers/views? Role based if statements?
Thanks in advance!
What you'd first have to do is define some CanCan roles, like supplier, customer and staff, and then create an interstitial controller to handle the forking:
class CheckingController < ApplicationController
def index
#path = case user.role
when 'supplier'
supplier_path
when 'customer'
customer_path
when 'staff'
staff_path
else
admin_sign_in_path #or whatever
end
redirect_to #path
end
end
Then in your routes.rb file you can send users to either the root or index action of your controller by first sending them to CheckingController#index which will redirect based on your CanCan roles.

RoR: Different user roles for each new created record?

I want to make a record management system. The system will have 4 different user roles: Admin, Viewer, Editor and Reviewer.
While the first two are easy to implement using gems such as cancan and declarative authorization, the other two are not so simple.
Basically each new record is created by an Admin (only an Admin can create new records), and should have its own separate Editor and Reviewer roles. That is, a user can be assigned many different roles on different records but not others, so a user might be assigned Editor roles for Record A and C but not B etc.
Editor: can make changes to the record, and will have access to specific methods in the controller such as edit etc.
Reviewer: will be able to review (view the changes) made to the record and either approve it or submit comments and reject.
Viewer: Can only view the most recent approved version of each record.
Are there any ways of handling such record-specific user roles?
This can be accomplished without too much effort with the cancan gem and a block condition. A block condition checks for authorization against an instance. Assuming your Record class had an editors method that returns an array of authorized editors the cancan ability for updating a Record might look something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
...
can :update, Record do |record|
record.editors.include?(user)
end
...
end
end
See "Block Conditions" on the CanCan wiki:
https://github.com/ryanb/cancan/wiki/Defining-Abilities
Update
Storing which users have which access to which records could be done many ways depending on your specific needs. One way might be to create a model like this to store role assignments:
class UserRecordRoles < ActiveRecord::Base
# Has three fields: role, user_id, record_id
attr_accessible :role, :user_id, :record_id
belongs_to :user_id
belongs_to :record_id
end
Now create a has_many association in the User and Record models so that all role assignments can be easily queried. An editors method might look like this:
class Record < ActiveRecord::Base
...
has_many :user_record_roles
def editors
# This is rather messy and requires lot's of DB calls...
user_record_roles.where(:role => 'editor').collect {|a| a.user}
# This would be a single DB call but I'm not sure this would work. Maybe someone else can chime in? Would look cleaner with a scope probably.
User.joins(:user_record_roles).where('user_record_roles.role = ?' => 'editor')
end
...
end
Of course there are many many ways to do this and it varies wildly depending on your needs. The idea is that CanCan can talk to your model when determining authorization which means any logic you can dream up can be represented. Hope this helps!

What is the best way to do scoped finds based on access control rules in Rails?

I need to find an elegant solution to scoped finds based on access control rules. Essentially I have the following setup:
Users
Customers
AccessControl - Defines which user has access to another users data
Users need to be able to access not just their own customers but also shared customers of other users.
Obviously something like a simple association will not work:
has_many :customers
and neither will this:
has_many :customers, :conditions => 'user_id in (1,2,3,4,5)'
because the association uses with_scope and the added condition is an AND condition not an OR condition.
I also tried overriding the find and method_missing methods with the association extension like this:
has_many :customers do
def find(*args)
#get the user_id and retrieve access conditions based on the id
#do a find based on the access conditions and passed args
end
def method_missing(*args)
#get the user_id and retrieve access conditions based on the id
#do a find based on the access conditions and passed args
end
end
but the issue is that I don't have access to the user object / parent object inside the extension methods and it just does not work as planned.
I also tried default_scope but as posted here before you can't pass a block to a default scope.
Anyhow, I know that data segmentation and data access controls have been done before using rails and am wondering if somebody found an elegant way to do it.
UPDATE:
The AccessControl table has the following layout
user_id
shared_user_id
The customer table has this structure:
id
account_id
user_id
first_name
last_name
Assuming the the following data would be in the AccessControl table:
1 1
1 3
1 4
2 2
2 13
and so on...
And the account_id for user 1 is 13 I need to be able to retrieve customers that can be best described with the following sql statement:
select * from customers where (account_id = 13 and user_id = null) or (user_id in (1,3,4))
Sorry if I've completely missed the point here but I'm not 100% sure of what you want to do. Is AccessControl a relationship between User and Customer? If so looks like you just need to setup a many-to-many relationship.
class User
has_and_belongs_to_many :customers
# or this if you need to store meta data in the join table
has_many :customers
has_many :access_controls
has_many :accessible_customers, through => :access_controls
end

Resources