1

I'm trying to build a multi tenanted app in which which different banks are separated by subdomain. This part is working fine. Now there is one more level of multitenancy for bank products.

  • Each bank has multiple products
  • A devise user can belong to only on product
  • This means that you will have to register twice for two products of the same bank even though they are under same subdomain(client requirement can't change)
  • Because of this you can have same email address for two products. Uniqueness is scoped to product_id
  • So I have to select a product while signing in and signing up

This is how I'm trying to implement above solution

around_filter :scope_current_bank, :scope_current_product
before_filter :authenticate_user!

helper_method :current_bank, :current_product

def current_bank
  @current_bank = Bank.find_by_subdomain!(request.subdomains.first)
end

def current_product
  if user_signed_in?
    @current_product = current_bank.products.find_by_id(params[:product_id])
  else
    @current_product = current_user.product
  end
end

def scope_current_bank
  Bank.current_id = current_bank.id
  yield
ensure
  Bank.current_id = nil
end

def scope_current_product
  Product.current_id = (current_product.id rescue nil)
  yield
ensure
  Product.current_id = nil
end

Now the problem is while user is sigining in, the scope_current_product method calls user_signed_in?, obviously it fails because product_id is nil. Now it enters the else block after which I expect it to call authenticate_user! as its a before_filter but it does not happen as authentication was already done. So I get a message saying authentication failed.

Is their any way to call authenticate_user again?

Super Engineer
  • 1,966
  • 1
  • 13
  • 21

1 Answers1

1

Although not a direct answer, hopefully this will give you some ideas:


Authorization

Perhaps you should look at - Is there a difference between authentication and authorization? - there's a good RailsCast about this

I think your issue comes down to the idea you need to authenticate the user once (login / logout), but should then authorize that user to work with different resources


Code

A devise user can belong to only on product - I would recommend this:

#app/models/product_user.rb
Class ProductUser < ActiveRecord::Base
    belongs_to :product
    belongs_to :user
end

#app/models/product.rb
Class Product < ActiveRecord::Base
    has_many :product_users
    has_many :users, through: :product_users
end

#app/models/user.rb
Class User < ActiveRecord::Base
    has_many :product_users
    has_many :products, through: :product_users
end

This is a typical has_many :through association:

@user.products
@product.users

CanCan

It means you can use CanCan to do something like this:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)
    if user
      can :manage, Product, users.exists?(user.id)
    else
      can :read, :all
    end
  end
end

This allows you to control which products the user can edit / access. Obviously my code needs to be tweaked, but I hope it shows you the value of authorization over trying to do multiple authentications

Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • But as I have mentioned, A user can't have multiple accounts. For you to have two products, you actually need to create two separate accounts. So this approach will not work. – Super Engineer Mar 10 '14 at 10:06
  • I was suggesting you only need one account. The account credentials will then be able to be applied across the application using Join tables etc. Your idea of authenticating twice is bloated & inefficient. Just because users have to sign up twice doesn't mean they have to be authenticated twice. It just means you hold their Devise user details in a central `users` db, and then create other data silos for those users - as I've demonstrated using a `ProductUser` join model – Richard Peck Mar 10 '14 at 10:11
  • What I was suggesting is like how Facebook's `oAuth` flow works - you have one account but "allow" third party apps to work – Richard Peck Mar 10 '14 at 10:12
  • But this means that you need something which can uniquely map two user accounts. For example an email address. Also if I'm not wrong that user will have to use same password for all of its products. Which I don't think is acceptable for the client. I want to do it the same way that you suggested but I can't. +1 for suggesting a different approach to the problem. – Super Engineer Mar 10 '14 at 10:15
  • Would you rather chat about it? You must remember I only have what you've posted on here -- I don't know anything about the client's specs or security requirements :) – Richard Peck Mar 10 '14 at 10:17
  • Really appreciate the help you are offering. You can ping me on akshayshinde7@gmail.com. I'm interested in knowing how you will tackle this problem. – Super Engineer Mar 10 '14 at 10:24
  • Thanks - let me answer some questions & I'll send an email through!! – Richard Peck Mar 10 '14 at 10:26