Multiple devise models vs permission based - ruby-on-rails

The application I'm building is to allow users to download vouchers. Employers sign up for an account, add employees (who get a login), select which vouchers to enable, and then employees can see the enabled vouchers and download.
I initially created two devise models: employee and employer. This was so active record associations would be simple (employer has_many employees, employee has_many vouchers). But this would also mean separate database tables and therefore separate sign in forms.
I looked into single sign in forms for multiple users and this seemed to have the consensus that you should instead have a single User model and use CanCan and Rolify for permissions. But the problem with that is you cannot (I believe?) do active record associations between these two roles (not separate models).
I next looked at subclassing so I could do associations, but it has issues as people say Rails isn't really meant to subclass, and it seems a bit hacky.
So I'm left feeling like I have to choose the lesser of evils, whereas I really just want to find the right way.. Thanks in advance for any help.

So a friend of mine solved this very elegantly for me, for everyones reference:
Good question. It’s a great problem that deals with the intersection between good engineering (model implementation, database design) and user experience (single sign in form).
Assuming that Employees and Employers differ enough, it makes sense to implement them as separate models. But it also makes sense to have a single sign in form—employees and employers shouldn’t have to care that they’re signing into the right form.
Single table inheritence usually appears to be the ideal solution, but tends to best be avoided in Ruby on Rails applications unless absolutely necessary.
I’ve actually thought about this problem before, so I would suggest an implementation along these lines:
An Employer model.
An Employee model.
A SignIn/Login/Credentials/WhateverYouWantToCallIt model.
In terms of employer/employee associations, as before:
Employee belongs_to :employer
Employer has_many :employees
Now, considering that both models are able to sign in, it makes sense to separate these credentials into their own SignIn model. If you do some reading up on polymorphic associations (http://guides.rubyonrails.org/association_basics.html#polymorphic-associations), you’ll find that they are awesome for creating relationships where the association can be with different models.
So now you need to create associations between sign in credentials and employers and employees:
SignIn :belongs_to :signinable, polymorphic: true
Employer has_one :sign_in, as: :signinable
Employee has_one :sign_in, as: :signinable
The elegance of this solution (in my opinion), is that you’re able to separate your SignIn, Employer and Employee models, which not only conforms to good Ruby on Rails conventions, but is good database normalisation practice. At the same time, you have a SignIn model that makes it trivial to implement a better sign in form experience that allows both employers and employees to sign in.

Related

How can you set up a multi-tenant Rails app without using subdomains?

I'm trying to create a SAAS e-commerce tool with a backend for staff that also allows customers to have accounts and checkout on the front end. I'm struggling with how to design this so that the Company, Account Owners, Staff, and Customers are all siloed off to each Company, while also having the appropriate restrictions based on their roles.
From what I've read so far most of the rails solutions use multi-tenant patterns with subdomains, such as the Apartment gem, to silo off accounts. But it seems simpler to just have your site use one big app and database. For instance Basecamp recently switched to this approach with Basecamp3. Newer apps seem to be built this way.
And, should the admin features and the customer accounts / front end shop be separate apps completely, or can you do this with a "majestic monolith"? One big app and database, while large, seems more straight forward to me.
I found this blog post that explains how to do something like this with Pundit, but I'm still having trouble groking the big picture of how this could work with Account Owners, Staff, and Customers all in the same app.
Here are the basic needs for my app:
User Roles
Account Owner (creates the company's account and has full access to their company's data)
Staff (invited to join a company and doesn't have access to some of the company's data, such as billing information)
Customer (can sign up for the site and view products, add the them to cart, but can't access any of the staff or account owner features.)
All Users (no matter the role) belong to a Company and can't access another company's data. (Thus providing the the ability to run separate stores on the same app, which is needed to run this as a SAAS app.)
Account Owners and Staff can CRUD Products, but not Customers.
A great analogy would be how Shopify's admin area and customer accounts currently work for shop owners, but unlike Shopify, it doesn't require using subdomains.
Potential Models and Associations
Company
has_many :users, dependent: :destroy
has_many :products, dependent: :destroy
User
belongs_to :company
Product
belongs_to :company
Authorization
Would it work to use Pundit to restrict the controller actions based on User roles and then ensure that data is siloed off via the Model associations?
Signup Flow
I'm a little fuzzy on how to handle scoping the different User roles and where the "staff invites" and "customer" sign up could fit into a sign up flow.
Would this approach work?
Create separate controllers for "Account Owner Signup," "Staff Signup," "Customer Signup," and then embed my signup form into those views. (Using Clearance for authentication and would like to keep that if possible, but just augment it as needed).
Account Owner Signup: So if a someone signs up through the New Account Signup controller (with embedded authentication form) they would also create a Company.
Staff Invite: The Account Owner can create new Staff Users by inputing a Name and Email address. This creates a new User with the role of "Staff" (and thus cannot become Account Owners on another account). The new "Staff" user is sent an invite email that is basically password reset email inviting them to accept the invitation by creating a password.
Customer Signup: If someone signs up through the "Customer Signup" controller, they would automatically be given the user role "customer". Not sure how to set the Company ID in this case. (Pass the company_id as a hidden input on the customer sign up form?)
Is there a better way to design this type of app that I'm missing? Am I on the right track? I have no experience building something like this so any clues would be extremely helpful.
It seems like newer apps follow this type of pattern for multi-tenancy rather than subdomains.
You open with simple e-commerce site but the questions you're asking indicate that you're looking for something that's a little more complex :) You're on the right track.
The acts_as_tenant gem is worth a look. We use this now and it helps make sure your queries are all scoped appropriately.
I would also look at & evaluate rolify if you need to do roles (but don't rule out a boolean flag on your user as well).
I wouldn't rule out devise, but clearance is quite popular.
Using a subdomain might be unrealized work depending on the amount of effort, unless you need to actually use subdomains for vanity purposes (my.example.com vs example.com/my), you can do multi-tenancy without it.
I would consider separate controllers & namespacing for the different roles if their access varies wildly; you can also combine them into singular controllers using Pundit (but this could be unwieldy). You'll still want to use Pundit, however, Pundit can do things like scope the records a user should see.
You're on the right track and asking the right questions but the answers to all of these will depend on other questions (that you probably can't even answer right now).
I have a project where I'm doing what you noted (pundit to restrict data, acts_as_tenant to silo things) but as it develops certain patterns emerge that lead me down a different path. Namespacing admin, rather than doing admin checks inside the same controller for example; because if you re-write to an API you end up trying to make the same endpoint do different things and it's much cleaner to separate out the 2 endpoints behind a namespace & document the actual behavior in my opinion.

Parent/Child classes in Devise

I'm newish to Rails and working on a project that I would like to create a parent/child relationship using devise. I've looked through a good bit of the literature on Devise, but didn't see any clear cut way to do what I'm trying to accomplish (though it's certainly possible I overlooked the answer because I was using different terminology).
Here's what I'd like to do:
Companies sign up using the normal devise registration flow with a little extra information - i.e. Company name, address, etc. They then add 'users' once inside the application. Users shouldn't have an external sign-up flow. I would like to each user to inherit the company information from Company.
Here is what I thought I'd try:
Generate both the Company and the User devise models.
Generate the User controller, modify the new action to #current_company.user.build
Would this accomplish what I'm trying to do?
Any further reading that you might be able to pass along would be appreciated!
Do this....
#app/models/company.rb
class Company < ActiveRecord::Base
has_many :users
end
#app/models/user.rb
class User < ActiveRecord::Base
devise ....
belongs_to :company
end
This is a has_and_belongs_to_many association.
What you should be doing is leaving companies to be its own model; don't include Devise on it at all.
Instead, you should make your User model the only one with the authentication. After that, each user will have to be related to a particular company (or if you wanted to use has_many :through, you could have a user a member of many companies).
You'd have to scope the Devise authentication around a company (IE company.users.exists? user); I've been working on that but don't have a thorough implementation yet.
There's a good ref here: Scope login to subdomain
This will allow you to have users for a particular company; which you'll be either be able to scope using subdomains (to make a crude "multi tenant" app), or to have a select box at sign-in or something.
Obviously, your lack of input means I've got to write a broad answer.
If I were you, I'd think about your desired flow.
Research multi tenancy with Rails (the apartment gem is good if you're using PGSQL) - this should give you a better idea on the way the application should work (IE how does a company need to support users).

Validations based on session data, sharing data between subdomains

We're building an application for product support. The idea is to have multiple subdomains, each for supporting other organizations products. We call this Account - each account is tied to exactly one subdomain.
Users also have roles - but, user can have one role on account1, and other role on account2.
Basically, there are two problems:
1) many validations are based on the role current user has. Since it depends on current_account (which is session data), I cannot do these kinds of validations in the model. This leads me to some ugly controller code (ugly, in the sense that it really feels out of place). I thought of storing current_account after in the model class variable, but I read that this is not thread safe. Any recommendations?
2) almost every database record is specific to the current account; so, almost every table should have an account_id column and the model should have a belongs_to account association. I want to avoid that. The first (obvious) thing is to have a seperate database for every account, but
a) there are shared tables
b) the boss says this solution is unacceptable (there will be many accounts, with relatively low number of users). Is there a third way?
If anyone runs into similar problem:
The problem described is called multitenancy; understanding default_scope should help. Also, there is the multitenant gem which worked for me nicely.

