milia 1.2.1 → 1.3.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/confirmations_controller.rb +7 -7
  3. data/app/controllers/registrations_controller.rb +13 -19
  4. data/app/views/devise/registrations/new.html.haml +53 -0
  5. data/lib/generators/milia/install_generator.rb +44 -41
  6. data/lib/generators/milia/templates/devise_permitted_parameters.rb +18 -0
  7. data/lib/generators/milia/templates/initializer.rb +3 -9
  8. data/lib/milia.rb +3 -22
  9. data/lib/milia/base.rb +88 -109
  10. data/lib/milia/control.rb +25 -25
  11. data/lib/milia/invite_member.rb +6 -6
  12. data/lib/milia/version.rb +1 -1
  13. metadata +16 -184
  14. data/.document +0 -5
  15. data/.gitignore +0 -94
  16. data/.ruby-gemset +0 -1
  17. data/.ruby-version +0 -1
  18. data/.slugignore +0 -2
  19. data/.travis.yml +0 -4
  20. data/Gemfile +0 -4
  21. data/LICENSE.txt +0 -20
  22. data/README.md +0 -1207
  23. data/Rakefile +0 -1
  24. data/doc/gemfile_addition.txt +0 -28
  25. data/doc/manual_sample.sh +0 -816
  26. data/doc/ref_notes.txt +0 -155
  27. data/doc/sample.sh +0 -276
  28. data/markdown.rb +0 -38
  29. data/milia.gemspec +0 -29
  30. data/test/.ruby-gemset +0 -1
  31. data/test/.ruby-version +0 -1
  32. data/test/Gemfile +0 -81
  33. data/test/Gemfile.lock +0 -223
  34. data/test/README.md +0 -83
  35. data/test/Rakefile +0 -6
  36. data/test/app/assets/javascripts/application.js +0 -16
  37. data/test/app/assets/stylesheets/application.css +0 -13
  38. data/test/app/controllers/application_controller.rb +0 -13
  39. data/test/app/controllers/home_controller.rb +0 -10
  40. data/test/app/helpers/application_helper.rb +0 -2
  41. data/test/app/models/member.rb +0 -34
  42. data/test/app/models/post.rb +0 -14
  43. data/test/app/models/team.rb +0 -9
  44. data/test/app/models/team_asset.rb +0 -6
  45. data/test/app/models/tenant.rb +0 -54
  46. data/test/app/models/user.rb +0 -14
  47. data/test/app/models/zine.rb +0 -8
  48. data/test/app/views/home/index.html.erb +0 -2
  49. data/test/app/views/home/show.html.erb +0 -2
  50. data/test/app/views/layouts/application.html.erb +0 -14
  51. data/test/bin/bundle +0 -3
  52. data/test/bin/rails +0 -4
  53. data/test/bin/rake +0 -4
  54. data/test/config/application.rb +0 -36
  55. data/test/config/boot.rb +0 -4
  56. data/test/config/database.yml +0 -25
  57. data/test/config/environment.rb +0 -5
  58. data/test/config/environments/development.rb +0 -48
  59. data/test/config/environments/production.rb +0 -95
  60. data/test/config/environments/test.rb +0 -42
  61. data/test/config/initializers/backtrace_silencers.rb +0 -7
  62. data/test/config/initializers/devise.rb +0 -254
  63. data/test/config/initializers/filter_parameter_logging.rb +0 -4
  64. data/test/config/initializers/inflections.rb +0 -16
  65. data/test/config/initializers/milia.rb +0 -51
  66. data/test/config/initializers/mime_types.rb +0 -5
  67. data/test/config/initializers/secret_token.rb +0 -12
  68. data/test/config/initializers/session_store.rb +0 -3
  69. data/test/config/initializers/wrap_parameters.rb +0 -14
  70. data/test/config/locales/en.yml +0 -23
  71. data/test/config/routes.rb +0 -77
  72. data/test/db/migrate/20111012050200_add_sessions_table.rb +0 -12
  73. data/test/db/migrate/20111012050340_devise_create_users.rb +0 -48
  74. data/test/db/migrate/20111012050532_create_tenants.rb +0 -11
  75. data/test/db/migrate/20111012050600_create_tenants_users_join_table.rb +0 -8
  76. data/test/db/migrate/20111012050650_create_members.rb +0 -12
  77. data/test/db/migrate/20111012231923_create_posts.rb +0 -12
  78. data/test/db/migrate/20111013050657_create_zines.rb +0 -10
  79. data/test/db/migrate/20111013050753_create_teams.rb +0 -10
  80. data/test/db/migrate/20111013050837_create_team_assets.rb +0 -11
  81. data/test/db/schema.rb +0 -126
  82. data/test/db/seeds.rb +0 -7
  83. data/test/test/controllers/home_controller_test.rb +0 -132
  84. data/test/test/ctlr_test_helper.rb +0 -11
  85. data/test/test/fixtures/members.yml +0 -35
  86. data/test/test/fixtures/posts.yml +0 -96
  87. data/test/test/fixtures/team_assets.yml +0 -30
  88. data/test/test/fixtures/teams.yml +0 -17
  89. data/test/test/fixtures/tenants.yml +0 -12
  90. data/test/test/fixtures/tenants_users.yml +0 -15
  91. data/test/test/fixtures/users.yml +0 -33
  92. data/test/test/fixtures/zines.yml +0 -25
  93. data/test/test/models/member_test.rb +0 -75
  94. data/test/test/models/post_test.rb +0 -66
  95. data/test/test/models/team_test.rb +0 -49
  96. data/test/test/models/tenant_test.rb +0 -228
  97. data/test/test/models/user_test.rb +0 -182
  98. data/test/test/models/zine_test.rb +0 -40
  99. data/test/test/test_helper.rb +0 -31
@@ -50,12 +50,12 @@ module Milia
50
50
  # ensures email exists and that email is unique and not already in system
51
51
  # ------------------------------------------------------------------------
52
52
  def save_and_invite_member( )
53
- if (
54
- self.email.blank? ||
55
- User.where([ "lower(email) = ?", self.email.downcase ]).first
56
- )
57
- self.errors.add(:email,"must be present and unique")
58
- status = nil
53
+ status = nil
54
+
55
+ if (self.email.blank?)
56
+ self.errors.add(:email, :blank)
57
+ elsif User.where([ "lower(email) = ?", self.email.downcase ]).present?
58
+ self.errors.add(:email, :taken)
59
59
  else
60
60
  check_or_set_password()
61
61
  status = self.save && self.errors.empty?
@@ -1,3 +1,3 @@
1
1
  module Milia
2
- VERSION = "1.2.1"
2
+ VERSION = "1.3.1.beta1"
3
3
  end
metadata CHANGED
@@ -1,10 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: milia
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.1.beta1
5
5
  platform: ruby
6
6
  authors:
7
- - daudi amani
7
+ - dsaronin
8
+ - jekuno
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
@@ -16,28 +17,28 @@ dependencies:
16
17
  requirements:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: '4.2'
20
+ version: '5.0'
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
25
  - - "~>"
25
26
  - !ruby/object:Gem::Version
26
- version: '4.2'
27
+ version: '5.0'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: devise
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
32
  - - "~>"
32
33
  - !ruby/object:Gem::Version
33
- version: '3.4'
34
+ version: '4.2'
34
35
  type: :runtime
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
39
  - - "~>"
39
40
  - !ruby/object:Gem::Version
40
- version: '3.4'
41
+ version: '4.2'
41
42
  - !ruby/object:Gem::Dependency
42
43
  name: bundler
43
44
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +67,6 @@ dependencies:
66
67
  - - ">="
67
68
  - !ruby/object:Gem::Version
68
69
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: sqlite3
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
70
  - !ruby/object:Gem::Dependency
84
71
  name: shoulda
85
72
  requirement: !ruby/object:Gem::Requirement
@@ -108,35 +95,22 @@ dependencies:
108
95
  - - ">="
109
96
  - !ruby/object:Gem::Version
110
97
  version: '0'
111
- description: Multi-tenanting gem for hosted Rails/Ruby/devise applications
98
+ description: Transparent multi-tenanting for web applications based on Rails and Devise
112
99
  email:
113
- - dsaronin@gmail.com
100
+ - jekuno@users.noreply.github.com
114
101
  executables: []
115
102
  extensions: []
116
103
  extra_rdoc_files: []
117
104
  files:
118
- - ".document"
119
- - ".gitignore"
120
- - ".project"
121
- - ".ruby-gemset"
122
- - ".ruby-version"
123
- - ".slugignore"
124
- - ".travis.yml"
125
- - Gemfile
126
- - LICENSE.txt
127
- - README.md
128
- - Rakefile
129
105
  - app/controllers/confirmations_controller.rb
