stormpath-rails 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +1 -1
  5. data/app/controllers/stormpath/rails/base_controller.rb +34 -0
  6. data/app/controllers/stormpath/rails/forgot_password/create_controller.rb +16 -2
  7. data/app/controllers/stormpath/rails/forgot_password/new_controller.rb +17 -3
  8. data/app/controllers/stormpath/rails/login/create_controller.rb +22 -8
  9. data/app/controllers/stormpath/rails/login/new_controller.rb +11 -1
  10. data/app/controllers/stormpath/rails/register/create_controller.rb +26 -3
  11. data/app/controllers/stormpath/rails/register/new_controller.rb +13 -2
  12. data/app/controllers/stormpath/rails/verify_email/create_controller.rb +16 -2
  13. data/app/controllers/stormpath/rails/verify_email/show_controller.rb +13 -0
  14. data/app/forms/stormpath/rails/login_form.rb +6 -4
  15. data/app/forms/stormpath/rails/organization_form.rb +26 -0
  16. data/app/forms/stormpath/rails/registration_form.rb +37 -6
  17. data/app/forms/stormpath/rails/registration_form_fields.rb +2 -1
  18. data/app/services/stormpath/rails/organization_resolver.rb +40 -0
  19. data/app/services/stormpath/rails/resend_email_verification.rb +11 -3
  20. data/app/services/stormpath/rails/send_password_reset_email.rb +4 -3
  21. data/app/services/stormpath/rails/url_builder.rb +34 -0
  22. data/app/views/stormpath/rails/forgot_password/_form.html.erb +24 -0
  23. data/app/views/stormpath/rails/forgot_password/new.html.erb +6 -24
  24. data/app/views/stormpath/rails/login/new.html.erb +7 -3
  25. data/app/views/stormpath/rails/register/new.html.erb +5 -1
  26. data/app/views/stormpath/rails/shared/_select_organization.html.erb +30 -0
  27. data/app/views/stormpath/rails/verify_email/_form.html.erb +31 -0
  28. data/app/views/stormpath/rails/verify_email/new.html.erb +7 -31
  29. data/docs/changelog.rst +6 -0
  30. data/lib/generators/stormpath/install/templates/default_config.yml +16 -0
  31. data/lib/stormpath/rails.rb +1 -0
  32. data/lib/stormpath/rails/config/dynamic_configuration.rb +5 -0
  33. data/lib/stormpath/rails/config/multitenancy_verification.rb +37 -0
  34. data/lib/stormpath/rails/version.rb +1 -1
  35. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7fca6f7193b201914ce4e96ac41841eb07ef8c4
4
- data.tar.gz: 984fbe34b21d5ed95633f5b2f6307340498e096b
3
+ metadata.gz: 88eef9432856e3264850f71a1cfb8c436e351645
4
+ data.tar.gz: 26ec1cf47d26e18ebd6a87fac8a9004fc866d15a
5
5
  SHA512:
6
- metadata.gz: c42251546fc2a3ec727ec8153af72af540baf4dac8fe09098d26b10a4fd6316eaee743e1ccf30a7c26e4c1f26c2b7a53ce18182c6d00dfc2e4c39f8e6ab98d5e
7
- data.tar.gz: 31be84edd25ac71931b79b61737468c6e7be73022dd0f8343b4901924830f59974be2cba2fea327d07c7ad318762dbfc6bcab04b99b81652284f0853df25ea86
6
+ metadata.gz: 189c34db894a9640d635fbd00f87206e8cbad70c471e20d8e0dbc003f0a932115e9bd963db93d704bc54c007421efa51ff65860d06ea862e847ed86a1ec60514
7
+ data.tar.gz: 092c1446f6cf3369d4a8517ea0f297ec94ec10c62116a50d14f71de4e1306b74c73c8e77a5c46de62a3a1678c484ff56fb55b8bae4a3de86514b4582ec12a9ae
data/.gitignore CHANGED
@@ -12,4 +12,5 @@ spec/dummy/db/development.sqlite3
12
12
  spec/dummy/log/development.log
13
13
  spec/dummy/log/test.log
14
14
  spec/dummy/tmp
15
+ spec/dummy/.powder
15
16
  docs/_build
@@ -1,6 +1,12 @@
1
1
  Stormpath Rails Changelog
2
2
  ============================
3
3
 
4
+ Version 2.5.0
5
+ -------------
6
+ Released on Jan 09, 2017
7
+ - Implement Multi-tenancy
8
+
9
+
4
10
  Version 2.4.0
5
11
  -------------
6
12
  Released on Dec 16, 2016
data/README.md CHANGED
@@ -16,7 +16,7 @@ Add the stormpath-rails integration gem to your Gemfile.
16
16
  Stormpath is currently in beta so it is necessary to include the gem version:
17
17
 
18
18
  ```ruby
19
- gem 'stormpath-rails', '~> 2.4.0'
19
+ gem 'stormpath-rails', '~> 2.5.0'
20
20
  ```
21
21
 
22
22
  Bundle the Gemfile
@@ -24,6 +24,40 @@ module Stormpath
24
24
  request.format =
25
25
  ContentTypeNegotiator.new(request.headers['HTTP_ACCEPT']).convert_to_symbol
26
26
  end
