0
votes

I'm making an app that uses Apartment for multi-tenancy and Devise for user authentication.

Initially, an instance of Tenant would be created, then would redirect to subdomain.lvh.me/users/sign_up so a user can sign up and only be a part of the schema for that particular subdomain. This way users are only authorized to login on the subdomain that they initially signed up on, which is what I want. It was also beneficial because I set every User with id=1 to admin, so every time a new tenant is created, it redirects to the subdomain and the first user to sign up would be the default admin (made sense because user would start on landing page, create company account (tenant), and then create a User account with their name, email, password etc right after and they would be the first user associated with that tenant and therefore be the default admin.

This worked well because the person coming from the landing page (imagine the head of a team - director of marketing or whatever) could sign up for the product and set the company information as well as be the first user with administrator capabilities. Every employee on the team would sign up on the subdomain and wouldn't be authorized to login on another subdomain, or they would have to sign up on the landing page in which case they would be treated like a new company is signing up and not just a user.

Recently I implemented nested attributes so that I could have the initial sign-up process completed in one form/view only instead of two. The problem is because the instance of User is being created in the Tenant controller, it is not a part of the schema for the company subdomain and therefore cannot login on subdomain.lvh.me/users/sign_in.

I'm wondering if anyone has run into this challenge before and what your solution was. Would it be better to change it back to how it was originally, creating Tenant first, redirecting to subdomain/sign_up and then creating the first User?

Sorry if I didn't articulate this as well as I could have.

I've tried redirecting to the sub-domain before calling @tenant.users.build but it didn't work like I thought.

part of tenants_controller.rb

def new
    @tenant = Tenant.new
    @tenant.users.build
    render layout: false
end

def create
    @tenant = Tenant.new(tenant_params)
    @tenant.update_attribute :subdomain, @tenant.company.downcase
    respond_to do |format|
      if @tenant.save 
        format.html { redirect_to "http://#{@tenant.subdomain}.lvh.me:3000/users/sign_in", notice: 'Domain was successfully created.' }
        #format.html { redirect_to new_user_registration_path, notice: 'Tenant was successfully created.' }
        format.json { render :show, status: :created, location: @tenant }
      else
        format.html { render :new }
        format.json { render json: @tenant.errors, status: :unprocessable_entity }
      end
   end
end
1
What do you mean "be a part of the schema for that particular subdomain" - Kartikey Tanna
Maybe i dont understand enough to word it properly but from what i've read each subdomain has a separate db schema which is why before i nested the attributes you could create an account on one subdomain and it wouldn't have authorization for any other subdomains - user2570506

1 Answers

0
votes

I'm not sure how your app and apartment is configured. I had it set up that a tenant (in my case workspace) belongs to an owner (its class is User). Here is an excerpt.

controllers/workspace_controller.rb

class WorkspacesController < ApplicationController
  def new
    @workspace = Workspace.new
    @workspace.build_owner
  end

  def create
    @workspace = Workspace.new(workspace_params)
    respond_to do |format|
      if @workspace.valid?
        Apartment::Tenant.create(@workspace.subdomain)
        Apartment::Tenant.switch(@workspace.subdomain) do
          @workspace.save
        end
        format.html { redirect_to new_user_session_url(subdomain: @workspace.subdomain), notice: t("flash.controllers.#{controller_name}.#{action_name}.success") }
      else
        format.html { render :new }
      end
    end
  end
end

models/workspace.rb

class Workspace < ApplicationRecord
  belongs_to :owner, class_name: 'User', required: true
  accepts_nested_attributes_for :owner
end

The form is built with workspace and owner fields. If validation passes a new schema is created with Apartment::Tenant.create(@workspace.subdomain). Then we wrap the actual creation of the tenant in the Apartment switch Apartment::Tenant.switch(@workspace.subdomain) do … Everything in that block will be saved in the schema subdomain except whatever you have assigned to config.excluded_models in your Apartment config. In my case the user gets saved into 'subdomain'.'users' and the workspace into public.'workspaces'.

Don't hardcode your redirect destination. You can pass the subdomain to the url-helper.