130
106
  - app/controllers/passwords_controller.rb
131
107
  - app/controllers/registrations_controller.rb
132
108
  - app/controllers/sessions_controller.rb
109
+ - app/views/devise/registrations/new.html.haml
133
110
  - app/views/members/new.html.haml
134
- - doc/gemfile_addition.txt
135
- - doc/manual_sample.sh
136
- - doc/ref_notes.txt
137
- - doc/sample.sh
138
111
  - lib/generators/milia/install_generator.rb
139
112
  - lib/generators/milia/temp_generator.rb
113
+ - lib/generators/milia/templates/devise_permitted_parameters.rb
140
114
  - lib/generators/milia/templates/initializer.rb
141
115
  - lib/milia.rb
142
116
  - lib/milia/base.rb
@@ -146,79 +120,7 @@ files:
146
120
  - lib/milia/railtie.rb
147
121
  - lib/milia/tasks.rb
148
122
  - lib/milia/version.rb
149
- - markdown.rb
150
- - milia.gemspec
151
- - test/.ruby-gemset
152
- - test/.ruby-version
153
- - test/Gemfile
154
- - test/Gemfile.lock
155
- - test/README.md
156
- - test/Rakefile
157
- - test/app/assets/javascripts/application.js
158
- - test/app/assets/stylesheets/application.css
159
- - test/app/controllers/application_controller.rb
160
- - test/app/controllers/home_controller.rb
161
- - test/app/helpers/application_helper.rb
162
- - test/app/models/member.rb
163
- - test/app/models/post.rb
164
- - test/app/models/team.rb
165
- - test/app/models/team_asset.rb
166
- - test/app/models/tenant.rb
167
- - test/app/models/user.rb
168
- - test/app/models/zine.rb
169
- - test/app/views/home/index.html.erb
170
- - test/app/views/home/show.html.erb
171
- - test/app/views/layouts/application.html.erb
172
- - test/bin/bundle
173
- - test/bin/rails
174
- - test/bin/rake
175
- - test/config/application.rb
176
- - test/config/boot.rb
177
- - test/config/database.yml
178
- - test/config/environment.rb
179
- - test/config/environments/development.rb
180
- - test/config/environments/production.rb
181
- - test/config/environments/test.rb
182
- - test/config/initializers/backtrace_silencers.rb
183
- - test/config/initializers/devise.rb
184
- - test/config/initializers/filter_parameter_logging.rb
185
- - test/config/initializers/inflections.rb
186
- - test/config/initializers/milia.rb
187
- - test/config/initializers/mime_types.rb
188
- - test/config/initializers/secret_token.rb
189
- - test/config/initializers/session_store.rb
190
- - test/config/initializers/wrap_parameters.rb
191
- - test/config/locales/en.yml
192
- - test/config/routes.rb
193
- - test/db/migrate/20111012050200_add_sessions_table.rb
194
- - test/db/migrate/20111012050340_devise_create_users.rb
195
- - test/db/migrate/20111012050532_create_tenants.rb
196
- - test/db/migrate/20111012050600_create_tenants_users_join_table.rb
197
- - test/db/migrate/20111012050650_create_members.rb
198
- - test/db/migrate/20111012231923_create_posts.rb
199
- - test/db/migrate/20111013050657_create_zines.rb
200
- - test/db/migrate/20111013050753_create_teams.rb
201
- - test/db/migrate/20111013050837_create_team_assets.rb
202
- - test/db/schema.rb
203
- - test/db/seeds.rb
204
- - test/test/controllers/home_controller_test.rb
205
- - test/test/ctlr_test_helper.rb
206
- - test/test/fixtures/members.yml
207
- - test/test/fixtures/posts.yml
208
- - test/test/fixtures/team_assets.yml
209
- - test/test/fixtures/teams.yml
210
- - test/test/fixtures/tenants.yml
211
- - test/test/fixtures/tenants_users.yml
212
- - test/test/fixtures/users.yml
213
- - test/test/fixtures/zines.yml
214
- - test/test/models/member_test.rb
215
- - test/test/models/post_test.rb
216
- - test/test/models/team_test.rb
217
- - test/test/models/tenant_test.rb
218
- - test/test/models/user_test.rb
219
- - test/test/models/zine_test.rb
220
- - test/test/test_helper.rb
221
- homepage: ''
123
+ homepage: https://github.com/jekuno/milia
222
124
  licenses:
223
125
  - MIT
224
126
  metadata: {}
@@ -233,83 +135,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
233
135
  version: '0'
234
136
  required_rubygems_version: !ruby/object:Gem::Requirement
235
137
  requirements:
236
- - - ">="
138
+ - - ">"
237
139
  - !ruby/object:Gem::Version
238
- version: '0'
140
+ version: 1.3.1
239
141
  requirements: []
240
142
  rubyforge_project:
241
143
  rubygems_version: 2.2.2
242
144
  signing_key:
243
145
  specification_version: 4