27
+
28
+ def organization_resolution?
29
+ params.key?(:organization_resolution)
30
+ end
31
+
32
+ def organization_unresolved?
33
+ stormpath_config.web.multi_tenancy.enabled &&
34
+ not_on_parent_domain? &&
35
+ current_organization_name_key.nil?
36
+ end
37
+
38
+ def not_on_parent_domain?
39
+ req.host != stormpath_config.web.domain_name
40
+ end
41
+
42
+ def current_organization
43
+ if stormpath_config.web.multi_tenancy.enabled
44
+ Stormpath::Rails::OrganizationResolver.new(req, params[:organization_name_key])
45
+ .organization
46
+ end
47
+ end
48
+
49
+ def current_organization_name_key
50
+ begin
51
+ current_organization.try(:name_key)
52
+ rescue Stormpath::Rails::OrganizationResolver::Error
53
+ nil
54
+ end
55
+ end
56
+ helper_method :current_organization_name_key
57
+
58
+ def req
59
+ request
60
+ end
27
61
  end
28
62
  end
29
63
  end
@@ -4,11 +4,15 @@ module Stormpath
4
4
  class CreateController < Stormpath::Rails::BaseController
5
5
  def call
6
6
  begin
7
- SendPasswordResetEmail.new(params[:email]).call
7
+ if organization_resolution?
8
+ OrganizationForm.new(params[:organization_name_key]).save!
9
+ else
10
+ SendPasswordResetEmail.new(params[:email], current_organization).call
11
+ end
8
12
  respond_with_success
9
13
  rescue SendPasswordResetEmail::UnexistingEmailError
10
14
  respond_with_success
11
- rescue SendPasswordResetEmail::NoEmailError => error
15
+ rescue SendPasswordResetEmail::NoEmailError, Stormpath::Rails::OrganizationForm::FormError => error
12
16
  respond_with_error(error)
13
17
  end
14
18
  end
@@ -16,6 +20,8 @@ module Stormpath
16
20
  private
17
21
 
18
22
  def respond_with_success
23
+ return redirect_to subdomain_forgot_password_url if organization_resolution?
24
+
19
25
  respond_to do |format|
20
26
  format.html { redirect_to stormpath_config.web.forgot_password.next_uri }
21
27
  format.json { render nothing: true, status: 200 }
@@ -31,6 +37,14 @@ module Stormpath
31
37
  end
32
38
  end
33
39
  end
40
+
41
+ def subdomain_forgot_password_url
42
+ UrlBuilder.create(
43
+ req,
44
+ "#{params[:organization_name_key]}.#{stormpath_config.web.domain_name}",
45
+ stormpath_config.web.forgot_password.uri
46
+ )
47
+ end
34
48
  end
35
49
  end
36
50
  end
@@ -3,11 +3,25 @@ module Stormpath
3
3
  module ForgotPassword
4
4
  class NewController < Stormpath::Rails::BaseController
5
5
  def call
6
- respond_to do |format|
7
- format.json { render nothing: true, status: 404 }
8
- format.html { render stormpath_config.web.forgot_password.view }
6
+ if organization_unresolved?
7
+ redirect_to(parent_forgot_password_url)
8
+ else
9
+ respond_to do |format|
10
+ format.json { render nothing: true, status: 404 }
11
+ format.html { render stormpath_config.web.forgot_password.view }
12
+ end
9
13
  end
10
14
  end
15
+
16
+ private
17
+
18
+ def parent_forgot_password_url
19
+ UrlBuilder.create(
20
+ req,
21
+ stormpath_config.web.domain_name,
22
+ stormpath_config.web.forgot_password.uri
23
+ )
24
+ end
11
25
  end
12
26
  end
13
27
  end
@@ -7,9 +7,9 @@ module Stormpath
7
7
  def call
8
8
  begin
9
9
  form.save!
10
- set_cookies unless social_login?
10
+ set_cookies if account_login?
11
11
  respond_with_success
12
- rescue Stormpath::Error, LoginForm::FormError, SocialLoginForm::FormError => error
12
+ rescue Stormpath::Error, LoginForm::FormError, SocialLoginForm::FormError, OrganizationForm::FormError, OrganizationResolver::Error => error
13
13
  respond_with_error(error)
14
14
  end
15
15
  end
@@ -19,12 +19,18 @@ module Stormpath
19
19
  def form
20
20
  @form ||= if social_login?
21
21
  SocialLoginForm.new(provider, access_token, cookies)
22
+ elsif organization_resolution?
23
+ OrganizationForm.new(params[:organization_name_key])
22
24
  else
23
- LoginForm.new(params[:login], params[:password])
25
+ LoginForm.new(params[:login],
26
+ params[:password],
27
+ organization_name_key: current_organization.try(:name_key))
24
28
  end
25
29
  end
26
30
 
27
31
  def respond_with_success
32
+ return redirect_to subdomain_login_url if organization_resolution?
33
+
28
34
  respond_to do |format|
29
35
  format.html { redirect_to login_redirect_route, notice: 'Successfully signed in' }
30
36
  format.json { render json: serialized_account }
@@ -52,11 +58,7 @@ module Stormpath
52
58
  end
53
59
 
54
60
  def login_redirect_route
55
- if params[:next]
56
- URI(params[:next]).path
57
- else
58
- stormpath_config.web.login.next_uri
59
- end
61
+ params[:next] ? URI(params[:next]).path : stormpath_config.web.login.next_uri
60
62
  end
61
63
 
62
64
  def provider
@@ -67,9 +69,21 @@ module Stormpath
67
69
  params[:providerData][:accessToken]
68
70
  end
69
71
 
