This was the initial version of implementing subdomains using a BaseCamp like approach. While it is functional, I really didn’t get everything quite right. Quite right is really up to your application. I have posted another repository that cleans up a few thing and adds Devise Invitable to implement invitations and CanCan to do some basic authorization. Pick your poison! rails3-Devise-BCSD-Can-Invite.
Use this project as a starting point for a Rails 3 application that uses basecamp-like subdomains and authentication. User management and authentication is implemented using Devise.
This is a stab of modifying Rails3-Subdomain-Devise to provide basecamp-like subdomains.
Thanks to babinho’s fork for cleaning up some things up and going back to a subdomain id instead of name as the primary key.
The major changes are:
Routes and associations were modified:
devise_for :users resources :users, :only => [:index, :show] do member do get :valid end end resources :subdomains, :only => [:index, :show] constraints(SubdomainRoute) do match '/' => 'sites#index' match '/opps' => 'sites#opps' end root :to => "home#index"
User and subdomains are no longer nested, but associated.
class Subdomain < ActiveRecord::Base
has_many :users
validates_uniqueness_of :name, :case_sensitive => false
validates_presence_of :name
end
class User < ActiveRecord::Base
belongs_to :subdomain
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :name
validates_presence_of :subdomain_name, :on => :create # used to create a subdomain
validates_uniqueness_of :email, :case_sensitive => false
attr_accessor :subdomain_name # used to create a subdomain
attr_accessible :name, :subdomain_name, :email, :password, :password_confirmation, :loginable_token
before_create :create_subdomain
after_create :update_subdomain_owner
def self.valid?(params)
token_user = self.where(:loginable_token => params[:id]).first
if token_user
token_user.loginable_token = nil
token_user.save
end
return token_user
end
private
def create_subdomain
# get or create a subdomain on creating a new user
self.subdomain = Subdomain.find_by_name(self.subdomain_name)
self.subdomain ||= Subdomain.create!(:name => self.subdomain_name)
end
def update_subdomain_owner
# set owner of subdomain to user that created it
subdomain = self.subdomain
if subdomain && subdomain.user_id.nil?
subdomain.user_id = self.id
subdomain.save
end
end
end
The majority of the subdomains controller has been stubbed in that “sign_up” will create a subdomain if it does not exist. Code was not removed in that an Admin user might want to delete subdomains and associated users.
The model names were not changed, but take on different roles. List of other changes:
- Users#index probably should not be there, but it lists all users
- Site would be the home for the site and currently list users for the subdomain
- Subdomain still has user_id and indicates who created the subdomain (assume subdomain admin)
- A helper method “current_subdomain” was added to controllers/application.rb to check if subdomain exists
- Another helper method “check_my_domain(subdomain)” will check a passed subdomain against the current domain. Subdomain is kind of the root table, all major tables should be belong to subdomain and this check is there to prevent url modification (edit member not belonging to your domain). It will redirect to an “opps” action in the site controller.
Sign-in has been removed if no subdomain exists. If you modifiy the url and sign_in without a subdomain. It will log you in, but then immediately log you out and redirect to the subdomain sign_in form. I can’t seem to get the flash notice to work in this area.- If you sign-in to the root domain.
- A token is saved in the users record. You are signed_out of the root domain and redirected a user/valid action in the subdomin.
- The valid action will check the token and if found, will clear the toke and sign-in the user to the subdomain.
- If you register without a subdomain, it will be created when the user is created. If it exists, you are added as a user of that subdomain (TODO this should be fixed to reject adding user from sign_up without subdomain, if it exists)
- If you register in a subdomain, you are added as a the users of that subdomain.
P.S. I am not a novice at Rails, but don’t consider myself experienced.
Installation
- git clone
- bundle install
- rake db:create
- rake db:migrate
- rake db:seed
Please visit Rails3-Subdomain-Devise for the excellent step-by-step tutorial. You should still be able to follow the tutorial for this fork if you address the above changes.
Version (fork) that uses Mongodb with Mongoid by millisami at http://github.com/millisami/rails3-subdomain-devise
This work is a compilation and derivation from other previously released works. With the exception of various included works, which may be restricted by other licenses, the author or authors of this code dedicate any and all copyright interest in this code to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this code under copyright law.