Modeling Buyers & Sellers in a Rails Ecommerce App

I'm building a Rails app that has Etsy.com style functionality. In other words, it's like a mall. There are many buyers and many sellers.
I'm torn about how to model the sellers. Key facts:
There won't be many sellers. Perhaps less than 20 sellers in total.
There will be many buyers. Hopefully many thousands :)
I already have a standard user model in place with account creation and roles.
I've created a 'role' of 'seller', which the admin will manually apply to the proper users. Since we'll have very few sellers, this is not an issue.
I'm considering two approaches:
(1) Create a 'store' model, which will contain all the relevant store information. Products would :belong_to :store, rather than belonging to the seller. The relationship between the user and store models would be: user :has_one store. My main problem with this is that I've always found has_one associations to be a little funky, and I usually try to avoid them. The app is fairly complex, and I'm worried about running into a cascade of problems connected to the has_one association as I get further along into development.
(2) Simply include the relevant 'store' information as part of the user model. But in this case, the store-related db columns would only apply to a very small percentage of users since very few users will also be sellers. I'm not sure if this is a valid concern or not.
It's very possible that I'm thinking about this incorrectly. I appreciate any thoughts.
Thanks.
I would definitely use a relationship between a store and a user. This provides a lot more flexibility and is a much cleaner data design.
I have never had any issues using any of the basic associations in Rails/Active Record.
What do you mean by "funky"?