72
+ def account_login?
73
+ !social_login? && !organization_resolution?
74
+ end
75
+
70
76
  def social_login?
71
77
  params[:providerData].present?
72
78
  end
79
+
80
+ def subdomain_login_url
81
+ UrlBuilder.create(
82
+ req,
83
+ "#{params[:organization_name_key]}.#{stormpath_config.web.domain_name}",
84
+ stormpath_config.web.login.uri
85
+ )
86
+ end
73
87
  end
74
88
  end
75
89
  end
@@ -6,7 +6,9 @@ module Stormpath
6
6
 
7
7
  def call
8
8
  if stormpath_config.web.id_site.enabled
9
- redirect_to callback_url
9
+ redirect_to(callback_url)
10
+ elsif organization_unresolved?
11
+ redirect_to(parent_login_url)
10
12
  else
11
13
  respond_to do |format|
12
14
  format.json { render json: LoginNewSerializer.to_h }
@@ -23,6 +25,14 @@ module Stormpath
23
25
  path: Stormpath::Rails.config.web.id_site.login_uri
24
26
  )
25
27
  end
28
+
29
+ def parent_login_url
30
+ UrlBuilder.create(
31
+ req,
32
+ stormpath_config.web.domain_name,
33
+ stormpath_config.web.login.uri
34
+ )
35
+ end
26
36
  end
27
37
  end
28
38
  end
@@ -4,15 +4,17 @@ module Stormpath
4
4
  class CreateController < BaseController
5
5
  def call
6
6
  form.save!
7
- login_the_account if auto_login_enabled? && !email_verification_enabled?
7
+ login_the_account if valid_for_login?
8
8
  respond_with_success
9
- rescue RegistrationForm::FormError => error
9
+ rescue RegistrationForm::FormError, OrganizationForm::FormError => error
10
10
  respond_with_error(error)
11
11
  end
12
12
 
13
13
  private
14
14
 
15
15
  def respond_with_success
16
+ return redirect_to subdomain_register_url if organization_resolution?
17
+
16
18
  respond_to do |format|
17
19
  format.html { redirect_to success_redirect_route }
18
20
  format.json { render json: serialized_account }
@@ -57,6 +59,10 @@ module Stormpath
57
59
  render stormpath_config.web.register.view
58
60
  end
59
61
 
62
+ def valid_for_login?
63
+ auto_login_enabled? && !email_verification_enabled? && !organization_resolution?
64
+ end
65
+
60
66
  def auto_login_enabled?
61
67
  stormpath_config.web.register.auto_login
62
68
  end
@@ -74,12 +80,29 @@ module Stormpath
74
80
  end
75
81
 
76
82
  def form
77
- @form ||= RegistrationForm.new(params.except(*excluded_root_params))
83
+ @form ||= if organization_resolution?
84
+ OrganizationForm.new(params[:organization_name_key])
85
+ else
86
+ RegistrationForm.new(permitted_params)
87
+ end
78
88
  end
79
89
 
80
90
  def excluded_root_params
81
91
  [:controller, :action, :format, :create, :utf8, :button, :authenticity_token]
82
92
  end
93
+
94
+ def permitted_params
95
+ params.except(*excluded_root_params)
96
+ .merge(organization_name_key: current_organization_name_key)
97
+ end
98
+
99
+ def subdomain_register_url
100
+ UrlBuilder.create(
101
+ req,
102
+ "#{params[:organization_name_key]}.#{stormpath_config.web.domain_name}",
103
+ stormpath_config.web.register.uri
104
+ )
105
+ end
83
106
  end
84
107
  end
85
108
  end
@@ -2,11 +2,14 @@ module Stormpath
2
2
  module Rails
3
3
  module Register
4
4
  class NewController < BaseController
5
+
5
6
  def call
6
7
  if stormpath_config.web.id_site.enabled
7
- redirect_to callback_url
8
+ redirect_to(callback_url)
8
9
  elsif signed_in?
9
- redirect_to root_path
10
+ redirect_to(root_path)
11
+ elsif organization_unresolved?
12
+ redirect_to(parent_register_url)
10
13
  else
11
14
  respond_to do |format|
12
15
  format.json { render json: RegistrationFormSerializer.to_h }
@@ -23,6 +26,14 @@ module Stormpath
23
26
  path: Stormpath::Rails.config.web.id_site.register_uri
24
27
  )
25
28
  end
29
+
30
+ def parent_register_url
31
+ UrlBuilder.create(
32
+ req,
33
+ stormpath_config.web.domain_name,
34
+ stormpath_config.web.register.uri
35
+ )
36
+ end
26
37
  end
27
38
  end
28
39
  end
@@ -4,11 +4,15 @@ module Stormpath
4
4
  class CreateController < BaseController
5
5
  def call
6
6
  begin
7
- ResendEmailVerification.new(params[:email]).call
7
+ if organization_resolution?
8
+ OrganizationForm.new(params[:organization_name_key]).save!
9
+ else
10
+ ResendEmailVerification.new(params[:email], current_organization).call
11
+ end
8
12
  respond_with_success
9
13
  rescue ResendEmailVerification::UnexistingEmailError
10
14
  respond_with_success
11
- rescue ResendEmailVerification::NoEmailError => error
15
+ rescue ResendEmailVerification::NoEmailError, Stormpath::Rails::OrganizationForm::FormError => error
12
16
  respond_with_error(error)
13
17
  end
14
18
  end
@@ -16,6 +20,8 @@ module Stormpath
16
20
  private
