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.
- checksums.yaml +4 -4
- data/app/controllers/confirmations_controller.rb +7 -7
- data/app/controllers/registrations_controller.rb +13 -19
- data/app/views/devise/registrations/new.html.haml +53 -0
- data/lib/generators/milia/install_generator.rb +44 -41
- data/lib/generators/milia/templates/devise_permitted_parameters.rb +18 -0
- data/lib/generators/milia/templates/initializer.rb +3 -9
- data/lib/milia.rb +3 -22
- data/lib/milia/base.rb +88 -109
- data/lib/milia/control.rb +25 -25
- data/lib/milia/invite_member.rb +6 -6
- data/lib/milia/version.rb +1 -1
- metadata +16 -184
- data/.document +0 -5
- data/.gitignore +0 -94
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.slugignore +0 -2
- data/.travis.yml +0 -4
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -20
- data/README.md +0 -1207
- data/Rakefile +0 -1
- data/doc/gemfile_addition.txt +0 -28
- data/doc/manual_sample.sh +0 -816
- data/doc/ref_notes.txt +0 -155
- data/doc/sample.sh +0 -276
- data/markdown.rb +0 -38
- data/milia.gemspec +0 -29
- data/test/.ruby-gemset +0 -1
- data/test/.ruby-version +0 -1
- data/test/Gemfile +0 -81
- data/test/Gemfile.lock +0 -223
- data/test/README.md +0 -83
- data/test/Rakefile +0 -6
- data/test/app/assets/javascripts/application.js +0 -16
- data/test/app/assets/stylesheets/application.css +0 -13
- data/test/app/controllers/application_controller.rb +0 -13
- data/test/app/controllers/home_controller.rb +0 -10
- data/test/app/helpers/application_helper.rb +0 -2
- data/test/app/models/member.rb +0 -34
- data/test/app/models/post.rb +0 -14
- data/test/app/models/team.rb +0 -9
- data/test/app/models/team_asset.rb +0 -6
- data/test/app/models/tenant.rb +0 -54
- data/test/app/models/user.rb +0 -14
- data/test/app/models/zine.rb +0 -8
- data/test/app/views/home/index.html.erb +0 -2
- data/test/app/views/home/show.html.erb +0 -2
- data/test/app/views/layouts/application.html.erb +0 -14
- data/test/bin/bundle +0 -3
- data/test/bin/rails +0 -4
- data/test/bin/rake +0 -4
- data/test/config/application.rb +0 -36
- data/test/config/boot.rb +0 -4
- data/test/config/database.yml +0 -25
- data/test/config/environment.rb +0 -5
- data/test/config/environments/development.rb +0 -48
- data/test/config/environments/production.rb +0 -95
- data/test/config/environments/test.rb +0 -42
- data/test/config/initializers/backtrace_silencers.rb +0 -7
- data/test/config/initializers/devise.rb +0 -254
- data/test/config/initializers/filter_parameter_logging.rb +0 -4
- data/test/config/initializers/inflections.rb +0 -16
- data/test/config/initializers/milia.rb +0 -51
- data/test/config/initializers/mime_types.rb +0 -5
- data/test/config/initializers/secret_token.rb +0 -12
- data/test/config/initializers/session_store.rb +0 -3
- data/test/config/initializers/wrap_parameters.rb +0 -14
- data/test/config/locales/en.yml +0 -23
- data/test/config/routes.rb +0 -77
- data/test/db/migrate/20111012050200_add_sessions_table.rb +0 -12
- data/test/db/migrate/20111012050340_devise_create_users.rb +0 -48
- data/test/db/migrate/20111012050532_create_tenants.rb +0 -11
- data/test/db/migrate/20111012050600_create_tenants_users_join_table.rb +0 -8
- data/test/db/migrate/20111012050650_create_members.rb +0 -12
- data/test/db/migrate/20111012231923_create_posts.rb +0 -12
- data/test/db/migrate/20111013050657_create_zines.rb +0 -10
- data/test/db/migrate/20111013050753_create_teams.rb +0 -10
- data/test/db/migrate/20111013050837_create_team_assets.rb +0 -11
- data/test/db/schema.rb +0 -126
- data/test/db/seeds.rb +0 -7
- data/test/test/controllers/home_controller_test.rb +0 -132
- data/test/test/ctlr_test_helper.rb +0 -11
- data/test/test/fixtures/members.yml +0 -35
- data/test/test/fixtures/posts.yml +0 -96
- data/test/test/fixtures/team_assets.yml +0 -30
- data/test/test/fixtures/teams.yml +0 -17
- data/test/test/fixtures/tenants.yml +0 -12
- data/test/test/fixtures/tenants_users.yml +0 -15
- data/test/test/fixtures/users.yml +0 -33
- data/test/test/fixtures/zines.yml +0 -25
- data/test/test/models/member_test.rb +0 -75
- data/test/test/models/post_test.rb +0 -66
- data/test/test/models/team_test.rb +0 -49
- data/test/test/models/tenant_test.rb +0 -228
- data/test/test/models/user_test.rb +0 -182
- data/test/test/models/zine_test.rb +0 -40
- data/test/test/test_helper.rb +0 -31
data/lib/milia/invite_member.rb
CHANGED
@@ -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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
)
|
57
|
-
|
58
|
-
|
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?
|
data/lib/milia/version.rb
CHANGED
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.
|
4
|
+
version: 1.3.1.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
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: '
|
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: '
|
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: '
|
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: '
|
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:
|
98
|
+
description: Transparent multi-tenanting for web applications based on Rails and Devise
|
112
99
|
email:
|
113
|
-
-
|
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
|
-
|
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:
|
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:
|
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
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
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
milia2
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.1.3
|
data/.slugignore
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/LICENSE.txt
DELETED
@@ -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
|
-
|