Ok use for Single Table Inheritence?

I have the need for a user and a contacts model, both have common attributes like first name, last name, sex etc. It thus seemed reasonable to create a model individual and have user and contacts inherit from that model.
class Individual < ActiveRecord::Base
end
class User < Individual
end
class Contact < Individual
end
My question is, what type of security problems would I open up by having the above setup? I'm using authlogic and all of its required fields are within the individuals table. The above method seems easy, and yet I'm worried I may open myself up to unforeseen security issues, any thoughts? Just to be clear a user can login and a user will have many contacts.
Using STI doesn't open you up to any new security issues as they are orthogonal..
However, STI may upset your DBAs as you will have null fields within that table. (attributes that are present in one sub classed model and not the other)
As from the design stand point, what are you achieving by having such a model relationship. If the only distinction between a User and a Contact is that a User can log in and can have numerous contacts then you can simply have an individual that can have many contacts (which map to Individuals) AND can possibly have login credentials
Remember that a contact may be shared by numerous individuals which stipulates either a has_and_belongs_to_many or has_many :through
hth
Conceptually I wouldn't mix them. The underlying intent/function of each model is substantially different, so the fact that they look similar in shape is a red-herring.
Potentially you'd run into a problem where someone could figure out a way to authenticate with a Contact, although it doesn't seem very likely, given its unlikely that you assign a username to a contact, or the secret hash stuff. But it would be possible if there is a bug in AuthLogic, or if someone tried hard enough to hack your setup and you weren't anal about protecting against injection attacks.
Also, there are performance reasons to not do this if your app grows a lot. Basically, I think its a bad design to have these two models rely on the same table for fairly superficial reasons.

Resources