17
21
 
18
22
  def respond_with_success
23
+ return redirect_to subdomain_verify_email_url if organization_resolution?
24
+
19
25
  respond_to do |format|
20
26
  format.html { redirect_to "#{stormpath_config.web.login.uri}?status=unverified" }
21
27
  format.json { render nothing: true }
@@ -31,6 +37,14 @@ module Stormpath
31
37
  end
32
38
  end
33
39
  end
40
+
41
+ def subdomain_verify_email_url
42
+ UrlBuilder.create(
43
+ req,
44
+ "#{params[:organization_name_key]}.#{stormpath_config.web.domain_name}",
45
+ stormpath_config.web.verify_email.uri
46
+ )
47
+ end
34
48
  end
35
49
  end
36
50
  end
@@ -4,8 +4,11 @@ module Stormpath
4
4
  class ShowController < BaseController
5
5
  def call
6
6
  begin
7
+ return redirect_to(parent_verify_email_url) if organization_unresolved?
8
+
7
9
  account = VerifyEmailToken.new(params[:sptoken]).call
8
10
  login_the_account(account) if stormpath_config.web.register.auto_login
11
+
9
12
  respond_with_success
10
13
  rescue InvalidSptokenError, NoSptokenError => error
11
14
  respond_to_error(error)
@@ -23,6 +26,8 @@ module Stormpath
23
26
  end
24
27
 
25
28
  def respond_with_success
29
+ return redirect_to parent_verify_email_url if organization_resolution?
30
+
26
31
  respond_to do |format|
27
32
  format.html { redirect_to success_redirect_route }
28
33
  format.json { render nothing: true, status: 200 }
@@ -45,6 +50,14 @@ module Stormpath
45
50
  end
46
51
  end
47
52
  end
53
+
54
+ def parent_verify_email_url
55
+ UrlBuilder.create(
56
+ req,
57
+ stormpath_config.web.domain_name,
58
+ stormpath_config.web.verify_email.uri
59
+ )
60
+ end
48
61
  end
49
62
  end
50
63
  end
@@ -1,13 +1,13 @@
1
1
  module Stormpath
2
2
  module Rails
3
3
  class LoginForm
4
- attr_accessor :login, :password
5
- attr_accessor :authentication_result
4
+ attr_accessor :login, :password, :authentication_result, :organization_name_key
6
5
  delegate :account, to: :authentication_result
7
6
 
8
- def initialize(login, password)
7
+ def initialize(login, password, options = {})
9
8
  @login = login
10
9
  @password = password
10
+ @organization_name_key = options[:organization_name_key]
11
11
  validate_login_presence
12
12
  validate_password_presence
13
13
  end
@@ -39,7 +39,9 @@ module Stormpath
39
39
  end
40
40
 
41
41
  def password_grant_request
42
- Stormpath::Oauth::PasswordGrantRequest.new(login, password)
42
+ Stormpath::Oauth::PasswordGrantRequest.new(login,
43
+ password,
44
+ organization_name_key: organization_name_key)
43
45
  end
44
46
 
45
47
  def application
@@ -0,0 +1,26 @@
1
+ module Stormpath
2
+ module Rails
3
+ class OrganizationForm
4
+ attr_reader :name_key
5
+
6
+ def initialize(name_key)
7
+ @name_key = name_key
8
+ raise FormError, "Organization Name Key can't be blank" if name_key.blank?
9
+ end
10
+
11
+ class FormError < ArgumentError
12
+ def status
13
+ 400
14
+ end
15
+ end
16
+
17
+ def save!
18
+ begin
19
+ Stormpath::Rails::OrganizationResolver.new(OpenStruct.new(subdomains: [name_key])).organization
20
+ rescue Stormpath::Rails::OrganizationResolver::Error
21
+ raise FormError, 'Organization could not be found'
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -3,7 +3,7 @@ module Stormpath
3
3
  class RegistrationForm
4
4
  include ActiveModel::Model
5
5
  attr_accessor(*RegistrationFormFields.enabled_field_names)
6
- attr_accessor :account
6
+ attr_accessor :account, :organization_name_key
7
7
 
8
8
  validate :validate_presence_of_required_attributes
9
9
  validate :validate_password_repeated_twice_matches?
@@ -22,7 +22,7 @@ module Stormpath
22
22
  params = params.except(:customData).merge(custom_data_params)
23
23
  params = params.stringify_keys.transform_keys(&:underscore).symbolize_keys
24
24
 
25
- arbitrary_param_names = params.keys - RegistrationFormFields.enabled_field_names
25
+ arbitrary_param_names = params.keys - RegistrationFormFields.enabled_field_names.push(:organization_name_key)
26
26
 
27
27
  if arbitrary_param_names.any?
28
28
  raise ArbitraryDataSubmitted, "Can't submit arbitrary data: #{arbitrary_param_names.join(', ')}"
@@ -35,9 +35,7 @@ module Stormpath
35
35
  return false if invalid?
36
36
 
37
37
  begin
38
- self.account = Stormpath::Rails::Client.application.accounts.create(
39
- Stormpath::Resource::Account.new(stormpath_registration_params)
40
- )
38
+ self.account = account_resource
41
39
  rescue Stormpath::Error => error
42
40
  errors.add(:base, error.message) && false
43
41
  end
@@ -50,6 +48,38 @@ module Stormpath
50
48
 
51
49
  private
52
50
 