244
- summary: Transparent multi-tenanting for hosted rails/ruby/devise web applications
245
- test_files:
246
- - test/.ruby-gemset
247
- - test/.ruby-version
248
- - test/Gemfile
249
- - test/Gemfile.lock
250
- - test/README.md
251
- - test/Rakefile
252
- - test/app/assets/javascripts/application.js
253
- - test/app/assets/stylesheets/application.css
254
- - test/app/controllers/application_controller.rb
255
- - test/app/controllers/home_controller.rb
256
- - test/app/helpers/application_helper.rb
257
- - test/app/models/member.rb
258
- - test/app/models/post.rb
259
- - test/app/models/team.rb
260
- - test/app/models/team_asset.rb
261
- - test/app/models/tenant.rb
262
- - test/app/models/user.rb
263
- - test/app/models/zine.rb
264
- - test/app/views/home/index.html.erb
265
- - test/app/views/home/show.html.erb
266
- - test/app/views/layouts/application.html.erb
267
- - test/bin/bundle
268
- - test/bin/rails
269
- - test/bin/rake
270
- - test/config/application.rb
271
- - test/config/boot.rb
272
- - test/config/database.yml
273
- - test/config/environment.rb
274
- - test/config/environments/development.rb
275
- - test/config/environments/production.rb
276
- - test/config/environments/test.rb
277
- - test/config/initializers/backtrace_silencers.rb
278
- - test/config/initializers/devise.rb
279
- - test/config/initializers/filter_parameter_logging.rb
280
- - test/config/initializers/inflections.rb
281
- - test/config/initializers/milia.rb
282
- - test/config/initializers/mime_types.rb
283
- - test/config/initializers/secret_token.rb
284
- - test/config/initializers/session_store.rb
285
- - test/config/initializers/wrap_parameters.rb
286
- - test/config/locales/en.yml
287
- - test/config/routes.rb
288
- - test/db/migrate/20111012050200_add_sessions_table.rb
289
- - test/db/migrate/20111012050340_devise_create_users.rb
290
- - test/db/migrate/20111012050532_create_tenants.rb
291
- - test/db/migrate/20111012050600_create_tenants_users_join_table.rb
292
- - test/db/migrate/20111012050650_create_members.rb
293
- - test/db/migrate/20111012231923_create_posts.rb
294
- - test/db/migrate/20111013050657_create_zines.rb
295
- - test/db/migrate/20111013050753_create_teams.rb
296
- - test/db/migrate/20111013050837_create_team_assets.rb
297
- - test/db/schema.rb
298
- - test/db/seeds.rb
299
- - test/test/controllers/home_controller_test.rb
300
- - test/test/ctlr_test_helper.rb
301
- - test/test/fixtures/members.yml
302
- - test/test/fixtures/posts.yml
303
- - test/test/fixtures/team_assets.yml
304
- - test/test/fixtures/teams.yml
305
- - test/test/fixtures/tenants.yml
306
- - test/test/fixtures/tenants_users.yml
307
- - test/test/fixtures/users.yml
308
- - test/test/fixtures/zines.yml
309
- - test/test/models/member_test.rb
310
- - test/test/models/post_test.rb
311
- - test/test/models/team_test.rb
312
- - test/test/models/tenant_test.rb
313
- - test/test/models/user_test.rb
314
- - test/test/models/zine_test.rb
315
- - test/test/test_helper.rb
146
+ summary: Easy multi-tenanting for Rails + Devise
147
+ test_files: []
data/.document DELETED
@@ -1,5 +0,0 @@
1
- lib/**/*.rb
2
- bin/*
3
- -
4
- features/**/*.feature
5
- LICENSE.txt
data/.gitignore DELETED
@@ -1,94 +0,0 @@
1
- #####################################################################################
2
- # Rails
3
- .bundle
4
- test/db/*.sqlite3
5
- test/db/*.sqlite3-journal
6
- log/
7
- tmp/
8
- *.log
9
- tmp/**/*
10
- .tmp*
11
-
12
- # Documentation
13
- doc/api
14
- doc/app
15
- .yardoc
16
- .yardopts
17
- rdoc/
18
-
19
- # Public Uploads
20
- public/system/*
21
- public/themes/*
22
-
23
- # Public Cache
24
- public/javascripts/cache
25
- public/stylesheets/cache
26
-
27
- # Vendor Cache
28
- vendor/cache
29
-
30
- # Acts as Indexed
31
- index/**/*
32
-
33
- # Refinery Specific
34
- *.tmproj
35
- *.autobackupbyrefinery.*
36
- /refinerycms-*.gem
37
- .autotest
38
-
39
- # Mac
40
- .DS_Store
41
-
42
- # Windows
43
- Thumbs.db
44
-
45
- # NetBeans
46
- nbproject
47
-
48
- # Eclipse
49
- .project
50
-
51
- # Redcar
52
- .redcar
53
-
54
- # Rubinius
55
- *.rbc
56
-
57
- # Vim
58
- *.swp
59
- *.swo
60
- *~
61
-
62
- # RubyMine
63
- .idea
64
-
65
- # Backup
66
- *~
67
-
68
- # Capybara Bug
69
- capybara-*html
70
-
71
- # sass
72
- .sass-cache
73
- .sass-cache/*
74
-
75
- #rvm
76
- .rvmrc
77
- .rvmrc.*
78
-
79
- #gem
80
- pkg/
81
- *.gem
82
-
83
- # other
84
- .loadpath
85
- all.js
86
- all.css
87
- temp*.txt
88
- config/database.yml
89
- *.dump
90
- .bashrc
91
-
92
- #git
93
- *.keep
94
- *.gitkeep
@@ -1 +0,0 @@
1
- milia2
@@ -1 +0,0 @@
1
- ruby-2.1.3
@@ -1,2 +0,0 @@
1
- test
2
- doc
@@ -1,4 +0,0 @@
1
- language: ruby
2
- gemfile: test/Gemfile
3
- before_install: cd test
4
- script: bundle exec rake
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in milia.gemspec
4
- gemspec
@@ -1,20 +0,0 @@
1
- Copyright (c) 2011 David Anderson
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md DELETED
@@ -1,1207 +0,0 @@
1
- # milia
2
- [![Build Status](https://travis-ci.org/jekuno/milia.svg?branch=master)](https://travis-ci.org/jekuno/milia)
3
-
4
- Milia is a multi-tenanting gem for Ruby on Rails applications.
5
-
6
- * Milia is a solid choice for (SaaS) applications which are used by more than one tenant (i.e. companies or organizations) and is tailored for common use cases of multi-tenanted applications.
7
- * Milia allows to save the data of all tenants in the same database and enforces row based separation of the tenant data.
8
- * Milia uses the devise gem for user authentication and registration.
9
-
10
- You are viewing the documentation of the milia branch for **Rails 4.2.x** applications.
11
- Milia also supports **Rails 5.x**. Please consider the according [README](../rails5-support/README.md) of the [Rails5 branch](../../tree/rails5-support/).
12
-
13
- ## Basic concepts for the milia multi-tenanting gem
14
-
15
- ### Milia highlights
16
-
17
- * Transparent to the main application code
18
- * Symbiotic with user authentication
19
- * Raises exceptions upon attempted illegal access
20
- * Enforces tenanting (not allow sloppy access to all tenant records)
21
- * Allows application flexibility upon new tenant sign-up, usage of eula information, etc
22
- * As non-invasive (as possible) to Rails code
23
- * Uses row-based tenanting
24
- * Uses default_scope to enforce tenanting
25
-
26
- ### Row based vs. schema based tenanting
27
- * Milia uses row based tenanting.
28
- * The author used schema-based tenanting in the past but found it deficient for the following reasons. Schema-based tenancy
29
- * is not what DBMS are optimized for (most DBMS are optimized to handle enormous number of rows but not an enormous number of schema (tables)),
30
- * took a performance hit,
31
- * was seriously time-consuming to backup and restore,
32
- * was invasive into the Rails code structure (monkey patching),
33
- * was complex to implement, and
34
- * couldn't use Rails migration tools as-is.
35
- * Heroku also [strongly recommends against](https://devcenter.heroku.com/articles/heroku-postgresql#multiple-schemas) using schema based tenanting.
36
-
37
- ### tenants/users vs organizations/members
38
-
39
- A tenant == an organization; users == members of the organization.
40
- Only organizations sign up for new tenants, not members (users).
41
- The very first user of an organization, let's call him the Organizer,
42
- is the one responsible for initiating the organizational signup.
43
- The Organizer becomes the first member (user) of the organization (tenant).
44
- Thereafter, other members only obtain entry to the organization (tenant)
45
- by invitation. New tenants are not created for every new user.
46
-
47
- ## Version
48
-
49
- milia v1.2.0 is the release version for Rails 4.2.x and is now available for usage.
50
-
51
-
52
- ## v1.2.0 - What's changed?
53
- * Rails 4.2.x adapted
54
- * fixes Issue #42: Redirect loop (sign up & activate with email1; trying to sign up again with email1 fails but immediately signing in with email1 caused a redirect loop).
55
-
56
- ## v1.1.x - What's changed?
57
- * Rails 4.1.x adapted
58
- * Devise 3.4.x adapted
59
-
60
- ## v1.0.x - What's changed?
61
-
62
- * Rails 4.0.x adapted (changes to terms, strong_parameters, default_scope, etc)
63
- * Devise 3.2.x adapted
64
- * All the changes which version 0.3.x advised to be inserted in applications_controller.rb are now automatically loaded into ActionController by milia.
65
- * that includes authenticate_tenant!
66
- * so if you've been using an older version of milia, you'll need to remove that stuff from applications_controller!
67
- * generators for easy install of basic rails/milia/devise
68
- * callback after successful authenticate_tenant!
69
- * debug & info logging and trace for troubleshooting
70
- * improved invite_member support
71
- * revised README instructions
72
-
73
- The last previous release version for Rails 3.2.x can be found in the git branch 'v0.3', but
74
- it is essentially obsolete. Go with v1.0.x
75
-
76
- ## Sample app and documentation
77
-
78
- There were numerous requests for me to provide a complete sample web application
79
- which uses milia and devise. I have done this. This README will have a brief section
80
- on creating and installing the sample application.
81
-
82
- This sample app is now running on heroku and you may test it out here:
83
- http://sample-milia.herokuapp.com
84
- Instructions for deploying to heroku can be found in doc/sample.sh STEP 6
85
- within the milia gem.
86
-
87
-
88
- Further details about this process can be found via the sources listed below:
89
-
90
- * see doc/sample.sh for easy generator usage for setting up and creating a working app.
91
- Although all the same information is here in this README, it's perhaps clearer, and
92
- presented better, as a step-by-step instruction manual. This README must perforce be
93
- more as a reference manual.
94
- * the sample app uses web-theme-app to provide some pleasantly formatted views for your testing pleasure.
95
- * see doc/manual_sample.sh for complete step-by-step instructions for manually setting up and creating a working app.
96
- * if you want to know exactly everything the generators are doing, see the manual_sample.sh
97
- - instructions are very detailed and loaded with comments (600 lines!).
98
- - Stage one: with simple devise and no milia,
99
- - Stage two: installing milia for complete tenanting,
100
- - Stage three: adding in invite_member capability
101
- * the entire sample is also fully available on github, if you wish to check your work. diff can be your friend.
102
- this sample on github, however, will always be for the latest release or latest beta (whichever is most recent).
103
- * find it at: https://github.com/dsaronin/sample-milia-app
104
-
105
- ### Available documentation resources for milia
106
-
107
- * doc/sample.sh -- this document will ALWAYS be the most recent
108
- (for example in the edge branch: "newdev")
109
- * doc/manual_sample.sh -- non-generator-based instructions for manually editing files.
110
- (this may no longer be the most recent since further work will focus on the generators)
111
- * doc/gemfile_addition.txt -- the additions to Gemfile needed for setting up the sample-milia-app
112
-
113
- * github.com/milia/wiki/sample-milia-app-tutorial
114
- this should be the same as the manual_sample.sh doc for the current
115
- stable release (or last beta version); but markdown formatted
116
- https://github.com/dsaronin/milia/wiki/sample-milia-app-tutorial
117
- * milia README (this document):
118
- - this will be the knowledgable programmer's digest of the essentials
119
- - and thus it won't cover some of the intricacies of actually
120
- implementing milia: either the tutorial or sample.sh will do that
121
- - if you're a first time milia implementer, please use both the
122
- README and either of the two above documents for assistance: it will save you time.
123
-
124
- ## converting an existing app to multi-tenanted
125
-
126
- It is doable, but you'll need to first understand how milia basically is installed. I'd still recommend
127
- bringing up the sample-milia-app, getting it working, and then figuring out how to either graft it onto your app.
128
- Or (recommended), grafting your app onto it. I prefer to work that way because it's based off of a pure Rails 4.0
129
- and devise 3.2 install.
130
-
131
- ## Dependency requirements
132
-
133
- * Ruby 2.1.3
134
- * Rails 4.1.x
135
- * Devise 3.4.x
136
-
137
- ## this readme is for v1.1.0
138
- * changes in v1.1.0: just gem dependency; web-app-theme generator had a change
139
-
140
- * changes in beta-7: model & controller testing is almost complete;
141
- minor bug fixed; mixed-in controller methods are now public, not
142
- private.
143
-
144
- * changes in beta-6: user_params added to Tenant.create_new_tenant;
145
- ability to add additional whitelist parameters during config
146
-
147
- * changes in beta-5: logging, callback, bug fixes
148
-
149
- * changes in beta-4:
150
- corrections to README for Gemfile requirements
151
- generator tests for requirements
152
-
153
- * changes in beta-3: improved generators getting a new app started
154
-
155
- * changes in beta-2: invite_member capability
156
-
157
- ### edge branch: "newdev"
158
-
159
- If I'm actively developing, this can be in a state of flux. Use at your own risk.
160
-
161
- ## Authorized Roles
162
-
163
- Milia doesn't have any requirements re roles for users. But you will probably need
164
- something in your app to support different roles levels. Devise recommends cancan, but
165
- I have not used it and do not know how it might affect milia. In my app, I used to use
166
- ACL9 before it encountered version issues with Rails. Rather than debugging it, I spun
167
- off my own simplified version which I use now with great success. The gem I wrote is
168
- open sourced. It is called _kibali_ and is available at github: https://github.com/dsaronin/kibali.
169
- Kibali is a simple replacement for ACL9, a role-based authentication gem.
170
- I prefer the non-obstrusive nature of kibali and the clear-cut way it deliniates
171
- roles for actions at the start of each controller. This simplicity was also in ACL9.
172
- Kibali is primarily oriented for functioning as a before_action role authentication scheme for Rails controllers.
173
-
174
- ## Structure
175
-
176
- * necessary models: user, tenant
177
- * necessary migrations: user, tenant, tenants_users (join table)
178
-
179
- You must understand which of your apps models will be tenanted ( <i>acts_as_tenant</i> )
180
- and which will be universal ( <i>acts_as_universal</i>). Universal data NEVER has critical user/company
181
- information in the table. It is usually only for system-wide constants. For example, if you've put
182
- too much user information in the users table, you'll need to seperate it out. by definition, the devise
183
- user table MUST be universal and should only contain email, encrypted password, and devise-required data.
184
- ALL OTHER USER DATA (name, phone, address, etc) should be broken out into a tenanted table (say called member_data)
185
- which belongs_to :user, and in the User model, has_one :member_data. Ditto for organization (account or company)
186
- information.
187
-
188
- Most of your tables (except for pure join tables, users, and tenants) SHOULD BE tenanted. You should rarely have
189
- universal tables, even for things you consider to be system settings. At some time in the future, your accounts
190
- (organizations) will want to tailor/customize this data. So might as well start off correctly by making the
191
- table tenanted. It costs you nothing to do so now at the beginning. It does mean that you will need to seed
192
- these tables whenever a new tenant (organizational account) is created.
193
-
194
- Finally:
195
-
196
- * tenants = organizational accounts and are created via sign up, a one-time event. this also creates the
197
- first MEMBER of that account in your app who is usually the organizing admin. This person can then issue
198
- invitations (below) to bring other members into the account on the app.
199
- * members = members WITHIN a tenant and are created by invitation only; they do NOT sign up. An invitation is
200
- sent to them, they click on an activate or confirm link, and then they become a member of a tenanted group.
201
- * The invitation process involves creating both a new user (within the current_tenant) and its corresponding
202
- member_data records.
203
- * ALL models (whether tenanted or universal) are expected to have a field in the table labelled: tenant_id.
204
- * YOUR CODE SHOULD NEVER EVER TRY TO CHANGE OR SET THE tenant_id OF A RECORD. milia will not allow it, milia
205
- will check for deviance; milia will raise exceptions if it's wrong; and milia will override it to maintain integrity.
206
- * Tenanted records will have tenant_id set to the appropriate tenant automagically by milia.
207
- * Universal records will have tenant_id always set to nil, automagically by milia; and references to any
208
- universal table will ALWAYS expect this field to be nil.
209
- * Pure join tables (has_and_belongs_to_many HABTM associations) get neither designation (tenant nor universal).
210
- The way that rails accesses these ensures that it will validate the tenant of joined member. A pure HABTM join
211
- table is created with generation such as follows:
212
-
213
- ```
214
- rails g migration CreateModel1sModel2sJoinTable model1s model2s
215
- ```
216
-
217
- ## Installation
218
-
219
- This README describes two different ways to install:
220
- * use of a milia generator to install itself and automate those
221
- tweaks for most use cases (recommended method)
222
- * a bare minimum manual setup which requires many minor tweaks;
223
- those tweaks will be described in a later section.
224
-
225
- Later sections of the README will enumerate:
226
- * how to create a simple working sample rails/devise/milia application
227
- * all the expected tweaks which the generator performed automatically
228
- * advice on advanced usage of milia (from rake tasks, console)
229
- * specifics about the milia API
230
-
231
-
232
- ## Creating and Installing a Rails/Milia/Devise Sample Application
233
-
234
- ### Getting started for the sample application
235
-
236
- Multi-tenanting, much like user authentication, exists within the large scheme of
237
- an application. I recommend first creating and installing the following complete, but simple,
238
- reference Rails application so that you can validate a working setup on your
239
- system, and have a reference point for making changes to your own application.
240
-
241
- ### Environment setup
242
-
243
- I put these in .bashrc for an Ubuntu system.
244
-
245
- ```
246
- export PORT=3000
247
- export RACK_ENV=development
248
- export SMTP_ENTRY=<my smtp password>
249
- # OPTIONAL: recaptcha keys
250
- export RECAPTCHA_PUBLIC_KEY=6LeYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKpT
251
- export RECAPTCHA_PRIVATE_KEY=6LeBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBgQBv
252
- ```
253
-
254
- ### Rails setup
255
-
256
- This example shows getting starting a new rails project in an rvm environment and using
257
- git (github) as the SCM, and expecting to use heroku (eventually) as the PaaS
258
- (Platform as a Service) provider. Note: foreman is available when you set up the
259
- heroku toolbelt (see doc/sample.sh for more details).
260
-
261
- This example will set everything up for milia AND devise (you won't need to do devise).
262
- It assumes you won't use airbrake, you will use recaptcha for new account sign-ups,
263
- and you will use invite_member capability. Skeleton user, tenant, and member models
264
- will be created. Before you run db:migrate, you can add any additional fields to the
265
- tenant and member models. User model really is primarily only used by devise, and so
266
- you shouldn't add anything to this model as it's a universal model. Member is a tenanted
267
- model and so this is where all the information for a member should be kept.
268
-
269
- ```ruby
270
- $ cd projectspace # if not there already
271
-
272
- $ rails new sample-milia-app --skip-bundle
273
- $ echo "sample-milia-app" > sample-milia-app/.ruby-gemset
274
- $ echo "2.1.3" > sample-milia-app/.ruby-version
275
- $ echo "web: bundle exec thin start -R config.ru -p \$PORT -e \$RACK_ENV" > sample-milia-app/Procfile
276
- $ rvm gemset create sample-milia-app
277
- ```
278
-
279
- Change .gitignore to match your development environment.
280
- I just copy my standard .gitignore from another project
281
- but you can copy mine from sample-milia-app on github.
282
-
283
- ```
284
- $ cd sample-milia-app
285
- $ cp ../<an existing project>/.gitignore .
286
-
287
- $ git init
288
- $ git add --all .
289
- $ git commit -am 'initial commit'
290
- $ git remote add origin git@github.com:<git-user>/sample-milia-app.git
291
- $ git push -u origin master
292
- ```
293
-
294
- ### milia and devise setup
295
-
296
- This sample web application depends on my updates to the web-app-theme, as
297
- well as several other gems for the application, which need to be added
298
- to the Gemfile, before running the installer.
299
- You can directly import these into your Gemfile
300
- by getting them from <i>doc/gemfile_addition.txt.</i>
301
- After adding this addition to the Gemfile, please make sure the correct
302
- milia branch is being designated (it sometimes points to edge branch).
303
-
304
- Note: web-app-theme is no longer
305
- listed as a milia dependency in the gemspec, but the above gemfile_addition
306
- references it and will place it in your Gemfile.
307
-
308
- ```
309
- $ vim Gemfile
310
- G
311
- :r <path to milia gem>/doc/gemfile_addition.txt
312
- ZZ
313
- $ bundle install
314
- ```
315
-
316
- ### WARNING: don't go commando and try to change everything at once! Don't be a perfectionist and try to bring up a fully written app at once!
317
-
318
- Just follow the instructions for creating the sample, exactly, step-by-step.
319
- Get the basics working. Then change, adapt, and spice to taste.
320
- Please?! Because I'm more inclined to help you solve problems if you've started out by
321
- getting the sample working exactly as described! If you've tried to go off into the jungle on your own, you are, well, on
322
- your own. And as they say, _"get out the way you got in!"_
323
-
324
-
325
- #### complete generating the sample application
326
-
327
- Running the generator below will completely install milia, devise, and a sample app. You will not
328
- need to run the "milia:install" given above. You'll need to do the following:
329
-
330
- ```
331
- $ rails g milia:install --org_email='<your smtp email for dev work>'
332
- $ rails g web_app_theme:milia
333
- ```
334
-
335
- NOTE: The above generator has an option to specify an email address to
336
- be used for sending emails for confirmation and account activation.
337
-
338
- The generator set up basic information for
339
- being able to send the confirmation & activation emails.
340
- But, you might need to complete entering in your email and smtp
341
- information in the following places:
342
-
343
- * _config/environments/development.rb_
344
- * _config/environments/production.rb_
345
-
346
- #### create the database and migration
347
- ```
348
- $ rake db:create
349
- $ rake db:migrate
350
- ```
351
-
352
- #### test by starting server:
353
- ```
354
- $ foreman start
355
- ```
356
-
357
- #### open your browser to http://localhost:3000
358
-
359
- And that's all you have to do!
360
-
361
- ## Milia Basic Installation (not needed if you used the generator above)
362
-
363
- ### Getting started for the Bare minimum setup
364
-
365
- This is the mininum necessary for using milia with a Rails application. If you're new to Rails
366
- (or Devise and Milia), then I'd recommend you skip this section and instead follow the instructions
367
- (previous section above) for Creating and Installing a Rails/Milia/Devise Sample Application.
368
-
369
- <strong>In any case, do NOT do both installations.</strong>
370
-
371
- If you'll be using the recaptcha option, then milia will generate expecting the following
372
- environment variables (put them in .bashrc, with the correct keys):
373
-
374
- ```
375
- export RECAPTCHA_PUBLIC_KEY=6LeYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKpT
376
- export RECAPTCHA_PRIVATE_KEY=6LeBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBgQBv
377
- ```
378
-
379
- ### Steps for Bare minimum
380
- Add to your Gemfile:
381
-
382
- ```ruby
383
- gem 'devise', '~>3.2'
384
- gem 'milia', '~>1.0'
385
- ```
386
-
387
- If you'll be working with any beta or leading edge version, specify as follows:
388
-
389
- ```
390
- gem 'milia', :git => 'git://github.com/dsaronin/milia.git', :branch => 'v1.0.0-beta-7'
391
- ```
392
-
393
- Then,
394
- ```
395
- $ bundle install
396
- $ rails g milia:install --org_email='<your smtp email for dev work>'
397
- ```
398
-
399
- Note: The milia generator has an option to specify an email address to be used for sending emails for
400
- confirmation and account activation. Also note that the milia generator runs two
401
- devise generators.
402
-
403
- Make any changes required to the generated migrations, then:
404
- ```
405
- $ rake db:create
406
- $ rake db:migrate
407
- ```
408
-
409
- ---------------------------------------------------------------------
410
-
411
- ## Installation Reference Manual
412
-
413
- This information is for reference only. The two generators automatically perform
414
- these changes when installing the sample application. Do NOT repeat these steps
415
- if you followed the automatic installation of the sample application.
416
-
417
- #### information and expectations
418
-
419
- **The above generator did everthing that's required. This section
420
- will explain why the generator did what it did. You won't need
421
- to do any of these steps unless you decide to customize or adapt.**
422
-
423
- #### User session required
424
-
425
- Rails 4 now handles this with a gem:
426
-
427
- ```
428
- gem 'activerecord-session_store', github: 'rails/activerecord-session_store'
429
- ```
430
-
431
- #### Generate a session migration
432
-
433
- ```
434
- $ rails g active_record:session_migration
435
- ```
436
-
437
- ### Devise setup
438
-
439
- * See https://github.com/plataformatec/devise for how to set up devise.
440
- * The current version of milia requires that devise use a *User* model.
441
-
442
- ```
443
- $ rails g devise:install
444
- $ rails g devise user
445
- ```
446
-
447
- Add the following in <i>config/routes.rb</i> to the existing devise_for :users :
448
-
449
- ```
450
- as :user do # *MUST* come *BEFORE* devise's definitions (below)
451
- match '/user/confirmation' => 'milia/confirmations#update', :via => :put, :as => :update_user_confirmation
452
- end
453
-
454
- devise_for :users, :controllers => {
455
- :registrations => "milia/registrations",
456
- :confirmations => "milia/confirmations",
457
- :sessions => "milia/sessions",
458
- :passwords => "milia/passwords",
459
- }
460
-
461
- ```
462
-
463
- Add the appropriate line below to <i>config/environments/</i>_
464
- files <i>development.rb, production.rb, test.rb</i>_ (respectively below, editing hosts as appropriate for your app).
465
- Make sure you've also correctly set up the ActionMailer::Base.smtp_settings. If you're unclear as to how to
466
- do that, refer to the sample-milia-app.
467
-
468
- ```
469
- config.action_mailer.default_url_options = { :host => 'localhost:3000' }
470
- config.action_mailer.default_url_options = { :host => 'secure.simple-milia-app.com', :protocol => 'https' }
471
- config.action_mailer.default_url_options = { :host => "www.example.com" }
472
- ```
473
-
474
- EDIT: <i>db/migrate/xxxxxxx_devise_create_users.rb</i>
475
- and uncomment the confirmable section, it will then look as follows:
476
-
477
- ```
478
- ## Confirmable
479
- t.string :confirmation_token
480
- t.datetime :confirmed_at
481
- t.datetime :confirmation_sent_at
482
- t.string :unconfirmed_email # Only if using reconfirmable
483
- ```
484
-
485
- and uncomment the confirmation_token index line to look as follows
486
-
487
- ```
488
- add_index :users, :confirmation_token, :unique => true
489
- ```
490
-
491
- and add above the t.timestamps line:
492
-
493
- ```
494
- # milia member_invitable
495
- t.boolean :skip_confirm_change_password, :default => false
496
-
497
- t.references :tenant
498
- ```
499
-
500
- edit <i>config/initializers/devise.rb</i>
501
- and change mailer_sender to be your from: email address
502
-
503
- ```
504
- config.mailer_sender = "my-email@simple-milia-app.com"
505
- ```
506
-
507
- OPTIONAL (not required for milia):
508
- in the same initializer file, locate and uncomment the following lines:
509
-
510
- ```
511
- config.pepper = '46f2....'
512
- config.confirmation_keys = [ :email ]
513
- config.email_regexp = /\A[^@]+@[^@]+\z/
514
- ```
515
-
516
- ### Milia setup
517
-
518
- #### migrations
519
-
520
- *ALL* models require a tenanting field, whether they are to be universal or to
521
- be tenanted. So make sure the following is added to each migration:
522
-
523
- <i>db/migrate/xxxxxxx_create_modelXYZ.rb</i>
524
-
525
- ```
526
- t.references :tenant
527
- ```
528
-
529
- Tenanted models will also require indexes for the tenant field.
530
-
531
- ```
532
- add_index :<tablename>, :tenant_id
533
- ```
534
-
535
- BUT: Do not add any <i>belongs_to :tenant</i> statements into any of your
536
- models. milia will do that for all. I do recommend, however, that you add
537
- into your <i>app/models/tenant.rb</i> file, one line per tenanted model such
538
- as the following (replacing <model> with your model's name):
539
-
540
- ```
541
- has_many :<model>s, :dependency => destroy
542
- ```
543
-
544
- The reason for this is that if you wish to have a master destroy tenant action,
545
- it will also remove all related tenanted tables and records.
546
-
547
- Generate the tenant migration
548
-
549
- ```
550
- $ rails g model tenant tenant:references name:string:index
551
- ```
552
-
553
- Generate the tenants_users join table migration
554
-
555
- ```
556
- $ rails g migration CreateTenantsUsersJoinTable tenants users
557
- ```
558
-
559
- EDIT: <i>db/migrate/20131119092046_create_tenants_users_join_table.rb</i>
560
- then uncomment the first index line as follows:
561
-
562
- ```
563
- t.index [:tenant_id, :user_id]
564
- ```
565
-
566
- #### application controller
567
-
568
- <i>app/controllers/application_controller.rb</i>
569
- add the following line IMMEDIATELY AFTER line 4 protect_from_forgery
570
-
571
-
572
- ```
573
- before_action :authenticate_tenant! # authenticate user and sets up tenant
574
-
575
- rescue_from ::Milia::Control::MaxTenantExceeded, :with => :max_tenants
576
- rescue_from ::Milia::Control::InvalidTenantAccess, :with => :invalid_tenant
577
-
578
- # milia defines a default max_tenants, invalid_tenant exception handling
579
- # but you can override if you wish to handle directly
580
- ```
581
-
582
- ### Designate which model determines account
583
-
584
- Add the following acts_as_... to designate which model will be used as the key
585
- into tenants_users to find the tenant for a given user.
586
- Only designate one model in this manner.
587
-
588
- <i>app/models/user.rb</i>
589
-
590
- ```ruby
591
- class User < ActiveRecord::Base
592
-
593
- acts_as_universal_and_determines_account
594
-
595
- end # class User
596
- ```
597
-
598
- ### Designate which model determines tenant
599
-
600
- Add the following acts_as_... to designate which model will be used as the
601
- tenant model. It is this id field which designates the tenant for an entire
602
- group of users which exist within a single tenanted domain.
603
- Only designate one model in this manner.
604
-
605
- <i>app/models/tenant.rb</i>
606
-
607
- ```ruby
608
- class Tenant < ActiveRecord::Base
609
-
610
- acts_as_universal_and_determines_tenant
611
-
612
- end # class Tenant
613
- ```
614
-
615
- ### Clean up any generated belongs_to tenant references in all models
616
-
617
- which the generator might have generated
618
- ( both <i>acts_as_tenant</i> and <i>acts_as_universal</i> will specify these ).
619
-
620
- ### Designate universal models
621
-
622
- Add the following acts_as_universal to *ALL* models which are to be universal.
623
-
624
- ```ruby
625
- acts_as_universal
626
- ```
627
-
628
- ### Designate tenanted models
629
-
630
- Add the following acts_as_tenant to *ALL* models which are to be tenanted.
631
- Example for a ficticous Post model:
632
-
633
- <i>app/models/post.rb</i>
634
-
635
- ```ruby
636
- class Post < ActiveRecord::Base
637
-
638
- acts_as_tenant
639
-
640
- end # class Post
641
- ```
642
-
643
- ### Exceptions raised
644
-
645
- ```ruby
646
- Milia::Control::InvalidTenantAccess
647
- Milia::Control::MaxTenantExceeded
648
- ```
649
-
650
- ### post authenticate_tenant! callback [optional]
651
-
652
- In some applications, you will want to set up commonly used
653
- variables used throughout your application, after a user and a
654
- tenant have been established. This is optional and if the
655
- callback is missing, nothing will happen.
656
-
657
- <i>app/controllers/application_controller.rb</i>
658
-
659
- ```ruby
660
- def callback_authenticate_tenant
661
- # set_environment or whatever else you need for each valid session
662
- end
663
- ```
664
-
665
-
666
-
667
- ### Tenant pre-processing hooks
668
-
669
- #### Milia expects a tenant pre-processing & setup hook:
670
-
671
- ```ruby
672
- Tenant.create_new_tenant(tenant_params, coupon_params) # see sample code below
673
- ```
674
-
675
- where the sign-up params are passed, the new tenant must be validated, created,
676
- and then returned. Any other kinds of prepatory processing are permitted here,
677
- but should be minimal, and should not involve any tenanted models. At this point
678
- in the new account sign-up chain, no tenant has been set up yet (but will be
679
- immediately after the new tenant has been created).
680
-
681
- <i>app/models/tenant.rb</i>
682
-
683
- ```ruby
684
- def self.create_new_tenant(tenant_params, user_params, coupon_params)
685
-
686
- tenant = Tenant.new(:name => tenant_params[:name])
687
-
688
- if new_signups_not_permitted?(coupon_params)
689
-
690
- raise ::Milia::Control::MaxTenantExceeded, "Sorry, new accounts not permitted at this time"
691
-
692
- else
693
- tenant.save # create the tenant
694
- end
695
- return tenant
696
- end
697
-
698
- # ------------------------------------------------------------------------
699
- # new_signups_not_permitted? -- returns true if no further signups allowed
700
- # args: params from user input; might contain a special 'coupon' code
701
- # used to determine whether or not to allow another signup
702
- # ------------------------------------------------------------------------
703
- def self.new_signups_not_permitted?(params)
704
- return false
705
- end
706
- ```
707
-
708
- #### Milia expects a tenant post-processing hook:
709
-
710
- ```ruby
711
- Tenant.tenant_signup(user,tenant,other) # see sample code below
712
- ```
713
-
714
- The purpose here is to do any tenant initialization AFTER devise
715
- has validated and created a user. Objects for the user and tenant
716
- are passed. It is recommended that only minimal processing be done
717
- here ... for example, queueing a background task to do the actual
718
- work in setting things up for a new tenant.
719
-
720
- <i>app/models/tenant.rb</i>
721
-
722
- ```ruby
723
- # ------------------------------------------------------------------------
724
- # tenant_signup -- setup a new tenant in the system
725
- # CALLBACK from devise RegistrationsController (milia override)
726
- # AFTER user creation and current_tenant established
727
- # args:
728
- # user -- new user obj
729
- # tenant -- new tenant obj
730
- # other -- any other parameter string from initial request
731
- # ------------------------------------------------------------------------
732
- def self.tenant_signup(user, tenant, other = nil)
733
- # StartupJob.queue_startup( tenant, user, other )
734
- # any special seeding required for a new organizational tenant
735
-
736
- Member.create_org_admin(user) # sample if using Member as tenanted member information model
737
- end
738
- ```
739
-
740
- ### View for Organizer sign ups
741
-
742
- This example shows how to display a signup form together with recaptcha.
743
- It also shows usage of an optional coupon field
744
- for whatever reason you might need. If you're not familiar with haml, leading spaces are significant
745
- and are used to indicate logical blocks. Otherwise, it's kinda like erb without all the syntactical cruff.
746
- Leading "." indicate div class; "#" indicates a div ID. The example here is
747
- taken from sample-milia-app.
748
-
749
- <i>app/views/devise/registrations/new.html.haml</i>
750
-
751
- ```ruby
752
- %h1 Simple Milia App
753
- .block#block-signup
754
- %h2 New Organizational Sign up
755
- .content
756
- %span.description
757
- %i
758
- If you're a member of an existing group in our system,
759
- click the activate link in the invitation email from your organization's admin.
760
- You should not sign up for a new organizational account.
761
- %br
762
- .flash
763
- - flash.each do |type, message|
764
- %div{ :class => "message #{type}" }
765
- %p= message
766
- - flash.clear # clear contents so we won't see it again
767
-
768
- = form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "form" }) do |f|
769
- .group
770
- = f.label :email, :class => "label"
771
- = f.text_field :email, :class => "text_field"
772
- %span.description Ex. test@example.com
773
- .group
774
- = f.label :password, :class => "label"
775
- = f.password_field :password, :class => "text_field"
776
- %span.description must be at least 6 characters
777
- .group
778
- = f.label :password_confirmation, "Re-enter Password", :class => "label"
779
- = f.password_field :password_confirmation, :class => "text_field"
780
- %span.description to confirm your password
781
-
782
- .group
783
- = fields_for( :tenant ) do |w|
784
- = w.label( :name, 'Organization', :class => "label" )
785
- = w.text_field( :name, :class => "text_field")
786
- %span.description unique name for your group or organization for the new account
787
-
788
- - if ::Milia.use_coupon
789
- .group
790
- = label_tag( 'coupon', 'Coupon code', :class => "label" )
791
- = text_field_tag( "coupon[coupon]", @coupon.to_s, :size => 8, :class => "text_field" )
792
- %span.description optional promotional code, if any
793
-
794
- - if ::Milia.use_recaptcha
795
- = recaptcha_tags( :display => { :theme => 'clean', :tabindex => 0 } )
796
-
797
- .group.navform.wat-cf
798
- %button.button{ :type => "submit" }
799
- = image_tag "web-app-theme/icons/tick.png"
800
- Sign up
801
- = render :partial => "devise/shared/links"
802
-
803
- ```
804
-
805
- ### Alternate use case: user belongs to multiple tenants
806
-
807
- Your application might allow a user to belong to multiple tenants. You will need
808
- to provide some type of mechanism to allow the user to choose which account
809
- (thus tenant) they wish to access. Once chosen, in your controller, you will need
810
- to put:
811
-
812
- <i>app/controllers/any_controller.rb</i>
813
-
814
- ```ruby
815
- set_current_tenant( new_tenant_id )
816
- ```
817
-
818
- ## joins might require additional tenanting restrictions
819
-
820
- Subordinate join tables will not get the Rails default scope.
821
- Theoretically, the default scope on the master table alone should be sufficient
822
- in restricting answers to the current_tenant alone .. HOWEVER, it doesn't feel
823
- right.
824
-
825
- If the master table for the join is a universal table, however, you really *MUST*
826
- use the following workaround, otherwise the database will access data in other
827
- tenanted areas even if no records are returned. This is a potential security
828
- breach. Further details can be found in various discussions about the
829
- behavior of databases such as POSTGRES.
830
-
831
- The milia workaround is to add an additional .where( where_restrict_tenant(klass1, klass2,...))
832
- for each of the subordinate models in the join.
833
-
834
- ### usage of where_restrict_tenant
835
-
836
- ```ruby
837
- Comment.joins(stuff).where( where_restrict_tenant(Post, Author) ).all
838
- ```
839
-
840
- ## no tenant authorization required controller actions: root_path
841
-
842
- Any controller actions, such as the root_path page, will need to skip the tenant & user authorizations.
843
- For example in <i>app/controllers/home_controller.rb </i> place the following near the top of the controller:
844
-
845
- ```ruby
846
- skip_before_action :authenticate_tenant!, :only => [ :index ]
847
- ```
848
-
849
- ## using tokens for authentication
850
-
851
- My app has certain actions which require a token for authentication, instead of a user
852
- sign-in. These use cases include an icalendar feed for a particular user's assignments
853
- or a generic icalendar feed for all of an organization's events. The tokens are NOT
854
- a general replacement for user sign-in for all actions, but merely to enable a simple
855
- restful API for certain specific actions. This section will explain how to incorporate
856
- token authentication together with milia/devise. Please note that the application
857
- assigns to each user an authentication token for this use, as well as creates a
858
- generic "guest" for the organization itself for accessing the organization-wide action.
859
-
860
- The general scheme is to have a prepend_before_action authenticate_by_token! specified
861
- only for those actions allowed. This action determines the "user" required to proceed
862
- with the action, signs in that user via devise, then falls through to the normal
863
- before_action authenticate_tenant! action which establishes the current_tenant.
864
-
865
- Below are some examples of this (typically the token is passed as the id parameter):
866
-
867
- <i>app/controllers/application_controller</i>
868
- ```ruby
869
- # ------------------------------------------------------------------------------
870
- # NOTE: be sure to use prepend_before_action authenticate_by_token!
871
- # so that this will occur BEFORE authenticate_tenant!
872
- # ------------------------------------------------------------------------------
873
- # Notice we are passing store false, so the user is not
874
- # actually stored in the session and a token is needed for every request.
875
- # ------------------------------------------------------------------------------
876
- def authenticate_by_token!
877
- # special case for designated actions only
878
- if ( controller_name == "feeder" &&
879
- ( user = User.find_user_by_user_feed( params ) )
880
- ) ||
881
- ( controller_name == "questions" && ['signup_form', 'finish_signup'].include?(action_name) &&
882
- ( user = User.find_user_by_user_feed( params ) )
883
- )
884
-
885
- # create a special session after authorizing a user
886
- reset_session
887
- sign_in(user, store: false) # devise's way to signin the user
888
- # now continue with tenant authorization & set up
889
- true # ok to continue processing
890
-
891
- else
892
- act_path = controller_name.to_s + '/' + action_name.to_s
893
- 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)
894
- render( :nothing => true, :status => :forbidden) # redirect_back # go back to where you were
895
- nil # abort further processing
896
- end
897
-
898
- end
899
-
900
- ```
901
- <i>app/controllers/feeder_controller</i>
902
- ```ruby
903
- prepend_before_action :authenticate_by_token! # special authtentication by html token
904
- ```
905
-
906
- <i>app/models/user.rb</i>
907
- ```ruby
908
- # ------------------------------------------------------------------------
909
- # find_user_by_user_feed -- returns a user based on auth code from params
910
- # ------------------------------------------------------------------------
911
- def self.find_user_by_user_feed( params )
912
- # can get auth by either :userfeed or :id
913
- key = ( params[:userfeed].blank? ? params[:id] : params[:userfeed] )
914
- return nil if key.blank? # neither key present; invalid
915
- return User.where( :authentication_token => key ).first # find by the key; nil if invalid
916
- end
917
-
918
- def make_authentication_token
919
- self.authentication_token = generate_unique_authentication_token
920
- end
921
-
922
- def generate_unique_authentication_token
923
- loop do
924
- token = AuthKey.make_token # this can be anything to generate a random large token
925
- break token unless User.where(authentication_token: token).first
926
- end
927
- end
928
- ```
929
-
930
-
931
- ## console
932
-
933
- Note that even when running the console, ($ rails console) it will be run in
934
- multi-tenanting mode. You will need to establish a current_user and
935
- setup the current_tenant, otherwise most Model DB accesses will fail.
936
-
937
- For the author's own application, I have set up a small ruby file which I
938
- load when I start the console. This does the following:
939
-
940
- ```ruby
941
- def change_tenant(my_id,my_tenant_id)
942
- @me = User.find( my_id )
943
- @w = Tenant.find( my_tenant_id )
944
- Tenant.set_current_tenant @w
945
- end
946
-
947
- change_tenant(1,1) # or whatever is an appropriate starting user, tenant
948
- ```
949
-
950
- ## Whitelisting additional parameters for tenant/user/coupon
951
-
952
- During the Tenant.create_new_tenant part of the sign-up process, three
953
- sets of whitelisted parameters are passed to the method: The parameters
954
- for tenant, user, and coupon. But some applications might require more or
955
- other parameters than the ones expected by milia. Sometimes the application
956
- might need to add some parameters of its own, such a EULA version number,
957
- additions to an activation message, or a unique name for the tenant itself.
958
-
959
- Milia has a mechanism to add additional parameters to be whitelisted.
960
- In <i>config/initializers/milia.rb</i> you can add a list of symbols for
961
- the additional parameters to each of a config setting for any of the
962
- three (tenant, user, or coupon). The example below shows how.
963
-
964
- ```ruby
965
- # whitelist user params list
966
- # allows an app to expand the permitted attribute list
967
- # specify each attribute as a symbol
968
- # example: [:name]
969
- config.whitelist_user_params = [:eula_id, :message]
970
-
971
- # whitelist tenant params list
972
- # allows an app to expand the permitted attribute list
973
- # specify each attribute as a symbol
974
- # example: [:name]
975
- config.whitelist_tenant_params = [:company, :cname]
976
-
977
- # whitelist coupon params list
978
- # allows an app to expand the permitted attribute list
979
- # specify each attribute as a symbol
980
- # example: [:coupon]
981
- config.whitelist_coupon_params = [:vendor]
982
-
983
- ```
984
-
985
- ## inviting additional user/members
986
-
987
- To keep this discussion simple, we'll give the example of using class Member < Activerecord::Base
988
- which will be a tenanted table for keeping information regarding all the members in a given
989
- organization. The name "Member" is not a requirement of milia. But this is how you would set up an
990
- invite_member capability. It is in this event, that you will require the line in the Tenant
991
- post-processing hook <i>tenant_signup</i> <pre>Member.create_org_admin(user)</pre> which also
992
- creates the Member record for the initial admin on the account.
993
-
994
- ```
995
- $ rails g resource member tenant:references user:references first_name:string last_name:string favorite_color:string
996
- ```
997
-
998
- ADD to <i>app/models/tenant.rb</i>
999
- ```ruby
1000
- has_many :members, dependent: :destroy
1001
- ```
1002
-
1003
- ADD to <i>app/models/user.rb</i>
1004
- ```ruby
1005
- has_one :member, :dependent => :destroy
1006
- ```
1007
-
1008
-
1009
- EDIT <i>app/models/member.rb</i>
1010
- REMOVE belongs_to :tenant
1011
- ADD
1012
- ```ruby
1013
- acts_as_tenant
1014
-
1015
- DEFAULT_ADMIN = {
1016
- first_name: "Admin",
1017
- last_name: "Please edit me"
1018
- }
1019
-
1020
- def self.create_new_member(user, params)
1021
- # add any other initialization for a new member
1022
- return user.create_member( params )
1023
- end
1024
-
1025
- def self.create_org_admin(user)
1026
- new_member = create_new_member(user, DEFAULT_ADMIN)
1027
- unless new_member.errors.empty?
1028
- raise ArgumentError, new_member.errors.full_messages.uniq.join(", ")
1029
- end
1030
-
1031
- return new_member
1032
-
1033
- end
1034
- ```
1035
-
1036
- CREATE a form for inputting new member information for an invite
1037
- (below is a sample only)
1038
- <i>app/views/members/new.html.haml</i>
1039
- ```ruby
1040
- %h1 Simple Milia App
1041
- .block#block-signup
1042
- %h2 Invite a new member into #{@org_name}
1043
- .content.login
1044
- .flash
1045
- - flash.each do |type, message|
1046
- %div{ :class => "message #{type}" }
1047
- %p= message
1048
- - flash.clear # clear contents so we won't see it again
1049
-
1050
- = form_for(@member, :html => { :class => "form login" }) do |f|
1051
- - unless @member.errors.empty? && @user.errors.empty?
1052
- #errorExplanation.group
1053
- %ul
1054
- = @member.errors.full_messages.uniq.inject(''){|str, msg| (str << "<li> #{msg}") }.html_safe
1055
- = @user.errors.full_messages.uniq.inject(''){|str, msg| (str << "<li> #{msg}") }.html_safe
1056
-
1057
- = fields_for( :user ) do |w|
1058
- .group
1059
- = w.label :email, :class => "label "
1060
- = w.text_field :email, :class => "text_field"
1061
- %span.description Ex. test@example.com; must be unique
1062
-
1063
- .group
1064
- = f.label :first_name, :class => "label "
1065
- = f.text_field :first_name, :class => "text_field"
1066
-
1067
- .group
1068
- = f.label :last_name, :class => "label "
1069
- = f.text_field :last_name, :class => "text_field"
1070
-
1071
- .group
1072
- = f.label :favorite_color, :class => "label "
1073
- = f.text_field :favorite_color, :class => "text_field"
1074
- %span.description What is your favorite color?
1075
-
1076
- .group.navform.wat-cf
1077
- %button.button{ :type => "submit" }
1078
- = image_tag "web-app-theme/icons/key.png"
1079
- Create user and invite
1080
- ```
1081
-
1082
- ## authorized tenanted user landing page:
1083
-
1084
- You will need a members-only landing page for after someone successfully signs into your app.
1085
- Here is what I typically do:
1086
-
1087
- ```ruby
1088
- # REPLACE the empty def index ... end with following ADD:
1089
- # this will give you improved handling for letting user know
1090
- # what is expected. If you want to have a welcome page for
1091
- # signed in users, uncomment the redirect_to line, etc.
1092
- def index
1093
- if user_signed_in?
1094
-
1095
- # was there a previous error msg carry over? make sure it shows in flasher
1096
- flash[:notice] = flash[:error] unless flash[:error].blank?
1097
- redirect_to( welcome_path() )
1098
-
1099
- else
1100
-
1101
- if flash[:notice].blank?
1102
- flash[:notice] = "sign in if your organization has an account"
1103
- end
1104
-
1105
- end # if logged in .. else first time
1106
-
1107
- end
1108
-
1109
- def welcome
1110
- end
1111
-
1112
- ```
1113
-
1114
- ## Milia API Reference Manual
1115
-
1116
- ### From controller-levels:
1117
-
1118
- ```ruby
1119
- set_current_tenant( tenant_id )
1120
- # raise InvalidTenantAccess unless tenant_id is one of the current_user valid tenants
1121
- ```
1122
-
1123
- set_current_tenant can be used to change the current_tenanted (for example, if a member
1124
- can belong to multiple tenants and wants to switch between them). See example else in this
1125
- README. NOTE: you will normally NEVER use this. Milia does this automatically during
1126
- authorize_tenant! so you never should at the beginning of a session.
1127
-
1128
- ### From model-levels:
1129
- ```ruby
1130
- Tenant.current_tenant -- returns tenant object for the current tenant; nil if none
1131
-
1132
- Tenant.current_tenant_id -- returns tenant_id for the current tenant; nil if none
1133
- ```
1134
-
1135
- If you need to gain access to tenant object itself (say to get the name of the tenant),
1136
- then use these accessor methods.
1137
-
1138
- ### From background, rake, or console-level (CAUTION):
1139
-
1140
- From background jobs (only at the start of the task);
1141
- tenant can either be a tenant object or an integer tenant_id; anything else will raise
1142
- exception. set_current_tenant -- is model-level ability to set the current tenant
1143
- NOTE: *USE WITH CAUTION* normally this should *NEVER* be done from
1144
- the models ... it is only useful and safe WHEN performed at the start
1145
- of a background job (DelayedJob#perform) or at start of rails console, or a rake task.
1146
-
1147
- ```ruby
1148
- Tenant.set_current_tenant( tenant )
1149
- raise ArgumentError, "invalid tenant object or id"
1150
- ```
1151
-
1152
-
1153
- ## running tests
1154
-
1155
- You must cd into the milia/test directory.
1156
- Then run test:units, test:functionals seperately.
1157
- For some reason, rake test won't work and yields errors.
1158
-
1159
- ```ruby
1160
- $ cd test
1161
- $ rake db:create
1162
- $ rake db:migrate
1163
- $ rake db:test:prepare
1164
- $ rake test:units
1165
- $ rake test:functionals
1166
- ```
1167
-
1168
- ### test coverage
1169
- * All models, including milia-added methods, are tested.
1170
- * Functional testing currently covers all milia-added controller methods.
1171
- * TBD: milia overrides of devise registration, confirmation controllers
1172
-
1173
- ## Cautions
1174
-
1175
- * Milia designates a default_scope for all models (both universal and tenanted). From Rails 3.2 onwards, the last designated default scope overrides any prior scopes and will invalidate multi-tenanting; so *DO NOT USE default_scope*
1176
- * Milia uses Thread.current[:tenant_id] to hold the current tenant for the existing Action request in the application.
1177
- * SQL statements executed outside the context of ActiveRecord pose a potential danger; the current milia implementation does not extend to the DB connection level and so cannot enforce tenanting at this point.
1178
- * The tenant_id of a universal model will always be forced to nil.
1179
- * The tenant_id of a tenanted model will be set to the current_tenant of the current_user upon creation.
1180
- * HABTM (has_and_belongs_to_many) associations don't have models; they shouldn't have id fields
1181
- (setup as below) nor any field other than the joined references; they don't have a tenant_id field;
1182
- rails will invoke the default_scope of the appropriate joined table which does have a tenant_id field.
1183
-
1184
-
1185
- ## Further documentation
1186
-
1187
- * Check out the three-part blog discussion of _Multi-tenanting Ruby on Rails Applications on Heroku_
1188
- at: http://myrailscraft.blogspot.com/2013/05/multi-tenanting-ruby-on-rails.html
1189
- * See the Milia tutorial at: http://myrailscraft.blogspot.com/2013/05/multi-tenanting-ruby-on-rails_3982.html
1190
- * see code & setup sample in test/railsapp, which is also used to run the tests.
1191
- * see milia wiki on github for a CHANGE HISTORY page.
1192
-
1193
-
1194
- ## Contributing to milia
1195
-
1196
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
1197
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
1198
- * Fork the project
1199
- * Start a feature/bugfix branch
1200
- * Commit and push until you are happy with your contribution
1201
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
1202
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
1203
-
1204
- ## Copyright
1205
-
1206
- Copyright (c) 2014 Daudi Amani. See LICENSE.txt for further details.
1207
-