jekuno / milia Goto Github PK
View Code? Open in Web Editor NEWEasy multi-tenanting for Rails5 (or Rails4) + Devise
License: MIT License
Easy multi-tenanting for Rails5 (or Rails4) + Devise
License: MIT License
Everything seems to work otherwise, but no email send is attempted in the logs.
Running in development mode my config/environments/development.rb has:
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => "587",
:authentication => :plain,
:user_name => ENV["SMTP_EMAIL"],
:password => ENV["SMTP_PASSWORD"],
:enable_starttls_auto => true
}
I've followed the readme and tried running the test app but I'm stuck. Shouldn't it start by registering a new tenant?
I recently ran into an issue where I wasn't sure if my validations against a tenanted model was working correctly.
When I was using the following code:
validates_uniqueness_of :name
The model would not save a record with a name that had an identical name to another record which was under a different tenant. So for example if I had a record were name = joe and tenant_id = 1. Then I went in under tenant #2 and tried to save another record with name = joe, it would not save and say the name had to be unique. I ended up using the following validation to fix it.
validates_uniqueness_of :name, :scope => :tenant_id
My question is just is this the correct way to handle unique validation with milia, or did I bork something in my application and shouldn't need the :scope => :tenant_id part? If I did screw something up, do you have any pointers on where to start looking?
FYI, everything else "seems" to operate as expected. Here is the first part of my model code just for your info.
class Plan < ActiveRecord::Base
#sets up model as a tenant in milia
acts_as_tenant
has_many :donations
attr_accessible :stripe_plan_id, :name, :summary, :title, :amount
validates :amount, presence: true
validates_uniqueness_of :name, :scope => :tenant_id, :case_sensitive => false
validates :name, presence: true
validates :stripe_plan_id, presence: true, uniqueness: { case_sensitive: false }
validates :summary, presence: true
validates_uniqueness_of :summary, :scope => :tenant_id, :case_sensitive => false
Second FYI, I was installing the AASM gem at the time too (though I did uninstall AASM to make sure it wasn't causing the issue). I saw you had AASM experience too and just wondered if you had seen any conflicts between AASM and Milia.
Thanks, any of your thoughts will be appreciated.
Hi,
Great gem btw!
My question is about setting up subdomains for Milia, mainly the steps I should follow to get that implemented correctly ?
Thanks in advance!
Hello,
When do you plan to release milia to be fully Rails 4.1 compatible?
We are upgrading to latest version of Rails, and the next step of our application will be to find a suitable multi tenancy gem. Currently, a con with this gem is the current Rails 4.0.x dependency.
Since this dependency is due to the web-app-theme, namely rails g web_app_theme:milia
, and I plan to incorporate this into an existing application (not running the command at all) then I should be able to safely ignore the 4.0.x dependency by forking that gem and loosening the gemspec as per #34 - Am I correct?
Best regards,
Mathias
This is not so much an issue as a plea for guidance! We have a nicely working multi-tenanted site, but, how can we write back-end admin tools that let us see data across all tenants? Something like rails_admin.
Has anyone done this or can point to what to do?
Maybe adding a user to all tenants?
Tips appreciated!
Hello,
I couldn't seem to find where the option to disable Recapcha was.
Also, I didn't find the location of the ENV variables in the documenation. I assumed maybe an application.yml?
Any help appreciated.
Hi,
I'm trying to create an administration page where I can see all of the users using my app.
I have created a controller with privileges only for me, and I have a regular index view.
In this controller I can access the users by using '@users = User.all' but when I try to get to the tenant (to show it's ID and name), I can't do that. Any suggestions how can I access all the tenants from this admin controller?
Thanks!
Hello,
I'm using:
devise (2.2.4)
milia (0.3.30)
Rails 3.2.12
ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-darwin10.8.0]
I've tried to follow your tutorial, but upon user creation I get:
uninitialized constant Control
below the log:
Started POST "/users" for 127.0.0.1 at 2013-06-06 12:02:49 +0200
Processing by Devise::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8uZpO5HRLvkhxsrhUfZzq3i710po/AnngI1VZJeOZ70=", "user"=>{"username"=>"", "email"=>"[cut]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
(0.1ms) begin transaction
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = '[cut]' LIMIT 1
SQL (0.7ms) INSERT INTO "tenants" ("created_at", "name", "tenant_id", "updated_at") VALUES (?, ?, ?, ?) ["created_at", Thu, 06 Jun 2013 12:02:49 CEST +02:00], ["name", :username], ["tenant_id", nil], ["updated_at", Thu, 06 Jun 2013 12:02:49 CEST +02:00] rollback transaction
Completed 500 Internal Server Error in 70msNameError (uninitialized constant Control):
milia (0.3.30) lib/milia/base.rb:61:inblock in acts_as_universal' activesupport (3.2.8) lib/active_support/callbacks.rb:440:in
_run__3560432618853441863__save__2851447896423817079__callbacks'
activesupport (3.2.8) lib/active_support/callbacks.rb:405:in__run_callback' activesupport (3.2.8) lib/active_support/callbacks.rb:385:in
_run_save_callbacks'
activesupport (3.2.8) lib/active_support/callbacks.rb:81:inrun_callbacks' activerecord (3.2.8) lib/active_record/callbacks.rb:264:in
create_or_update'
activerecord (3.2.8) lib/active_record/persistence.rb:84:insave' activerecord (3.2.8) lib/active_record/validations.rb:50:in
save'
activerecord (3.2.8) lib/active_record/attribute_methods/dirty.rb:22:insave' activerecord (3.2.8) lib/active_record/transactions.rb:241:in
block (2 levels) in save'
activerecord (3.2.8) lib/active_record/transactions.rb:295:inblock in with_transaction_returning_status' activerecord (3.2.8) lib/active_record/connection_adapters/abstract/database_statements.rb:192:in
transaction'
activerecord (3.2.8) lib/active_record/transactions.rb:208:intransaction' activerecord (3.2.8) lib/active_record/transactions.rb:293:in
with_transaction_returning_status'
activerecord (3.2.8) lib/active_record/transactions.rb:241:inblock in save' activerecord (3.2.8) lib/active_record/transactions.rb:252:in
rollback_active_record_state!'
activerecord (3.2.8) lib/active_record/transactions.rb:240:insave' devise (2.2.4) app/controllers/devise/registrations_controller.rb:15:in
create'
actionpack (3.2.8) lib/action_controller/metal/implicit_render.rb:4:insend_action' actionpack (3.2.8) lib/abstract_controller/base.rb:167:in
process_action'
actionpack (3.2.8) lib/action_controller/metal/rendering.rb:10:inprocess_action' actionpack (3.2.8) lib/abstract_controller/callbacks.rb:18:in
block in process_action'
activesupport (3.2.8) lib/active_support/callbacks.rb:458:in `_run__4479752414565192415__process_action__2670286493514513815__callbacks'
[cut].....
Thanks for the awesome gem, it has helped us to bootstrap our app with multitenancy. This is ok with our initial development but we would need this to be supported across multiple databases/schemas. Do you have any plan to develop Milia on this line in near future?
We have integrated milia in our App and while testing the member invitation, we found the member is also allowed to invite new members.
Do we need to restrict this from our application or can this be handled in your gem itself.
Using Milia 1.0.0 with Rails 4.1.5. The "invite user" method sends out an email with a confirmation link like:
http://localhost:3000/users/confirmation?confirmation_token=RxCpy4xz955ymQmL3txK
Clicking this link generates an error:
ActionView::MissingTemplate (Missing template milia/confirmations/show, devise/confirmations/show, devise/show, application/show with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee, :haml]}.
I don't see a confirmations#show view anywhere in milia or devise. Looking at the source for https://github.com/dsaronin/milia/blob/master/app/controllers/confirmations_controller.rb I see this comment at line 56:
# else fall thru to show template which is form to set a password
# upon SUBMIT, processing will continue from update
Where should the confirmations#show template be coming from?
I am trying to use milia with a new rails 4 project using the devise rails4 branch. When sign_up is called I am getting the following stack trace. I know it is due to the new strong attributes in Rails 4 - just not sure the best place to fix it.
ActiveModel::ForbiddenAttributesError - ActiveModel::ForbiddenAttributesError:
(gem) activemodel-4.0.0.beta1/lib/active_model/forbidden_attributes_protection.rb:21:in `sanitize_for_mass_assignment'
(gem) activerecord-4.0.0.beta1/lib/active_record/attribute_assignment.rb:21:in `assign_attributes'
(gem) activerecord-4.0.0.beta1/lib/active_record/core.rb:174:in `initialize'
(gem) activerecord-4.0.0.beta1/lib/active_record/inheritance.rb:24:in `new'
/home/ed/.rvm/gems/ruby-2.0.0-rc2/bundler/gems/devise-ce37c301ff91/lib/devise/models/registerable.rb:20:in `new_with_session'
/home/ed/.rvm/gems/ruby-2.0.0-rc2/bundler/gems/devise-ce37c301ff91/app/controllers/devise/registrations_controller.rb:87:in `build_resource'
/home/ed/work/cluey/milia/app/controllers/registrations_controller.rb:72:in `devise_create'
/home/ed/work/cluey/milia/app/controllers/registrations_controller.rb:29:in `block in create'
(gem) activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/database_statements.rb:201:in `block in transaction'
(gem) activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/database_statements.rb:209:in `within_new_transaction'
(gem) activerecord-4.0.0.beta1/lib/active_record/connection_adapters/abstract/database_statements.rb:201:in `transaction'
(gem) activerecord-4.0.0.beta1/lib/active_record/transactions.rb:209:in `transaction'
/home/ed/work/cluey/milia/app/controllers/registrations_controller.rb:23:in `create'
Hi, dsaronin. Sorry I am late for your updates. I am using 0.0.3 version milia in my app and I love it, although sometimes it breaks when it is facing devise_invitable and devise. After I came to know that you have updated it, I was quite happy and found that you have did a fantastic work again.
I could not find time to build a new app to test for you, but I would like to use the new version (currently 1.0.0-beta3) in my existing app which means, I have to know exactly what would be different between the old and the new, especially the database part.
Thanks.
in my tenant I have put the following line:
has_many :monthly_order_summaries, dependent: :destroy
When I destroy the tenant I see this in my logs:
MonthlyOrderSummary Load (0.2ms) SELECT
monthly_order_summaries.* FROM
monthly_order_summaries WHERE (monthly_order_summaries.tenant_id = NULL) AND
monthly_order_summaries.
tenant_id = 16
Obviously this will not work - it is ANDing on two tenant_id selects.
The model looks like this:
class MonthlyOrderSummary < ActiveRecord::Base acts_as_tenant end
This happens if I do:
Tenant.last.destroy
undefined method `prep_signup_view' for #Milia::RegistrationsController:0xb403eef4
I followed the sample app with Rails 4.2 and Ruby 2.1.5 and when I get to the signup page and submit I see the following stack trace in the error.
There is no tenant param being passed, and it's apparently required. I made sure I didn't miss a step, it's a brand new database with nothing in it, it's a brand new app without any modifications to the step-by-step instructions other than what's mentioned above with Ruby and Rails.
Any advice?
11:24:29 web.1 | Processing by Milia::RegistrationsController#create as HTML
11:24:29 web.1 | Parameters: {"utf8"=>"✓", "authenticity_token"=>"ReCQQiaS3UchSGJRxf1BjLzi84wjGUoq6VFdIzYWgWmD5NyYFXFtteqMoSLA0/0GNMLwRth3+qLguBv3b50Byw==", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
11:24:29 web.1 | Completed 400 Bad Request in 2ms (ActiveRecord: 0.0ms)
11:24:29 web.1 |
11:24:29 web.1 | ActionController::ParameterMissing (param is missing or the value is empty: tenant):
Hi David,
First, thanks for your work on this gem. It works very well and is much appreciated.
I'm working on updating my app to the latest point release of Rails (3.2.17 at the moment) to pick up the security fixes. Milia v0.3.38 has a dependency on rails = 3.2.13
. Is there a reason this is not rails ~> 3.2.13
?
OS: Ubuntu 12.04 (running in Vagrant)
RVM in use
rails g milia:install --org_email='[email protected]'
/home/vagrant/code/sample-milia-app/config/environments/development.rb:1:in <top (required)>': undefined method
configure' for #SampleMiliaApp::Application:0xac0ce5c (NoMethodError)
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in require' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in
block in require'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:214:in load_dependency' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in
require'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/engine.rb:591:in block (2 levels) in <class:Engine>' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/engine.rb:590:in
each'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/engine.rb:590:in block in <class:Engine>' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/initializable.rb:30:in
instance_exec'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/initializable.rb:30:in run' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/initializable.rb:55:in
block in run_initializers'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:150:in block in tsort_each' from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:183:in
block (2 levels) in each_strongly_connected_component'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:210:in block (2 levels) in each_strongly_connected_component_from' from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:219:in
each_strongly_connected_component_from'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:209:in block in each_strongly_connected_component_from' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/initializable.rb:44:in
each'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/initializable.rb:44:in tsort_each_child' from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:203:in
each_strongly_connected_component_from'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:182:in block in each_strongly_connected_component' from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:180:in
each'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:180:in each_strongly_connected_component' from /home/vagrant/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/tsort.rb:148:in
tsort_each'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/initializable.rb:54:in run_initializers' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/application.rb:215:in
initialize!'
from /home/vagrant/code/sample-milia-app/config/environment.rb:5:in <top (required)>' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in
require'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in block in require' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:214:in
load_dependency'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in require' from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/application.rb:189:in
require_environment!'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p481@sample-milia-app/gems/railties-4.0.1/lib/rails/commands.rb:44:in <top (required)>' from bin/rails:4:in
require'
from bin/rails:4:in `
Title says it all really. My User model is set to acts_as_universal_and_determines_account
I'm trying to figure out how to know which users belong to which tenants (for other code).
Is this a problem with my configuration? Is there some other way to figure out this association?
I've downloaded the sample-milia-app and it is up and running on my local server (meaning I can see the root page page and I can get to the sign up view).
A problem that I am having is that I am just trying to sign up my first organization through the form. When I submit the form, the form just sits there. My logs say this is happening:
Started POST "/users" for 127.0.0.1 at 2014-01-08 01:18:39 +0000
Started POST "/users" for 127.0.0.1 at 2014-01-08 01:18:39 +0000
Processing by Milia::RegistrationsController#create as HTML
Processing by Milia::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Sy2yLxZ7Sjpmc0laQWspuwan9wQcb83l7Hs0BjcpNXo=", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "tenant"=>{"name"=>""}, "coupon"=>{"coupon"=>""}}
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Sy2yLxZ7Sjpmc0laQWspuwan9wQcb83l7Hs0BjcpNXo=", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "tenant"=>{"name"=>""}, "coupon"=>{"coupon"=>""}}
(81.7ms) BEGIN
(81.7ms) BEGIN
SQL (254.1ms) INSERT INTO "tenants" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Tue, 07 Jan 2014 17:18:39 PST -08:00], ["name", ""], ["updated_at", Tue, 07 Jan 2014 17:18:39 PST -08:00]]
SQL (254.1ms) INSERT INTO "tenants" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Tue, 07 Jan 2014 17:18:39 PST -08:00], ["name", ""], ["updated_at", Tue, 07 Jan 2014 17:18:39 PST -08:00]]
User Exists (83.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1
User Exists (83.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1
Rendered /home/action/.rvm/gems/ruby-2.0.0-p353@milia/gems/devise-3.2.2/app/views/devise/shared/_links.erb (0.4ms)
Rendered /home/action/.rvm/gems/ruby-2.0.0-p353@milia/gems/devise-3.2.2/app/views/devise/shared/_links.erb (0.4ms)
Rendered devise/registrations/new.html.haml within layouts/sign (5.8ms)
Rendered devise/registrations/new.html.haml within layouts/sign (5.8ms)
(82.9ms) ROLLBACK
(82.9ms) ROLLBACK
Completed 200 OK in 901ms (Views: 11.6ms | ActiveRecord: 831.1ms)
Completed 200 OK in 901ms (Views: 11.6ms | ActiveRecord: 831.1ms)
Devise doesn't seem to like something about the form, but it doesn't give me any errors or anything. Any clues as to what is missing? I am using beta 3.
Thanks.
When I tried to send invitation to a new user, I don't know how to pass the tenant_id
to the new user.
User acts as universal, so its tenant_id
would have to be set to nil
.
But when a user tried to validate its invitation_token
, rails could not find a user with the token and tenant_id => nil
.
I think the problem might lie in the tenant_user
table, could you tell me your suggestion?
Hey guys, when a try to save a new user an exception is raised.
undefined local variable or method `verify_recaptcha' for #Milia::RegistrationsController:0x007fd4e8bb86f8
I'm trying to sort out how to integrate dashing-rails, which provides a Rails engine of Shopify's Dashing dashboard framework, into a Milia-enabled app.
https://github.com/gottfrois/dashing-rails
In general, what should be required to use an engine with Milia? Setting the current tenant seems like an obvious requirement but I'm unsure of how or where to do that when using an engine. Any tips?
I'm trying to setup milia for the very first time and I got an error after submiting sign-up form:
ActionController::ParameterMissing in Milia::RegistrationsController#create
param not found: tenant
stack trace showed me the place and I check it with binding.pry
as well:
milia (1.0.0) app/controllers/registrations_controller.rb:82:in `sign_up_params_tenant':
def sign_up_params_tenant()
params.require(:tenant).permit( ::Milia.whitelist_tenant_params ) # <- line 82
end
I asked for help at stackoverflow as well. I would appreciate your advice.
I have a superuser which belongs to several tenants. Data such as first_name
and last_name
are kept in a separate members
table as recommended. I understand your explanations in https://github.com/dsaronin/milia#inviting-additional-usermembers as follows:
user
record for this superusertenants_users
to define the membership of this user to the according tenantsmember
record for this user for each tenant this user can access (this is because the members
table is tenanted; only the users
table isn't tenanted)However:
has_one :member
relation on the user
(and not a has_many
).first_name
and last_name
) n
times if the user belongs to n
companies.What is the recommended way to do it with milia?
I am getting this error when registering a new user, and self.create_new_tenant is called.
this is my form:
<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "form" }) do |f| %>
<%= f.input :email, label: false, placeholder: "email", :class => "text_field" %>
<br>
<%= f.input :password, label: false, placeholder: "password", :class => "text_field" %><br>
<%= f.input :password_confirmation, label: false, placeholder: "confirm password", :class => "text_field" %>
<br>
<%= simple_fields_for( :tenant ) do |w| %>
<%= w.input :name, label: false, placeholder: "Organization account", :class => "text_field" %>
<% end %><br>
<%= f.button :submit, 'create account', class: "btn btn-primary" %>
</div>
<% end %>
and the parameters being sent:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"WnT5KNuhV1E7UBvVY4zQ7BPA2KAkIjNr8ofyLx6fNrM=", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "tenant"=>{"name"=>"company_name"}, "commit"=>"create account"}
My webApp has api functions. get method with authentication_token after sign_in user app redirected to root_path. Is there any way to perform controller action with parameters that I need without redirection?
Started GET "/api/bonus_cards/balance?auth_token=v5E-eYKb27yZCOOOOOJJOOJJ&number=00001" for 127.0.0.1 at 2015-03-30 15:46:59 +0300
Processing by ApiController#balance as HTML
Parameters: {"auth_token"=>"v5E-eYKb27yZCOOOOOJJOOJJ", "number"=>"00001"}
User Load (4.1ms) SELECT "users".* FROM "users" WHERE (users.tenant_id IS NULL) AND "users"."auth_token" = 'v5E-eYKb27yZCOOOOOJJOOJJ' LIMIT 1
(0.3ms) BEGIN
SQL (0.6ms) UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = 12 ["current_sign_in_at", "2015-03-30 15:46:59.962394"], ["last_sign_in_at", "2015-03-30 15:46:59.103771"], ["sign_in_count", 49], ["updated_at", "2015-03-30 15:46:59.964500"] COMMIT
Tenant Load (15.0ms) SELECT "tenants".* FROM "tenants" INNER JOIN "tenants_users" ON "tenants"."id" = "tenants_users"."tenant_id" WHERE (tenants.tenant_id IS NULL) AND "tenants_users"."user_id" = $1 ORDER BY "tenants"."id" ASC LIMIT 1 [["user_id", 12]]
MILIA >>>>> [change tenant] new: 11 old: %
Tenant Load (0.4ms) SELECT "tenants".* FROM "tenants" INNER JOIN "tenants_users" ON "tenants"."id" = "tenants_users"."tenant_id" WHERE (tenants.tenant_id IS NULL) AND "tenants_users"."user_id" = $1 [["user_id", 12]]
Redirected to
Redirected to http://localhost:3000/
Completed 302 Found in 43ms (ActiveRecord: 20.9ms)
Started GET "/" for 127.0.0.1 at 2015-03-30 15:47:00 +0300
Processing by RestaurantsController#index as HTML
Completed 401 Unauthorized in 1ms
#application controller
def auth_by_token!
# special case for designated actions only
if ( user = User.find_by_auth_token( params[:auth_token] ) )
# create a special session after authorizing a user
reset_session
sign_in(user, store: false) # devise's way to signin the user
# now continue with tenant authorization & set up
true # ok to continue processing
else
act_path = controller_name.to_s + '/' + action_name.to_s
logger.info("SECURITY - access denied #{Time.now.to_s(:db)} - auth: #{params[:userfeed] }\tuid:#{(user.nil? ? 'n/f' : user.id.to_s)}\tRequest: " + act_path)
render( :nothing => true, :status => :forbidden) # redirect_back # go back to where you were
nil # abort further processing
end
end
and in ApiController I have
#ApiController
skip_before_action :authenticate_tenant!
prepend_before_action :auth_by_token!, only: [:balance]
protect_from_forgery
include within milia all the devise work-arounds for simply inviting a member into the organization. Needs to skip requiring initial password to create the user, but should require the newly confirming member to create a password.
This will be enabled by the setup config.use_invite_member = true option. When false, will fall-back to typical devise handling.
Hi there,
I'm really looking forward to the new milia gem with rails 4 and devise 3 support!
I was testing out the beta3 and ran into an issue, I am using the seed_fu gem to populate default tenant data in the following manner, which was working with Milia 0.3.30
e.g. in a single file
Tenant.set_current_tenant Tenant.where(:name => 'First').first
Domain.seed_once(:uri,
{:uri => 'testing1.com'},
{:uri => 'testing2.com'}
)
Tenant.set_current_tenant Tenant.where(:name => 'Second').first
Domain.seed_once(:uri,
{:uri => 'testing3.com', :primary => true}
)
Now it throws an
Milia::Control::InvalidTenantAccess (Domain.rb uses acts_as_tenant)
I get the same behaviour when trying this via the rails console, even though Tenant.current_tenant shows the correct one is selected, it fails exactly on base.rb:31 since the tenant_id check fails.
I went through the seed_fu src to see what it was doing,
It does a Record.new followed by assign_attributes then save which when run in that sequence in the console works just fine. confused :?
Hi Daudi, I cannot get "bundle install" to work as web-app-theme fails with rails 4.1.1 "Bundler could not find compatible versions for gem "rails":
In Gemfile:
web-app-theme (>= 0) ruby depends on
rails (~> 4.0.1) ruby
rails (4.1.1)
Any thoughts would be appreciated. My git repo is stuartn60/sample-milia-app.
Thanks, Stuart
This isn't really an issue but I curious to know why you have chosen to store the current tenant in the Thread.
Is this not the equivalent to a global variable?
Am I expect to maintain my own current tenant id in either the session or the user object and move it into the Thread variable at the beginning of each request?
I am trying to get 1.0.0-beta-3 installed right now, and with a fresh application I get this when I try a rails g milia:install --org_email='[email protected]':
<path>/milia-db26774ebc67/app/controllers/registrations_controller.rb:3:in `<module:Milia>': uninitialized constant Milia::Devise (NameError)
Is this something I am doing wrong or is there a bug somewhere?
Thanks.
Thanks for your work on this! I am using it for a couple of projects now.
When I add a new member for a tenant, a password is being generated and used for the user creation for devise. The email goes out with a confirmation link. When they click on the link, it automatically logs them in and puts them on the welcome screen.
I also see this in the code and of particular interest is the change_or_confirm_user and line 38: if ( @confirmable.skip_confirm_change_password || @confirmable.valid? )
In the situation I described, I would have thought this would have returned false and thus would hit the else statement on line 40. However it appears to be resulting in true and executing the do_confirm (which logs them in automatically). Yet my new member never gets to set up their own password via a show view that I build and your update method.
I may not be following the code correct but any guidance would be appreciated.
I checked out your sample application. Registering and signing in as a user works fine.
However opening the profile page (the page to edit the signed in user) at http://localhost:3000/users/edit produces an error in the sample app:
Logfile: MILIA >>>>> [failed auth user]
Flash error: cannot sign in as <email missing>; check email/password
This error already occurs on opening the page (without posting the form). In my own app I get the same error as in the sample app.
Do you have any ideas on how to fix this?
Hi,
I just installed and tried the new version of the app and it is working much better - thanks! One thing I did notice is that members can invite members, is this by design? I would think only organizational users should be able to invite members but maybe I am missing something. Thanks again!
Stephen
Did you test milia
with Rails 5 already or do you have plans to do so?
Hi, I followed the setup instructions and I used the Milia and web-theme generators. Everything works on my local copy, except heroku crashes when I navigate to the Sign Up page.
Is this a known issue?
Started GET "/users/sign_up" for 24.45.239.219 at 2014-03-09 16:45:45 +0000
Processing by Milia::RegistrationsController#new as HTML
Rendered devise/registrations/new.html.haml within layouts/sign (8.8ms)
Completed 500 Internal Server Error in 14ms
ActionView::Template::Error (First argument in form cannot contain nil or be empty):
%p= message
= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "form" }) do |f|
- flash.clear # clear contents so we won't see it again
= devise_error_messages!
= f.label :email, :class => "label"
.group
Hi,
I'm trying to set it up. I told the Tenant
model to be universal but the generated SQL shows up that it is looking for a tenant_id
on the Tenant
model:
Mysql2::Error: Unknown column 'tenants.tenant_id' in 'where clause': SELECT `tenants`.* FROM `tenants` WHERE `tenants`.`code` IS NULL AND (tenants.tenant_id IS NULL) LIMIT 1
app/models/tenant.rb
class Tenant < ActiveRecord::Base
acts_as_universal_and_determines_tenant
def self.default
Tenant.find_by_code 'foo'
end
end
I don't know if it is related, but I don't want a user-based tenant system but a subdomain-based one. So I skip what I think was not related from the README.
Is there any way to disable "acts_as_...." in the models all at once?
Hi Daudi - many thanks for all of the work you have put into your milia gem! I have been trying to integrate 1.0.0-beta3 into my own project and hit an awkward issue that had me scratching my head for a couple of days and I think has highlighted an issue in the milia registrations controller and prep_signup_view code.
I was getting an ActiveModel::ForbiddenAttributesError error on my sign up form if hit submit whilst leaving the "Organisation Name" field blank (leaving the user fields blank was fine). When I ran your sample application I noticed that you don't have this issue (although your coupon field value becomes a hash for some reason - i'm leaving the coupon out so ignored this).
To cut a long painful story short, I had added a validation to my tenant model for the 'name' field: -
class Tenant < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 80 }, uniqueness: { case_sensitive: false }
I think it reasonable to want to add some validation on the tenant to ensure uniqueness, presence and length etc. This leads to a problem in the milia registrations controller. If the tenant does not save properly then the form is re-displayed by your registration controller: -
Tenant.transaction do
@tenant = Tenant.create_new_tenant(sign_up_params_tenant, sign_up_params_coupon)
if @tenant.errors.empty? # tenant created
initiate_tenant( @tenant ) # first time stuff for new tenant
devise_create # devise resource(user) creation; sets resource
if resource.errors.empty? # SUCCESS!
# do any needed tenant initial setup
Tenant.tenant_signup(resource, @tenant, params[:coupon])
else # user creation failed; force tenant rollback
raise ActiveRecord::Rollback # force the tenant transaction to be rolled back
end # if..then..else for valid user creation
else
prep_signup_view( @tenant, params[:user] , params[:coupon]) # PROBLEM
render :new
end # if .. then .. else no tenant errors
By adding a validation on the tenant I caused the tenant save to fail we get sent through prep_signup_view before the form is re-rendered. You are passing the tenant object instance variable but passing the params[:user] hash. The params hash does not have any strong parameter whitelisting (permits etc.) on it and so causes the ForbiddenAttributesError when klass_option_obj calls 'new' on User using this hash.
Further down the controller you call prep_signup_view like this: -
prep_signup_view( sign_up_params_tenant, sign_up_params, sign_up_params_coupon )
This is great because it preps the form by using the permitted strong parameter versions of the parameters but has the downside that all error messages are lost. The errors are lost because prep_signup_view recreates the @user, @Tenant and @coupon instance variables as new from the params hashes.
I have tested this modification to the registration create method which fixes the strong parameter issue and makes sure that ALL validation errors are available to the form: -
def create
sign_out_session!
build_resource(sign_up_params) # added by dave
prep_signup_view( sign_up_params_tenant, sign_up_params, sign_up_params_coupon ) # added by dave
# validate recaptcha first unless not enabled
if !::Milia.use_recaptcha || verify_recaptcha
Tenant.transaction do
@tenant = Tenant.create_new_tenant(sign_up_params_tenant, sign_up_params_coupon)
if @tenant.errors.empty? # tenant created
initiate_tenant( @tenant ) # first time stuff for new tenant
devise_create # devise resource(user) creation; sets resource
if resource.errors.empty? # SUCCESS!
# do any needed tenant initial setup
Tenant.tenant_signup(resource, @tenant, params[:coupon])
else # user creation failed; force tenant rollback
raise ActiveRecord::Rollback # force the tenant transaction to be rolled back
end # if..then..else for valid user creation
else
resource.valid?
#prep_signup_view( @tenant, resource, sign_up_params_coupon ) # removed by dave
render :new
end # if .. then .. else no tenant errors
end # wrap tenant/user creation in a transaction
else
flash[:error] = "Recaptcha codes didn't match; please try again"
resource.valid?
@tenant.valid?
#prep_signup_view( @tenant, resource, sign_up_params_coupon ) # removed by dave
render :new
end
end # def create
By placing prep_signup_view at the top - it makes sure that all of the instance variables are created unless they already exist.
I needed to call devises build_resource function and the calls to valid? so that all form objects get validated, even if there are recaptcha errors or tenant problems. The form in your sample app doesn't appear to display any error messages.
One last change, it would be good to remember the tenant name even if there are errors with the user object validations, e.g. password_confirmation does not match. Instead of creating a new tenant form entry each time, you could use the existing one if already defined: -
.group
= fields_for( :tenant, @tenant || Tenant.new ) do |w|
= w.label( :name, 'Organization', :class => "label" )
= w.text_field( :name, :class => "text_field")
%span.description unique name for your group or organization for the new account
This is a little ugly so it might be better to just use fields_for( @Tenant ) and add your prep_signup_view function to the devise controllers #new method (otherwise @Tenant will by nil the first time the form is called).
Thanks for having a look through my issue - I hope you will consider some of my suggestions! I do think that, as it stands, the sign_up form is broken if the tenant does not save properly (if someone innocently adds some simple validations to it!). I'm sure that a lot of people will be basing new applications on your sample application, so it would be very helpful if it it's able to display validation errors and remember the tenant and coupon fields successfully.
regards
Dave
Im using Milia and trying to integrate token authentication with 'simple_token_authentication' gem, but it doesn't seem to work. Is it possible at all to use an API with token authentication in combination with milia ? Thanks
Is there a way, say, from a rake task, to make changes to all instances of a tenanted model?
From my reading of the docs it seems that I might have to loop over all the tenants, set each one as the current tenant, and operate on each of their associated tenanted models.
Is there a better way?
Are there plans (or does anyone know of any) Mongoid 3+ versions of milia?
Is it right to say that "Rails 3.2 onwards, the last designated default scope overrides any prior scopes and will invalidate multi-tenanting"? Rails docs says "If you use multiple default_scope declarations in your model then they will be merged together". I tested it myself:
class Article < ActiveRecord::Base
acts_as_tenant
default_scope {
where("created_at > '2015-01-01'")
}
end
Console:
Tenant.set_current_tenant(1)
Article.all
SELECT "articles".* FROM "articles" WHERE (articles.tenant_id = 1) AND (created_at > '2015-01-01')
Is it possible to edit the name of a member after signing up?
I encountered a problem adding milia-1.2.0 to my rails-4.2.0 application (I unfortunately couldn't graft my app onto the sample-milia-app because the existing application is too evolved).
I setup everything as described in your documentation. When calling http://localhost:3000/users/sign_up the form for signing up a new tenant shows up as expected. However after submitting the form an error flash message wrong tenant access; sign out & try again
appears.
I debugged the problem and found out the following:
The data get processed by Milia::RegistrationsController as expected. The controller
devise_create( user_params )
in line 36, thenresource.save
in line 122, thenraise ::Milia::Control::InvalidTenantAccess unless obj.tenant_id.nil?
wrong tenant access; sign out & try again
As far as I understand the problem is that the user object in the callback in base.rb has a tenant_id
even though it is an universal object. I didn't find out how to solve this problem. Any help would be greatly appreciated! :)
Some information which might help to narrow down the problem:
acts_as_universal_and_determines_account
.:invitable, :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
The log looks as follows:
Started POST "/users" for ::1 at 2015-03-13 15:32:44 +0100
Processing by Milia::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxxxx", "user"=>{"name"=>"Admin TestCompany", "email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "tenant"=>{"name"=>"TestCompany"}, "commit"=>"Register"}
(0.2ms) BEGIN
SQL (1.1ms) INSERT INTO "tenants" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "TestCompany"], ["created_at", "2015-03-13 14:32:45.045574"], ["updated_at", "2015-03-13 14:32:45.045574"]]
MILIA >>>>> [change tenant] new: 1 old: %
User Exists (1.6ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1
User Load (0.5ms) SELECT "users".* FROM "users" WHERE (users.tenant_id IS NULL) AND "users"."confirmation_token" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["confirmation_token", "72a251b1aa2a2ac60e4f7025370d259e75338aab60540e41dfca6420769ffe46"]]
SQL (0.9ms) INSERT INTO "users" ("email", "encrypted_password", "name", "role", "skip_confirm_change_password", "created_at", "updated_at", "confirmation_token", "confirmation_sent_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id" [["email", "[email protected]"], ["encrypted_password", "$2a$10$aPLmuYCDLh23Z74jErof8OEvbw7BnEJXa0tOgjUkyStx9ECxuJH0q"], ["name", "Admin TestCompany"], ["role", 0], ["skip_confirm_change_password", "t"], ["created_at", "2015-03-13 14:32:45.135246"], ["updated_at", "2015-03-13 14:32:45.135246"], ["confirmation_token", "72a251b1aa2a2ac60e4f7025370d259e75338aab60540e41dfca6420769ffe46"], ["confirmation_sent_at", "2015-03-13 14:32:45.360023"]]
Tenant Load (0.4ms) SELECT "tenants".* FROM "tenants" WHERE (tenants.tenant_id IS NULL) AND "tenants"."id" = $1 LIMIT 1 [["id", 1]]
User Exists (0.4ms) SELECT "users".* FROM "users" WHERE (users.tenant_id IS NULL) AND "users"."tenant_id" = $1 AND "users"."id" = $2 [["tenant_id", 1], ["id", 1]]
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = '[email protected]' AND "users"."id" != 1) LIMIT 1
(0.2ms) ROLLBACK
Redirected to http://localhost:3000/users/sign_up
resource.save
:"{"id"=>nil, "email"=>"[email protected]", "encrypted_password"=>"$2a$10$aPLmuYCDLh23Z74jErof8OEvbw7BnEJXa0tOgjUkyStx9ECxuJH0q", "reset_password_token"=>nil, "reset_password_sent_at"=>nil, "remember_created_at"=>nil, "sign_in_count"=>0, "current_sign_in_at"=>nil, "last_sign_in_at"=>nil, "current_sign_in_ip"=>nil, "last_sign_in_ip"=>nil, "created_at"=>nil, "updated_at"=>nil, "name"=>"firmenadmin", "confirmation_token"=>nil, "confirmed_at"=>nil, "confirmation_sent_at"=>nil, "unconfirmed_email"=>nil, "role"=>0, "invitation_token"=>nil, "invitation_created_at"=>nil, "invitation_sent_at"=>nil, "invitation_accepted_at"=>nil, "invitation_limit"=>nil, "invited_by_id"=>nil, "invited_by_type"=>nil, "invitations_count"=>0, "skip_confirm_change_password"=>true, "tenant_id"=>nil}"
The URL in the test site is broken. It sends the folowing link:-
http://simple-milia.herokuapp.com/users/confirmation?confirmation_token=removed...
That should read http://sample-milia.herokuapp, (not simple ;-)
Hi,
When I delete tenant, the user, member and all it's records is still in the DB.
I have added to tenants.rb model:
has_many :members, dependent: :destroy
has_many :all_other_models_acts_as_tenants, dependent: :destroy
I'm working with postgresql.
I deleted the tenant from rails console and also directly from the pg-Admin gui.
also tried to delete the member, the user etc.
So my question is what is the way to delete Tenant with all it's associated records?
Thanks!
Hey
I'm experiencing a redirect loop (even with your sample app on http://sample-milia.herokuapp.com) when following these steps:
Then, the redirect loop occurs because of the Milia::Control::InvalidTenantAccess
- I guess the tenant_id stays in the Thread and that causes the invalid_tenant error which causes redirects from index to user's landing page and back.
I suspect we would have to clear Thread.current[:tenant_id]
somewhere along the way?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.