51
+ def account_resource
52
+ account_store.accounts.create(new_account)
53
+ end
54
+
55
+ def account_store
56
+ organization_resolved? ? organization : Stormpath::Rails::Client.application
57
+ end
58
+
59
+ def new_account
60
+ Stormpath::Resource::Account.new(stormpath_registration_params)
61
+ end
62
+
63
+ def organization_resolved?
64
+ if multitenancy_enabled?
65
+ organization.present? ? true : raise(FormError, 'Organization not found.')
66
+ else
67
+ false
68
+ end
69
+ end
70
+
71
+ def multitenancy_enabled?
72
+ Stormpath::Rails.config.web.multi_tenancy.enabled
73
+ end
74
+
75
+ def organization
76
+ begin
77
+ @organization ||= Stormpath::Rails::Client.client.organizations.search(name_key: organization_name_key).first
78
+ rescue Stormpath::Error
79
+ nil
80
+ end
81
+ end
82
+
53
83
  def validate_presence_of_required_attributes
54
84
  RegistrationFormFields.required_fields.each do |required_field, properties|
55
85
  if send(required_field).blank?
@@ -65,7 +95,8 @@ module Stormpath
65
95
  end
66
96
 
67
97
  def stormpath_registration_params
68
- predefined_registration_params.merge(custom_data: custom_registration_params)
98
+ predefined_registration_params.merge(custom_data: custom_registration_params,
99
+ organization_name_key: organization_name_key)
69
100
  end
70
101
 
71
102
  def predefined_registration_params
@@ -8,7 +8,8 @@ module Stormpath
8
8
  :username,
9
9
  :email,
10
10
  :password,
11
- :confirm_password
11
+ :confirm_password,
12
+ :organization_name_key
12
13
  ].freeze
13
14
 
14
15
  class << self
@@ -0,0 +1,40 @@
1
+ module Stormpath
2
+ module Rails
3
+ class OrganizationResolver
4
+ attr_reader :subdomain_name, :organization_name_key
5
+
6
+ def initialize(request, organization_name_key = nil)
7
+ @subdomain_name = request.subdomains.first
8
+ @organization_name_key = organization_name_key
9
+ end
10
+
11
+ def organization
12
+ find_organization(subdomain_name) || find_organization(organization_name_key) || raise_error
13
+ end
14
+
15
+ class Error < ArgumentError
16
+ def status
17
+ 400
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def find_organization(name_key)
24
+ return if name_key.nil?
25
+
26
+ application.account_store_mappings.find do |mapping|
27
+ mapping.account_store.try(:name_key) == name_key
28
+ end.try(:account_store)
29
+ end
30
+
31
+ def raise_error
32
+ raise Error, "Organization couldn't be resolved"
33
+ end
34
+
35
+ def application
36
+ Stormpath::Rails::Client.application
37
+ end
38
+ end
39
+ end
40
+ end
@@ -3,16 +3,17 @@ module Stormpath
3
3
  class ResendEmailVerification
4
4
  PROPERTY_VALUE_DOES_NOT_MATCH_A_STORMPATH_RESOURCE_CODE = 2016
5
5
 
6
- attr_reader :email
6
+ attr_reader :email, :account_store
7
7
 
8
- def initialize(email)
8
+ def initialize(email, account_store = nil)
9
9
  raise(NoEmailError, 'Email parameter not provided.') if email.blank?
10
10
  @email = email
11
+ @account_store = account_store
11
12
  end
12
13
 
13
14
  def call
14
15
  begin
15
- application.verification_emails.create(login: email)
16
+ application.verification_emails.create(email_verification_params)
16
17
  rescue Stormpath::Error => error
17
18
  if error.code == PROPERTY_VALUE_DOES_NOT_MATCH_A_STORMPATH_RESOURCE_CODE
18
19
  raise UnexistingEmailError, error.message
@@ -22,6 +23,13 @@ module Stormpath
22
23
  end
23
24
  end
24
25
 
26
+ def email_verification_params
27
+ {}.tap do |body|
28
+ body[:login] = email
29
+ body[:account_store] = { name_key: account_store.name_key } if account_store.present?
30
+ end
31
+ end
32
+
25
33
  def application
26
34
  Stormpath::Rails::Client.application
27
35
  end
@@ -3,16 +3,17 @@ module Stormpath
3
3
  class SendPasswordResetEmail
4
4
  PROPERTY_VALUE_DOES_NOT_MATCH_A_STORMPATH_RESOURCE_CODE = 2016
5
5
 
6
- attr_reader :email
6
+ attr_reader :email, :account_store
7
7
 
8
- def initialize(email)
8
+ def initialize(email, account_store = nil)
9
9
  raise(NoEmailError, 'Email parameter not provided.') if email.blank?
10
10
  @email = email
11
+ @account_store = account_store
11
12
  end
12
13
 
13
14
  def call
14
15
  begin
15
- application.send_password_reset_email(email)
16
+ application.send_password_reset_email(email, account_store: account_store)
16
17
  rescue Stormpath::Error => error
17
18
  if error.code == PROPERTY_VALUE_DOES_NOT_MATCH_A_STORMPATH_RESOURCE_CODE
18
19
  raise UnexistingEmailError, error.message
