I have a Model Bot and I would like to ensure that there is only one Bot object in my database. I also need to make sure it is persisted and not tampered with.
My original thought was to do this in a migration, one that would follow the :bots table migration. It would include a line that is something like:
Bot.all.size == 0 ? Bot.create! : nil
Maybe this would prevent the AR object from being messed with in future migrations?
BONUS: Would be awesome to be able to have instant and global access to this class object. I was thinking using a singleton module in my Bot class that way I can always reference Bot.instance and have access to that specific object.
USE CASE:
I have 4 types of users in my DB and this bot will be the facilitator to delivery role-specific messages to them through our in-app messaging feature.
The Class Bot will have a has_many association with BotMessage/bot_messages. On the bot_messages table will be an enum field for user_role.
Messages will be created by company admins and stored in these tables because we want them to be viewable at any time by looking at the "conversation" thread between the User and the Bot.
When it comes to only having 1 bot, it's just that. I have no need for an additional Bot object. Additionally, since there is only one object it would be nice to be able to have a way of explicitly targeting that object without having to run a query to find it.
For example, unlike User where there could be 1000 records and in order to find the specific one you would do something like #user = User.find_by_email('foo#bar.com'), doing something like that for the bot would be unnecessary since there is only one record to find. That is what lead me to believe having a singleton object may be worthwhile here, so whenever I need to pull up a message for a specific role, I could run Bot.instance.bot_messages.where(user_role: 1) or something similar
Based on your Use Case, I see no reason for Bot to be a model.
Let's say you have a role called cool_user and you want to get all the bot_messages for that role, you might do something like:
class Bot
class << self
def bot_messages(user_role)
BotMessage.send(user_role)
end
end
end
As a very thoughtful but potentially anonymous super code monkey notes in the comments, you could also do:
class Bot
def self.bot_messages(user_role)
BotMessage.send(user_role)
end
end
Which some folks might find more readable. IMO, it is a bit of an issue of personal preference.
In either case, you should be able to do
Bot.bot_messages(:cool_user)
Since, as stated in the docs,
Scopes based on the allowed values of the enum field will be provided as well.
So, I believe BotMessage, with the properly set enum, should respond to cool_user and return all the bot_messages for that role.
You may need to check the docs to get the syntax exactly right.
I believe this should also satisfy your BONUS requirement.
A proven solution would be to use an STI on User (with a user_type column)
class User < ApplicationRecord
...
end
class Bot < User
has_many :bot_messages, foreign_key: :user_id
end
Is it what you're looking for ?
Related
My Rails app has many models that form a hierarchy. For example: Retailer > Department > Product Category > Product > Review.
A business requirement is that high-authority users can "share" any individual element in the hierarchy with a new or existing "normal" user. Without having an object shared with them, normal users have no rights to see (or do anything else) with any object in any level of the hierarchy.
The sharing process includes a choice of whether the share grants permission to read-only, read-update, or full CRUD on the target object.
Sharing any object grants R/O, R/W or CRUD permission to that object and all lower level objects in the hierarchy, and R/O permission to all of the direct ancestors of the object. The object collection grows organically, so the permission system works by just logging the user_id, the object_id of the share, and the nature of the share (R/O, CRUD, etc). As the population of objects in this hierarchy grows all the time, it is impractical to create an explicit permission record in the DB for every user/object combination.
Instead, at the start of the user request cycle, ApplicationController gathers all the permission records (user X has CRUD permission to Department #5) and holds them in a hash in memory. A Permissions model knows how to evaluate the hash when any object is passed to it - Permission.allow?(:show, Department#5) would return true or false depending on the content of the user's permission hash.
Let's take, for example, the Department model:
# app/models/department.rb
class Department < ActiveRecord::Base
after_initialize :check_permission
private
def check_permission
# some code that returns true or false
end
end
When the check_permission method returns true, I want Department.first to bring back the first record in the database as normal, BUT, if check_permission returns false, I want to return nil.
Right now, I have a solution whereby default scopes trigger a permissions check, but this is causing 2X the number of queries, and for classes with a lot of objects, memory problems and time/performance issues are sure to be on the horizon.
My goal is to use after_initialize callbacks to pre-permission the objects.
It would appear however that after_initialize is unable to block the original object from being returned. It does allow me to reset the values of the attributes of the object, but not to dispense with it.
Anybody know how to achieve this?
EDIT:
Many thanks for all of the answers and comments offered so far; hopefully this extended version of the question clarifies things.
Basically you need to check for access rights (or permissions) before returning a database query result. And you are trying to integrate this logic into your models.
It is possible, but not with the design you described in your question. It is not clean to implement this directly in ActiveRecord adapter methods (such as first, all, last etc...). You need to rethink your design.
(skip to point 'D' if this is too much reading)
You have several choices, which all depend on the way your permissions are defined. Let's look at few cases:
A. A user have a list of departments he owns and only him can access them
You can simply implement this as a has_many/belongs_to association with Active Record Associations
B. Users and Departments are independent (in other words: no ownership such as described in the previous case) and permission can be set individually for each users and each departments.
Simply again, you can implement a has_and_belongs_to_many association with Active Record Associations. You will need to create web logic so the administrator of your application can add/edit/remove access rights.
C. More complex case: the existing authorization libraries
Most people will turn to authorization solutions such as cancan, pundit or other
D. When those authorization libraries are oversized for your needs (actually, my case in most of my projects), I found that implementing authorization through rails scoping answers all my needs.
Let's see it through a simple example. I want administrators to be able to access the whole database records ; and regular users to access only departments with status = open and only during operation hours (say 8am-6pm). I write a scope that implement my permission logic
# Class definition
class Department
scope :accessible_by -> (user) do
# admin user have all access, always
if user.is_admin?
all
# Regular user can access only 'open' departments, and only
# if their request is done between 8am and 6pm
elsif Time.now.hour >= 8 and Time.now.hour <= 18
where status: 'open'
# Fallback to return ActiveRecord empty result set
else
none
end
end
end
# Fetching without association
Department.accessible_by(current_user)
# Fetching through association
Building.find(5).departments.accessible_by(current_user)
Defining a scope obliges us to use it everywhere in our code. You can think of the risk to "forget" going through the scope and accessing directly the model (i.e writing Department.all instead of Department.accessible_by(current_user)). So that's why you must solidly test your permissions in your specs (at the controller or features level).
Note In this example we do not return nil when the permission fails (as you mentioned in your question), but an empty result set instead. It is generally better so you keep the ActiveRecord method chaining capability. But you could also raise an exception and rescue it from your controller then redirect to a 'not authorized' page for example.
That is not what the after_initialize callback is used for. Instead, you could just define a method that does the same thing. For example, put this in your Department model and it should achieve the results you are looking for:
def self.get_first
check_permission ? first : nil
end
UPDATE
I'm not exactly sure how safe something like this would be, but you could just override the all method as the other query methods are based off of it.
class Department < ActiveRecord::Base
def self.all
check_permission ? super : super.none
end
private
def self.check_permission
# some code that returns true or false
end
end
You are probably better off using some authorization framework though.
UPDATE 2
Thinking about this a little more, I strongly recommend using a different approach. You really shouldn't be overriding methods like all as there will surely be unintended side effects.
A practical alternative would be to create a has_and_belongs_to_many relationship between Department and User. Here is how you would set it up:
user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :departments
...
end
department.rb
class Department < ActiveRecord::Base
has_and_belongs_to_many :users
...
end
Then run these commands in your terminal:
rails g migration CreateJoinTableDepartmentsUsers departments users
rake db:migrate
Now you can add users to a department with #department.users << #user, or departments to a user with #user.departments << #department. This should achieve the functionality that you are looking for.
#user.departments will return only departments for that user, #user.departments.first will return the first department for that user or nil if it doesn't have any, and #user.departments.find(1) will return the corresponding department only if it belongs to the user or throw an exception otherwise.
You can use before_create callback to stop record creation if check permission is false. Just return false in check_permission filter and record will be not created.
class Department < ActiveRecord::Base
before_create :check_permission
private
def check_permission
# return false if permission is not allowed
end
end
Hi I'm new here and also new in rails.
I want to add a couple values by default to a database called books (Model: Book.erb)
there is a user who creates these books(current_user), and I thought that a way to identify who creates and deletes this content is by adding some default values from the user and clasificate them (to be specific username and password)
my table ":books" has available two fields for adding username and password
I tried to do this:
# Book.erb
class Book < ActiveRecord::Base
after_save :set_default_values
def set_default_values
self.username = current_user.username
self.password = current_user.encrypted_password
end
end
but it seems to be that I can't call 'current_user' from this model.
I was reading a pratice on this blog
but some were saying that this method violates the MVC pattern, do you agree with them?
do you guys know a better way to do this process without violating the pattern?
Well, I'm not sure I can conceive of why you'd want to store a user name and user password in a book table as even if it was easily explained, it would be in violation of normalization practices for good database design which pretty much states you should only express a field once and then share it where it needs to be shared.
Now, assuming you must do this for some reason I can't conceive, I'd have to ask if "username" is your actual field or is it just "name" which is more standard Rails. And, I believe you'll have to have a relationship between these models to pull the data from one into the other and I don't see that book has_many users or one or belongs_to or anything of that sort.
With a relationship between book and user you have access to all user properties without writing them anywhere other than the user table. So I think you probably want to look at that.
I wondering is there a way to represent model differently (or probably control access on fields level) depending on it's (model instance) state and controller using it.
Example:
Imagine we have an Order model with product_id, count, price and status fields.
status could be one of: :new, :confirmed, :accepted, :cancelled, :delivered and :closed.
Application can access Order from, say, two controllers CustomerOrdersController and SellerOrdersController. So, CustomerOrdersController could create and edit orders. But able to change only count field. On the other hand SellerOrdersController could edit orders. But able to change only price field. I.e. it would be great if instance of Order class that CustomerOrdersController working with have no price= method. Same for count=(product=) and SellerOrderController.
Further more set of columns permitted to edit depends on status field (probably work for some state machine).
So, the question is: how would you do this in your app?
PS
I think about some ActiveModel proxy objects for ActiveRecord instances, but do not know actually will it work or not. Consider:
class CustomerOrderProxy < ActiveModel::Base end
class SellerOrderProxy < ActiveModel::Base end
class Order < ActiveRecord::Base
def wrap_proxy(controller_user)
controller_user == CustomerOrdersController ? CustomerOrderProxy(self) : SellerOrderProxy(self)
end
end
Another approach would be to do tons of checks and params validations inside controller actions, but I do not want to. I believe in "Fat model - skinny controller" :)
PPS
I know that ruby have plenty state machine plugins, but AFAI understand they define only transitions, not method set (i.e. representation) of the object.
This sounds like simple access control. Access is granted based on the authorized user, not which controller is being used. Take a look at the cancan gem for implementing clean, declarative access control for your AR objects.
Looks like I've found appropriate solution: in Ryan Bates's screencast Dynamic attr_accessible
Update:
In Rails 3.1 update_attributes(params[:order], role) could be used. Check out rails api. Though it cannot be used to change access control according to object's state.
To preserve data integrity, I need to prevent some models from being modified after certain events. For example, a product shouldn't be allowed to be written off after it has been sold.
I've always implemented this in the controller, like so (pseudo-ish code):
def ProductsController < ApplicationController
before_filter require_product_not_sold, :only => [ :write_off ]
private
def require_product_not_sold
if #product.sold?
redirect_to #product, :error => "You can't write off a product that has been sold"
end
end
end
It just struck me that I could also do this in the model. Something like this:
def Product < ActiveRecord::Base
before_update :require_product_not_sold
private
def require_product_not_sold
if self.written_off_changed?
# Add an error, fail validation etc. Prevent the model from saving
end
end
end
Also consider that there may be several different events that require that a product has not been sold to take place.
I like the controller approach - you can set meaningful flash messages rather than adding validation errors. But it feels like this code should be in the model (eg if I wanted to use the model outside of my Rails app).
Am I doing it wrong?
What are the advantages of handling this in my model?
What are the disadvantages of handling this in my model?
If I handle it in the model, should I really be using validates rather than a callback? What's the cleanest way to handle it?
Thanks for your ideas :)
It seems like you already have this one covered, based on your question. Ideally a model should know how to guard its state, as data objects are typically designed with portability in mind (even when they'll never be used that way).
But in this case you want to prevent an action before the user even has access to the model. Using a model validation in this case means you're too late and the user has already gone farther than he should by having access to and attempting to write off a product which should never have been accessible based on its sold status.
So I guess the ideal answer is "both." The model "should" know how to protect itself, as a backup and in case it's ever used externally.
However in the real world we have different priorities and constraints, so I'd suggest implementing the change you listed if feasible, or saving it for the next project if not.
As far as using a model callback versus a validation, I think that's a trickier question but I'll go with a validation because you'd likely want to present a message to the user and validation is built for exactly that use (I'd consider this more of a friendly and expected user error than a hostile or security-related one which you might handle differently).
Is that along the lines of what you've been considering?
I'm trying to determine the best structure to approach multi level user groups. Thus far I've created one object called "User" which i assumed could potentially be broken into different levels. Or should I simply create different tables for each user group?
Have a look into Single Table Inheritance..
The short version is that you add a type(string) column to your table and subclass all other models that will use that table from User
Eg:
class SuperUser < User
...
end
I assume you are talking about differnt roles for your users. I am currently using RoleRequirement. It gets the job done fairly easily.
http://code.google.com/p/rolerequirement/
As EmFi suggested, single table inheritance is your best bet. You would need to add the type field to the users table in the database and subclass your User model as below:
class Admin < User
# You probably don't need any methods here.
end
but you would also need to create a before filter. Quite similar to the one which makes sure that the user is logged in, it simply checks the class of your user. This should go in your ApplicationController:
def check_admin
current_user.is_a? Admin
end
There you go, Bob's your uncle, you have rudimentary authorisation.
To promote a user to Admin within rails, just change the type field. Of course, in this way, the user can only hold one access level (which is fine for a lot of applications). If you should want a more complex system, acl9 is quite well equipped for the job. I personally make a habit of using it along with authlogic to form quite a powerful system.