@@ -0,0 +1,34 @@
1
+ module Stormpath
2
+ module Rails
3
+ class UrlBuilder
4
+ attr_reader :request, :host, :path
5
+
6
+ def initialize(request, host, path)
7
+ @request = request
8
+ @host = host
9
+ @path = path
10
+ end
11
+
12
+ def self.create(request, host, path)
13
+ new(request, host, path).url
14
+ end
15
+
16
+ def url
17
+ request_scheme_builder.build(host_and_path).to_s
18
+ end
19
+
20
+ private
21
+
22
+ def request_scheme_builder
23
+ request.scheme == 'https' ? URI::HTTPS : URI::HTTP
24
+ end
25
+
26
+ def host_and_path
27
+ {
28
+ host: host,
29
+ path: path
30
+ }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ <div class="email-password-area col-xs-12 large col-sm-12">
2
+ <div class="header">
3
+ <span>Forgot your password?</span>
4
+ <p>Enter your email address below to reset your password. You will be sent an email which you will need to open to continue. You may need to check your spam folder.</p>
5
+ </div>
6
+
7
+ <% if flash[:error] %>
8
+ <div class="alert alert-danger bad-login">
9
+ <p><%= flash[:error] %></p>
10
+ </div>
11
+ <% end %>
12
+
13
+ <%= form_for :password, html: { class: "login-form form-horizontal" }, url: forgot_password_path do |form| %>
14
+ <div class="form-group group-email">
15
+ <%= form.label :email, class: "col-sm-4" %>
16
+ <div class="col-sm-8">
17
+ <%= form.email_field :email, placeholder: 'Email', class: "form-control", name: :email %>
18
+ </div>
19
+ </div>
20
+ <div>
21
+ <%= button_tag "Submit", class: "login btn btn-login btn-sp-green", type: "submit" %>
22
+ </div>
23
+ <% end %>
24
+ </div>
@@ -12,31 +12,13 @@
12
12
  <% end %>
13
13
 
14
14
  <div class="box row">
15
- <div class="email-password-area col-xs-12 large col-sm-12">
16
- <div class="header">
17
- <span>Forgot your password?</span>
18
- <p>Enter your email address below to reset your password. You will be sent an email which you will need to open to continue. You may need to check your spam folder.</p>
19
- </div>
20
-
21
- <% if flash[:error] %>
22
- <div class="alert alert-danger bad-login">
23
- <p><%= flash[:error] %></p>
24
- </div>
25
- <% end %>
26
-
27
- <%= form_for :password, html: { class: "login-form form-horizontal" }, url: forgot_password_path do |form| %>
28
- <div class="form-group group-email">
29
- <%= form.label :email, class: "col-sm-4" %>
30
- <div class="col-sm-8">
31
- <%= form.email_field :email, placeholder: 'Email', class: "form-control", name: :email %>
32
- </div>
33
- </div>
34
- <div>
35
- <%= button_tag "Submit", :class => "login btn btn-login btn-sp-green", :type => "submit" %>
36
- </div>
37
- <% end %>
38
- </div>
15
+ <% if Stormpath::Rails.config.web.multi_tenancy.enabled && current_organization_name_key.nil? %>
16
+ <%= render partial: 'stormpath/rails/shared/select_organization', locals: { url: forgot_password_path } %>
17
+ <% else %>
18
+ <%= render partial: 'stormpath/rails/forgot_password/form' %>
19
+ <% end %>
39
20
  </div>
21
+
40
22
  <% if Stormpath::Rails.config.web.login.enabled %>
41
23
  <%= link_to "Back to Log In", new_login_path, class: "forgot" %>
42
24
  <% end %>
@@ -2,10 +2,14 @@
2
2
  <div class="va-wrapper">
3
3
  <div class="view login-view container">
4
4
  <div class="box row">
5
- <%= render partial: 'stormpath/rails/login/form' %>
5
+ <% if Stormpath::Rails.config.web.multi_tenancy.enabled && current_organization_name_key.nil? %>
6
+ <%= render partial: 'stormpath/rails/shared/select_organization', locals: {url: login_path(next: params[:next])} %>
7
+ <% else %>
8
+ <%= render partial: 'stormpath/rails/login/form' %>
6
9
 
7
- <% if Stormpath::Rails.config.web.has_social_providers %>
8
- <%= render 'stormpath/rails/shared/social_login' %>
10
+ <% if Stormpath::Rails.config.web.has_social_providers %>
11
+ <%= render 'stormpath/rails/shared/social_login' %>
12
+ <% end %>
9
13
  <% end %>
10
14
  </div>
11
15
 
@@ -2,7 +2,11 @@
2
2
  <div class="va-wrapper">
3
3
  <div class="view registration-view container">
4
4
  <div class="box row">
5
- <%= render partial: 'stormpath/rails/register/form' %>
5
+ <% if Stormpath::Rails.config.web.multi_tenancy.enabled && current_organization_name_key.nil? %>
6
+ <%= render partial: 'stormpath/rails/shared/select_organization', locals: { url: register_path } %>
7
+ <% else %>
8
+ <%= render partial: 'stormpath/rails/register/form' %>
9
+ <% end %>
6
10
  </div>
7
11
  <% if Stormpath::Rails.config.web.login.enabled %>
8
12
  <%= link_to "Back to Log In", new_login_path, class: "to-login" %>
@@ -0,0 +1,30 @@
1
+ <div class="email-password-area col-xs-12">
2
+ <div class="header">
3
+ <span>Select your Organization</span>
4
+ </div>
5
+
6
+ <% if flash[:error] %>
7
+ <div class="alert alert-danger bad-login">
8
+ <p><%= flash[:error] %></p>
9
+ </div>
10
+ <% end %>
11
+
12
+ <%= form_for '', url: url, html: {class: "login-form form-horizontal"} do |form| %>
13
+ <%= form.hidden_field :organization_resolution %>
14
+ <div class="form-group group-email">
15
+ <%= form.label :organization_name_key, 'Enter your organization name to continue', class: 'col-sm-12' %>
16
+ <div class="<%= input_class %>">
17
+ <%= form.text_field :organization_name_key,
18
+ value: params[:organization_name_key],
19
+ name: :organization_name_key,
20
+ placeholder: 'e.g. my-company',
21
+ required: true,
22
+ class: "form-control"
23
+ %>
24
+ </div>
25
+ </div>
26
+ <div>
27
+ <%= button_tag 'Submit', class: 'login btn btn-login btn-sp-green', type: 'submit' %>
28
+ </div>
29
+ <% end %>
30
+ </div>
@@ -0,0 +1,31 @@
1
+ <div class="email-password-area col-xs-12 large col-sm-12">
2
+ <div class="header">
3
+ <span>Resend Account Verification Email?</span>
4
+ <% if params[:sptoken].nil? %>
5
+ <p>
6
+ Enter your email address below and we'll resend your account
7
+ verification email. You will be sent an email which you will
8
+ need to open to continue. You may need to check your spam
9
+ folder.
10
+ </p>
11
+ <% end %>
12
+ </div>
13
+
14
+ <% if flash[:error] %>
15
+ <div class="alert alert-danger bad-login">
16
+ <p><%= flash[:error] %></p>
17
+ </div>
18
+ <% end %>
19
+
20
+ <%= form_for :verify_email, html: { class: "login-form form-horizontal" }, url: verify_email_path do |form| %>
21
+ <div class="form-group group-email">
22
+ <%= form.label :email, class: "col-sm-4" %>
23
+ <div class="col-sm-8">
24
+ <%= form.text_field :email, placeholder: 'Email', class: "form-control", name: :email, type: :email, required: true %>
25
+ </div>
26
+ </div>
27
+ <div>
28
+ <%= button_tag "Submit", :class => "login btn btn-login btn-sp-green", :type => "submit" %>
29
+ </div>
30
+ <% end %>
31
+ </div>
@@ -10,39 +10,15 @@
10
10
  </div>
11
11
  </div>
12
12
  <% end %>
13
- <div class="box row">
14
- <div class="email-password-area col-xs-12 large col-sm-12">
15
- <div class="header">
16
- <span>Resend Account Verification Email?</span>
17
- <% if params[:sptoken].nil? %>
18
- <p>
19
- Enter your email address below and we'll resend your account
20
- verification email. You will be sent an email which you will
21
- need to open to continue. You may need to check your spam
22
- folder.
23
- </p>
24
- <% end %>
25
- </div>
26
-
27
- <% if flash[:error] %>
28
- <div class="alert alert-danger bad-login">
29
- <p><%= flash[:error] %></p>
30
- </div>
31
- <% end %>
32
13
 
33
- <%= form_for :verify_email, html: { class: "login-form form-horizontal" }, url: verify_email_path do |form| %>
34
- <div class="form-group group-email">
35
- <%= form.label :email, class: "col-sm-4" %>
36
- <div class="col-sm-8">
37
- <%= form.text_field :email, placeholder: 'Email', class: "form-control", name: :email, type: :email, required: true %>
38
- </div>
39
- </div>
40
- <div>
41
- <%= button_tag "Submit", :class => "login btn btn-login btn-sp-green", :type => "submit" %>
42
- </div>
43
- <% end %>
44
- </div>
14
+ <div class="box row">
15
+ <% if Stormpath::Rails.config.web.multi_tenancy.enabled && current_organization_name_key.nil? %>
16
+ <%= render partial: 'stormpath/rails/shared/select_organization', locals: { url: verify_email_path } %>
17
+ <% else %>
18
+ <%= render partial: 'stormpath/rails/verify_email/form' %>
19
+ <% end %>
45
20
  </div>
21
+
46
22
  <% if Stormpath::Rails.config.web.login.enabled %>
47
23
  <%= link_to "Back to Log In", new_login_path, class: "forgot" %>
48
24
  <% end %>
@@ -6,6 +6,12 @@ Change Log
6
6
 
7
7
  Gem changes until version 2.0.1, in descending order.
8
8
 
9
+ Version 2.5.0
10
+ -------------
11
+ Released on Jan 09, 2017
12
+ - Implement Multi-tenancy
13
+
14
+
9
15
  Version 2.4.0
10
16
  -------------
11
17
  Released on Dec 16, 2016
@@ -6,6 +6,22 @@ stormpath:
6
6
  web:
7
7
  basePath: null
8
8
 
9
+ domainName: null # Required if using subdomain-based multi-tenancy
10
+
11
+ multiTenancy:
12
+
13
+ # When enabled, the framework will require the user to authenticate against
14
+ # a specific Organization. The authenticated organization is persisted in
15
+ # the access token that is issued.
16
+ #
17
+ # At the moment we only support a sub-domain based strategy, wherby the
18
+ # user must arrive on the subdomain that correlates with their tenant in
19
+ # order to authenticate. If they visit the parent domain, they will be
20
+ # required to identify the organization they wish to use.
21
+
22
+ enabled: false
23
+ strategy: "subdomain"
24
+
9
25
  oauth2:
10
26
  enabled: true
11
27
  uri: "/oauth/token"
@@ -4,6 +4,7 @@ require 'stormpath/rails/config/read_file'
4
4
  require 'stormpath/rails/config/application_resolution'
5
5
  require 'stormpath/rails/config/account_store_verification'
6
6
  require 'stormpath/rails/config/dynamic_configuration'
7
+ require 'stormpath/rails/config/multitenancy_verification'
7
8
  require 'stormpath/rails/config/social_login_verification'
8
9
  require 'stormpath/rails/configuration'
9
10
  require 'stormpath/rails/router'
@@ -17,6 +17,7 @@ module Stormpath
17
17
  def initialize(static_config)
18
18
  @static_config = static_config
19
19
  proccess_account_store_verification
20
+ process_multitenancy_verification if static_config.stormpath.web.multi_tenancy.enabled
20
21
  end
21
22
 
22
23
  def app
@@ -69,6 +70,10 @@ module Stormpath
69
70
  ).call
70
71
  end
71
72
 
73
+ def process_multitenancy_verification
74
+ MultitenancyVerification.new(static_config.stormpath.web).call
75
+ end
76
+
72
77
  def social_login_verification
73
78
  @social_login_verification ||= SocialLoginVerification.new(app.href)
74
79
  end
@@ -0,0 +1,37 @@
1
+ module Stormpath
2
+ module Rails
3
+ module Config
4
+ class MultitenancyVerification
5
+ attr_reader :web_config
6
+
7
+ def initialize(web_config)
8
+ @web_config = web_config
9
+ end
10
+
11
+ def call
12
+ verify_multitenancy_set_correctly
13
+ end
14
+
15
+ private
16
+
17
+ def verify_multitenancy_set_correctly
18
+ return if multitenancy_set_correctly?
19
+ raise(
20
+ InvalidConfiguration,
21
+ "Multitenancy is not set correctly in the configuration file. It needs to have a domainName and multitenancy strategy set to 'subdomain'"
22
+ )
23
+ end
24
+
25
+ def multitenancy_set_correctly?
26
+ multitenancy.enabled &&
27
+ multitenancy.strategy == 'subdomain' &&
28
+ !web_config.domain_name.nil?
29
+ end
30
+
31
+ def multitenancy
32
+ web_config.multi_tenancy
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Stormpath
3
3
  module Rails
4
- VERSION = '2.4.0'.freeze
4
+ VERSION = '2.5.0'.freeze
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stormpath-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nenad Nikolic
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-12-16 00:00:00.000000000 Z
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: stormpath-sdk
@@ -134,6 +134,7 @@ files:
134
134
  - app/controllers/stormpath/rails/verify_email/create_controller.rb
135
135
  - app/controllers/stormpath/rails/verify_email/show_controller.rb
136
136
  - app/forms/stormpath/rails/login_form.rb
137
+ - app/forms/stormpath/rails/organization_form.rb
137
138
  - app/forms/stormpath/rails/registration_form.rb
138
139
  - app/forms/stormpath/rails/registration_form_fields.rb
139
140
  - app/forms/stormpath/rails/social_login_form.rb
@@ -152,14 +153,17 @@ files:
152
153
  - app/services/stormpath/rails/delete_access_token.rb
153
154
  - app/services/stormpath/rails/delete_refresh_token.rb
154
155
  - app/services/stormpath/rails/forgot_password_token_verification.rb
156
+ - app/services/stormpath/rails/organization_resolver.rb
155
157
  - app/services/stormpath/rails/password_change.rb
156
158
  - app/services/stormpath/rails/refresh_token_authentication.rb
157
159
  - app/services/stormpath/rails/resend_email_verification.rb
158
160
  - app/services/stormpath/rails/send_password_reset_email.rb
159
161
  - app/services/stormpath/rails/token_and_cookies_cleaner.rb
160
162
  - app/services/stormpath/rails/token_cookie_setter.rb
163
+ - app/services/stormpath/rails/url_builder.rb
161
164
  - app/services/stormpath/rails/verify_email_token.rb
162
165
  - app/views/stormpath/rails/change_password/new.html.erb
166
+ - app/views/stormpath/rails/forgot_password/_form.html.erb
163
167
  - app/views/stormpath/rails/forgot_password/new.html.erb
164
168
  - app/views/stormpath/rails/layouts/stormpath.html.erb
165
169
  - app/views/stormpath/rails/login/_form.html.erb
@@ -167,7 +171,9 @@ files:
167
171
  - app/views/stormpath/rails/register/_form.html.erb
168
172
  - app/views/stormpath/rails/register/new.html.erb
169
173
  - app/views/stormpath/rails/shared/_input.html.erb
174
+ - app/views/stormpath/rails/shared/_select_organization.html.erb
170
175
  - app/views/stormpath/rails/shared/_social_login.html.erb
176
+ - app/views/stormpath/rails/verify_email/_form.html.erb
171
177
  - app/views/stormpath/rails/verify_email/new.html.erb
172
178
  - bin/console
173
179
  - bin/rails
@@ -241,6 +247,7 @@ files:
241
247
  - lib/stormpath/rails/config/account_store_verification.rb
242
248
  - lib/stormpath/rails/config/application_resolution.rb
243
249
  - lib/stormpath/rails/config/dynamic_configuration.rb
250
+ - lib/stormpath/rails/config/multitenancy_verification.rb
244
251
  - lib/stormpath/rails/config/read_file.rb
245
252
  - lib/stormpath/rails/config/social_login_verification.rb
246
253
  - lib/stormpath/rails/configuration.rb