sync_attr_with_auth0 0.0.25 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2c2ffc762b00c7c0b684b9569bfee0c4ed3a9316
4
- data.tar.gz: 841110b23f01dd606bfc8c7d08aae62a87bac2f3
3
+ metadata.gz: 472500ed61386a758384a9f866359b1a6e600eb2
4
+ data.tar.gz: 6b8c4af3b271483ac0166386e024083d0627c6dc
5
5
  SHA512:
6
- metadata.gz: 50cb1f6d46d46ec933da1f7b65b5c122cad18f6f7c87087753beb8b434bd76f8b843e273b1c466a1ebfb520f53e005b86c965668d59c1cd997432fb808c5bb92
7
- data.tar.gz: 76f3cd16cf226ed4130f9c926948514412913d811f84f88bcc726e69308716365cc24bbf7ef27d57487759e0ab2ea51d44adc295c65ac703d2628ec2c7f985cf
6
+ metadata.gz: 2eca8c691f9d505910dd4b559545775ee3341194116b0d31b1157893a2f0e4e953b8406252ec6795aaa92703765445f42bb014a0a85dc4ccf1280dcbfec5a83e
7
+ data.tar.gz: 5744cf31a52c72770aaa123971c359bd57e3a7b9ac0879b2b548bd73e80165107497c5aa74da62d7c7c2a2bc345ad5b01952960a286b69ddd6c45d96041772ee
@@ -0,0 +1,198 @@
1
+ module SyncAttrWithAuth0
2
+ module Adapters
3
+ module ActiveRecord
4
+ module Sync
5
+
6
+ def sync_password_with_auth0?
7
+ !!(auth0_attributes_to_sync.index(auth0_sync_configuration.password_attribute))
8
+ end # sync_password_with_auth0?
9
+
10
+
11
+ def sync_email_with_auth0?
12
+ !!(auth0_attributes_to_sync.index(auth0_sync_configuration.email_attribute))
13
+ end # sync_email_with_auth0?
14
+
15
+
16
+ def sync_with_auth0_on_create?
17
+ !!((self.respond_to?(:sync_with_auth0_on_create) and !self.sync_with_auth0_on_create.nil?) ? self.sync_with_auth0_on_create : true)
18
+ end # sync_with_auth0_on_create?
19
+
20
+
21
+ def sync_with_auth0_on_update?
22
+ !!((self.respond_to?(:sync_with_auth0_on_update) and !self.sync_with_auth0_on_update.nil?) ? self.sync_with_auth0_on_update : true)
23
+ end # sync_with_auth0_on_update?
24
+
25
+
26
+ def save_to_auth0_on_create
27
+ return true unless sync_with_auth0_on_create?
28
+
29
+ save_to_auth0
30
+
31
+ true # don't abort the callback chain
32
+ end # save_to_auth0_on_create
33
+
34
+
35
+ def save_to_auth0_on_update
36
+ return true unless sync_with_auth0_on_update?
37
+ return true unless auth0_dirty?
38
+
39
+ save_to_auth0
40
+
41
+ true # don't abort the callback chain
42
+ end # save_to_auth0_on_update
43
+
44
+
45
+ def auth0_dirty?
46
+ is_dirty = !!(
47
+ auth0_attributes_to_sync.inject(false) do |memo, attrib|
48
+ memo || self.try("#{attrib}_changed?")
49
+ end
50
+ )
51
+
52
+ # If the password was changed, force is_dirty to be true
53
+ is_dirty = true if auth0_user_password_changed?
54
+
55
+ # If the email was changed, force is_dirty to be true
56
+ is_dirty = true if auth0_user_email_changed?
57
+
58
+ return is_dirty
59
+ end # auth0_dirty?
60
+
61
+
62
+ def save_to_auth0
63
+ # Determine if the user needs to be created or updated
64
+ user_uid = auth0_user_uid
65
+
66
+ if user_uid.nil? or user_uid.empty?
67
+ found_user = users_in_auth0_with_matching_email.first
68
+
69
+ user_uid = found_user['user_id'] if found_user
70
+ end
71
+
72
+ if user_uid.nil? or user_uid.empty?
73
+ # The user has no auth0 uid assigned and we can't find a user
74
+ # with a matching email address, so create.
75
+ create_in_auth0
76
+ else
77
+ # The user already has an auth0 UID assigned or we have a user
78
+ # with a matching email address, so update.
79
+ update_in_auth0(user_uid)
80
+ end
81
+ end # save_to_auth0
82
+
83
+
84
+ def create_in_auth0
85
+ params = auth0_create_params
86
+
87
+ response = SyncAttrWithAuth0::Auth0.create_user(auth0_user_name, params, auth0_sync_configuration)
88
+
89
+ # Update the record with the uid after_commit
90
+ @auth0_uid = response['user_id']
91
+ end # create_in_auth0
92
+
93
+
94
+ def update_in_auth0(user_uid)
95
+ return unless user_uid
96
+
97
+ params = auth0_update_params
98
+
99
+ begin
100
+ SyncAttrWithAuth0::Auth0.patch_user(user_uid, params, auth0_sync_configuration)
101
+
102
+ # Update the record with the uid after_commit (in case it doesn't match what's on file).
103
+ @auth0_uid = user_uid
104
+ rescue ::Auth0::NotFound => e
105
+ # For whatever reason, the passed in uid was invalid,
106
+ # determine how to proceed.
107
+ found_user = users_in_auth0_with_matching_email.first
108
+
109
+ if found_user.nil?
110
+ # We could not find a user with that email address, so create
111
+ # instead.
112
+ create_in_auth0
113
+ else
114
+ # The uid was incorrect, so re-attempt with the new uid
115
+ # and update the one on file.
116
+ SyncAttrWithAuth0::Auth0.patch_user(found_user['user_id'], params, auth0_sync_configuration)
117
+
118
+ # Update the record with the uid after_commit
119
+ @auth0_uid = found_user['user_id']
120
+ end
121
+
122
+ rescue Exception => e
123
+ ::Rails.logger.error e.message
124
+ ::Rails.logger.error e.backtrace.join("\n")
125
+
126
+ raise e
127
+ end
128
+ end # update_in_auth0
129
+
130
+
131
+ def auth0_create_params
132
+ user_metadata = auth0_user_metadata
133
+
134
+ password = auth0_user_password
135
+
136
+ if password.nil? or password.empty?
137
+ # We MUST include a password on create.
138
+ password = auth0_default_password
139
+ end
140
+
141
+ email_verified = auth0_email_verified?
142
+
143
+ params = {
144
+ 'email' => auth0_user_email,
145
+ 'password' => password,
146
+ 'connection' => auth0_sync_configuration.connection_name,
147
+ 'email_verified' => email_verified,
148
+ 'user_metadata' => user_metadata
149
+ }
150
+
151
+ return params
152
+ end # auth0_create_params
153
+
154
+
155
+ def auth0_update_params
156
+ user_metadata = auth0_user_metadata
157
+ app_metadata = auth0_app_metadata
158
+
159
+ params = {
160
+ 'app_metadata' => app_metadata,
161
+ 'user_metadata' => user_metadata
162
+ }
163
+
164
+ if auth0_user_password_changed?
165
+ # The password needs to be updated.
166
+ params['password'] = auth0_user_password
167
+ params['verify_password'] = auth0_verify_password?
168
+ end
169
+
170
+ if auth0_user_email_changed?
171
+ # The email needs to be updated.
172
+ params['email'] = auth0_user_email
173
+ params['verify_email'] = auth0_email_verified?
174
+ end
175
+
176
+ return params
177
+ end # auth0_update_params
178
+
179
+
180
+ def update_uid_from_auth0
181
+ if @auth0_uid
182
+ self.sync_with_auth0_on_update = false if self.respond_to?(:sync_with_auth0_on_update=)
183
+ self.send("#{auth0_sync_configuration.auth0_uid_attribute}=", @auth0_uid)
184
+
185
+ # Nil the instance variable to prevent an infinite loop
186
+ @auth0_uid = nil
187
+
188
+ # Save!
189
+ self.save
190
+ end
191
+
192
+ true # don't abort the callback chain
193
+ end # update_uid_from_auth0
194
+
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,31 @@
1
+ module SyncAttrWithAuth0
2
+ module Adapters
3
+ module ActiveRecord
4
+ module Validation
5
+
6
+ def validate_with_auth0?
7
+ !!((self.respond_to?(:validate_with_auth0) and !self.validate_with_auth0.nil?) ? self.validate_with_auth0 : true)
8
+ end # validate_with_auth0?
9
+
10
+
11
+ def validate_email_with_auth0?
12
+ email_changed_method_name = "#{auth0_sync_configuration.email_attribute.to_s}_changed?"
13
+
14
+ !!(validate_with_auth0? and self.send(email_changed_method_name))
15
+ end # validate_email_with_auth0?
16
+
17
+
18
+ def validate_email_with_auth0
19
+ return true unless validate_email_with_auth0?
20
+
21
+ return users_in_auth0_with_matching_email.empty?
22
+ end # validate_email_with_auth0
23
+
24
+ def users_in_auth0_with_matching_email
25
+ return SyncAttrWithAuth0::Auth0.find_users_by_email(auth0_user_email, auth0_sync_configuration)
26
+ end # users_in_auth0_with_matching_email
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,161 @@
1
+ require 'sync_attr_with_auth0/adapters/active_record/validation'
2
+ require 'sync_attr_with_auth0/adapters/active_record/sync'
3
+
4
+ module SyncAttrWithAuth0
5
+ module Adapters
6
+ module ActiveRecord
7
+ extend ::ActiveSupport::Concern
8
+
9
+ include SyncAttrWithAuth0::Adapters::ActiveRecord::Validation
10
+ include SyncAttrWithAuth0::Adapters::ActiveRecord::Sync
11
+
12
+ require "uuidtools"
13
+
14
+ module ClassMethods
15
+
16
+ def sync_attr_with_auth0(*fields)
17
+ options = fields.extract_options!
18
+
19
+ # Setup methods for accessing fields and options
20
+ define_method 'auth0_attributes_to_sync' do
21
+ fields
22
+ end
23
+
24
+ define_method 'setup_auth0_sync_configuration' do
25
+ config = SyncAttrWithAuth0.configuration.dup
26
+
27
+ options.each do |key, value|
28
+ config.send(:"#{key}=", value)
29
+ end
30
+
31
+ config
32
+ end
33
+
34
+ # Setup callbacks
35
+ after_validation :validate_email_with_auth0
36
+ after_create :save_to_auth0_on_create
37
+ after_update :save_to_auth0_on_update
38
+ after_commit :update_uid_from_auth0
39
+ end # sync_attr_with_auth0
40
+
41
+ end # ClassMethods
42
+
43
+
44
+ def auth0_sync_configuration
45
+ @auth0_sync_configuration ||= setup_auth0_sync_configuration
46
+ end # auth0_sync_configuration
47
+
48
+ private
49
+
50
+ def auth0_user_email
51
+ self.send(auth0_sync_configuration.email_attribute) if self.respond_to?(auth0_sync_configuration.email_attribute)
52
+ end # auth0_user_email
53
+
54
+
55
+ def auth0_user_email_changed?
56
+ return false unless self.respond_to?(auth0_sync_configuration.email_attribute)
57
+ # return false unless sync_email_with_auth0? # We don't care if it changed if we aren't syncing it.
58
+
59
+ return self.send("#{auth0_sync_configuration.email_attribute}_changed?")
60
+ end # auth0_email_changed?
61
+
62
+
63
+ def auth0_user_uid
64
+ self.send(auth0_sync_configuration.auth0_uid_attribute) if self.respond_to?(auth0_sync_configuration.auth0_uid_attribute)
65
+ end # auth0_user_uid
66
+
67
+
68
+ def auth0_user_name
69
+ self.send(auth0_sync_configuration.name_attribute) if self.respond_to?(auth0_sync_configuration.name_attribute)
70
+ end # auth0_user_name
71
+
72
+
73
+ def auth0_user_given_name
74
+ self.send(auth0_sync_configuration.given_name_attribute) if self.respond_to?(auth0_sync_configuration.given_name_attribute)
75
+ end # auth0_user_name
76
+
77
+
78
+ def auth0_user_family_name
79
+ self.send(auth0_sync_configuration.family_name_attribute) if self.respond_to?(auth0_sync_configuration.family_name_attribute)
80
+ end # auth0_user_name
81
+
82
+
83
+ def auth0_user_password
84
+ self.send(auth0_sync_configuration.password_attribute) if self.respond_to?(auth0_sync_configuration.password_attribute)
85
+ end # auth0_user_password
86
+
87
+
88
+ def auth0_user_password_changed?
89
+ return false unless self.respond_to?(auth0_sync_configuration.password_attribute)
90
+ # return false unless sync_password_with_auth0? # We don't care if it changed if we aren't syncing it.
91
+
92
+ if self.respond_to?(:"#{auth0_sync_configuration.password_attribute.to_s}_changed?")
93
+ # We have a changed method, use it
94
+ return self.send(:"#{auth0_sync_configuration.password_attribute.to_s}_changed?")
95
+ else
96
+ # We don't have a changed method, check if the attribute was set.
97
+ return !self.send(auth0_sync_configuration.password_attribute).nil?
98
+ end
99
+ end # auth0_user_password_changed?
100
+
101
+
102
+ def auth0_default_password
103
+ # Need a9 or something similar to guarantee one letter and one number in the password
104
+ "#{auth0_new_uuid[0..19]}aA9"
105
+ end # auth0_default_password
106
+
107
+
108
+ def auth0_new_uuid
109
+ ::UUIDTools::UUID.random_create().to_s
110
+ end # auth0_new_uuid
111
+
112
+
113
+ def auth0_email_verified?
114
+ # Unless we're explicitly told otherwise, don't consider the email verified.
115
+ return false unless self.respond_to?(auth0_sync_configuration.email_verified_attribute)
116
+
117
+ return self.send(auth0_sync_configuration.email_verified_attribute)
118
+ end # auth0_email_verified?
119
+
120
+
121
+ def auth0_verify_password?
122
+ # Unless we're explicitly told otherwise, verify the password changes.
123
+ return true unless self.respond_to?(auth0_sync_configuration.verify_password_attribute)
124
+
125
+ self.send(auth0_sync_configuration.verify_password_attribute)
126
+ end # auth0_verify_password?
127
+
128
+
129
+ def auth0_user_metadata
130
+ user_metadata = {}
131
+
132
+ non_metadata_keys = [
133
+ auth0_sync_configuration.name_attribute,
134
+ auth0_sync_configuration.family_name_attribute,
135
+ auth0_sync_configuration.given_name_attribute,
136
+ auth0_sync_configuration.email_attribute,
137
+ auth0_sync_configuration.password_attribute,
138
+ auth0_sync_configuration.email_verified_attribute
139
+ ]
140
+
141
+ auth0_attributes_to_sync.each do |key|
142
+ user_metadata[key.to_s] = self.send(key) if self.respond_to?(key) and non_metadata_keys.index(key).nil?
143
+ end
144
+
145
+ return user_metadata
146
+ end # auth0_user_metadata
147
+
148
+
149
+ def auth0_app_metadata
150
+ return {
151
+ 'name' => auth0_user_name,
152
+ 'nickname' => auth0_user_name,
153
+ 'given_name' => auth0_user_given_name,
154
+ 'family_name' => auth0_user_family_name
155
+ }
156
+ end # auth0_app_metadata
157
+
158
+
159
+ end
160
+ end
161
+ end
@@ -1,5 +1,7 @@
1
1
  module SyncAttrWithAuth0
2
2
  module Auth0
3
+ class InvalidAuth0ConfigurationException < StandardError; end
4
+
3
5
  require "auth0"
4
6
  require "uuidtools"
5
7
 
@@ -18,26 +20,69 @@ module SyncAttrWithAuth0
18
20
  jwt = JWT.encode(payload, JWT.base64url_decode(global_client_secret))
19
21
 
20
22
  return jwt
21
- end
23
+ end # ::create_auth0_jwt
24
+
22
25
 
23
26
  def self.create_auth0_client(
24
27
  api_version: 2,
25
- global_client_id: ENV['AUTH0_GLOBAL_CLIENT_ID'],
26
- global_client_secret: ENV['AUTH0_GLOBAL_CLIENT_SECRET'],
27
- client_id: ENV['AUTH0_CLIENT_ID'],
28
- client_secret: ENV['AUTH0_CLIENT_SECRET'],
29
- namespace: ENV['AUTH0_DOMAIN']
28
+ config: SyncAttrWithAuth0.configuration
30
29
  )
30
+ validate_auth0_config_for_api(api_version, config)
31
+
31
32
  case api_version
32
33
  when 1
33
- auth0 = Auth0Client.new(client_id: client_id, client_secret: client_secret, namespace: namespace)
34
+ auth0 = Auth0Client.new(client_id: config.auth0_client_id, client_secret: config.auth0_client_secret, namespace: config.auth0_namespace)
34
35
  when 2
35
- jwt = SyncAttrWithAuth0::Auth0.create_auth0_jwt(global_client_id: global_client_id, global_client_secret: global_client_secret)
36
- auth0 = Auth0Client.new(api_version: 2, access_token: jwt, namespace: namespace)
36
+ jwt = SyncAttrWithAuth0::Auth0.create_auth0_jwt(global_client_id: config.auth0_global_client_id, global_client_secret: config.auth0_global_client_secret)
37
+ auth0 = Auth0Client.new(api_version: 2, access_token: jwt, namespace: config.auth0_namespace)
37
38
  end
38
39
 
39
40
  return auth0
40
- end
41
+ end # ::create_auth0_client
42
+
43
+
44
+ def self.validate_auth0_config_for_api(api_version, config = SyncAttrWithAuth0.configuration)
45
+ settings_to_validate = []
46
+ invalid_settings = []
47
+
48
+ case api_version
49
+ when 1
50
+ settings_to_validate = [:auth0_client_id, :auth0_client_secret, :auth0_namespace]
51
+ when 2
52
+ settings_to_validate = [:auth0_global_client_id, :auth0_global_client_secret, :auth0_namespace]
53
+ end
54
+
55
+ settings_to_validate.each do |setting_name|
56
+ unless config.send(setting_name)
57
+ invalid_settings << setting_name
58
+ end
59
+ end
60
+
61
+ if invalid_settings.length > 0
62
+ raise InvalidAuth0ConfigurationException.new("The following required auth0 settings were invalid: #{invalid_settings.join(', ')}")
63
+ end
64
+ end # ::validate_auth0_config_for_api
65
+
66
+
67
+ def self.find_users_by_email(email, config = SyncAttrWithAuth0.configuration)
68
+ auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client(config: config)
69
+
70
+ return auth0.users(q: "email:\"#{email}\"")
71
+ end # ::find_users_by_email
72
+
73
+
74
+ def self.create_user(name, params, config = SyncAttrWithAuth0.configuration)
75
+ auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client(config: config)
76
+
77
+ return auth0.create_user(name, params)
78
+ end # ::create_user
79
+
80
+
81
+ def self.patch_user(uid, params, config = SyncAttrWithAuth0.configuration)
82
+ auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client(config: config)
83
+
84
+ return auth0.patch_user(uid, params)
85
+ end # ::patch_user
41
86
 
42
87
  end
43
88
  end
@@ -0,0 +1,56 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ module SyncAttrWithAuth0
4
+
5
+ class << self
6
+ attr_accessor :configuration
7
+ end
8
+
9
+ # Start a SyncAttrWithAuth0 configuration block in an initializer
10
+ #
11
+ # example: Provide a default UID for the applicaiton
12
+ # SyncAttrWithAuth0.configure do |config|
13
+ # config.auth0_uid_attribute = :auth0_uid
14
+ # end
15
+ def self.configure
16
+ yield configuration
17
+ end
18
+
19
+ def self.configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def self.reset_configuration
24
+ @configuration = Configuration.new
25
+ end
26
+
27
+ # SyncAttrWithAuth0 configuration class.
28
+ # This is used by SyncAttrWithAuth0 to provide configuration settings.
29
+ class Configuration
30
+ attr_accessor :auth0_global_client_id, :auth0_global_client_secret,
31
+ :auth0_client_id, :auth0_client_secret, :auth0_namespace,
32
+ :auth0_uid_attribute, :name_attribute, :given_name_attribute,
33
+ :family_name_attribute, :email_attribute, :password_attribute,
34
+ :email_verified_attribute, :verify_password_attribute,
35
+ :connection_name
36
+
37
+
38
+ def initialize
39
+ @auth0_global_client_id = ENV['AUTH0_GLOBAL_CLIENT_ID']
40
+ @auth0_global_client_secret = ENV['AUTH0_GLOBAL_CLIENT_SECRET']
41
+ @auth0_client_id = ENV['AUTH0_CLIENT_ID']
42
+ @auth0_client_secret = ENV['AUTH0_CLIENT_SECRET']
43
+ @auth0_namespace = ENV['AUTH0_NAMESPACE']
44
+
45
+ @auth0_uid_attribute = :auth0_uid
46
+ @name_attribute = :name
47
+ @given_name_attribute = :given_name
48
+ @family_name_attribute = :family_name
49
+ @email_attribute = :email
50
+ @password_attribute = :password
51
+ @email_verified_attribute = :email_verified
52
+ @verify_password_attribute = :verify_password
53
+ @connection_name = 'Username-Password-Authentication'
54
+ end
55
+ end
56
+ end
@@ -1,4 +1,8 @@
1
1
  require 'sync_attr_with_auth0/auth0'
2
- require 'sync_attr_with_auth0/model'
2
+ # require 'sync_attr_with_auth0/model'
3
+ require 'sync_attr_with_auth0/configuration'
4
+ require 'sync_attr_with_auth0/adapters/active_record'
3
5
 
4
- ::ActiveRecord::Base.send :include, ::SyncAttrWithAuth0::Model
6
+ module SyncAttrWithAuth0
7
+ ::ActiveRecord::Base.send :include, ::SyncAttrWithAuth0::Adapters::ActiveRecord
8
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sync_attr_with_auth0
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.25
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick McGraw
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-05-30 00:00:00.000000000 Z
12
+ date: 2016-07-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -145,8 +145,11 @@ extensions: []
145
145
  extra_rdoc_files: []
146
146
  files:
147
147
  - lib/sync_attr_with_auth0.rb
148
+ - lib/sync_attr_with_auth0/adapters/active_record.rb
149
+ - lib/sync_attr_with_auth0/adapters/active_record/sync.rb
150
+ - lib/sync_attr_with_auth0/adapters/active_record/validation.rb
148
151
  - lib/sync_attr_with_auth0/auth0.rb
149
- - lib/sync_attr_with_auth0/model.rb
152
+ - lib/sync_attr_with_auth0/configuration.rb
150
153
  homepage: http://rubygems.org/gems/sync_attr_with_auth0
151
154
  licenses:
152
155
  - MIT
@@ -167,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
170
  version: '0'
168
171
  requirements: []
169
172
  rubyforge_project:
170
- rubygems_version: 2.4.8
173
+ rubygems_version: 2.5.1
171
174
  signing_key:
172
175
  specification_version: 4
173
176
  summary: Synchronize attributes on a local ActiveRecord user model with the user metadata
@@ -1,285 +0,0 @@
1
- module SyncAttrWithAuth0
2
- module Model
3
- extend ::ActiveSupport::Concern
4
-
5
- require "uuidtools"
6
-
7
- module ClassMethods
8
-
9
- def sync_attr_with_auth0(options = {})
10
- class_attribute :auth0_sync_options
11
-
12
- verify_environment_variables
13
- merge_default_options(options)
14
-
15
- after_validation :validate_email_with_auth0
16
- after_create :auth0_create
17
- after_update :auth0_update, if: :auth0_dirty?
18
- after_commit :auth0_set_uid
19
- end
20
-
21
- private
22
-
23
- def verify_environment_variables
24
- env_variables = %w(AUTH0_GLOBAL_CLIENT_ID AUTH0_GLOBAL_CLIENT_SECRET AUTH0_CLIENT_ID AUTH0_CLIENT_SECRET AUTH0_DOMAIN)
25
- missing_env_variables = []
26
-
27
- env_variables.each do |env_variable_name|
28
- unless ENV[env_variable_name]
29
- missing_env_variables << env_variable_name
30
- end
31
- end
32
-
33
- if missing_env_variables.size > 0
34
- raise Exception.new("Missing the following required environment variables: #{missing_env_variables.join(',')}")
35
- end
36
- end
37
-
38
- def merge_default_options(options)
39
- self.auth0_sync_options = {
40
- uid_att: :uid,
41
- name_att: :name,
42
- given_name_att: :given_name,
43
- family_name_att: :family_name,
44
- email_att: :email,
45
- password_att: :password,
46
- email_verified_att: :email_verified,
47
- verify_password_att: :verify_password,
48
- connection_name: 'Username-Password-Authentication',
49
- sync_atts: []
50
- }
51
-
52
- self.auth0_sync_options.merge!(options)
53
- end
54
-
55
- end
56
-
57
- def validate_email_with_auth0
58
- # If the email is being modified, verify the new email does not already
59
- # exist in auth0.
60
-
61
- ok_to_validate = (self.respond_to?(:validate_with_auth0) and !self.validate_with_auth0.nil? ? self.validate_with_auth0 : true)
62
-
63
- if ok_to_validate and self.email_changed?
64
- response = find_user_in_auth0
65
-
66
- return response.empty?
67
- end
68
-
69
- return true
70
- end
71
-
72
- def auth0_create
73
- # When creating a new user, create the user in auth0.
74
-
75
- ok_to_sync = (self.respond_to?(:sync_with_auth0_on_create) and !self.sync_with_auth0_on_create.nil? ? self.sync_with_auth0_on_create : true)
76
-
77
- # Do not create a user in auth0 if the user already has a uid from auth0
78
- if ok_to_sync
79
- unless self.send(auth0_sync_options[:uid_att]).nil? or self.send(auth0_sync_options[:uid_att]).empty?
80
- ok_to_sync = false
81
- end
82
- end
83
-
84
- if ok_to_sync
85
- create_user_in_auth0
86
- end
87
-
88
- true # don't abort the callback chain
89
- end
90
-
91
- def auth0_update
92
- ok_to_sync = (self.respond_to?(:sync_with_auth0_on_update) and !self.sync_with_auth0_on_update.nil? ? self.sync_with_auth0_on_update : true)
93
-
94
- if ok_to_sync
95
-
96
- # Get the auth0 uid
97
- uid = self.send(auth0_sync_options[:uid_att])
98
-
99
- # TODO: create a user if the uid is nil
100
- unless uid.nil?
101
- # Update the user in auth0
102
- update_user_in_auth0(uid)
103
- end
104
-
105
- end
106
-
107
- true # don't abort the callback chain
108
- end
109
-
110
- def auth0_set_uid
111
- if @auth0_uid
112
- self.sync_with_auth0_on_update = false if self.respond_to?(:sync_with_auth0_on_update=)
113
- self.send("#{auth0_sync_options[:uid_att]}=", @auth0_uid)
114
-
115
- # Nil the instance variable to prevent an infinite loop
116
- @auth0_uid = nil
117
-
118
- # Save!
119
- self.save
120
- end
121
-
122
- true # don't abort the callback chain
123
- end
124
-
125
- def create_user_in_auth0()
126
- user_metadata = auth0_user_metadata
127
-
128
- password = auth0_user_password
129
-
130
- if password.nil?
131
- password = auth0_default_password
132
- end
133
-
134
- email_verified = auth0_email_verified?
135
- args = {
136
- 'email' => self.send(auth0_sync_options[:email_att]),
137
- 'password' => password,
138
- 'connection' => auth0_sync_options[:connection_name],
139
- 'email_verified' => email_verified,
140
- 'user_metadata' => user_metadata
141
- }
142
-
143
- auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client
144
-
145
- response = auth0.create_user(self.send(auth0_sync_options[:name_att]), args)
146
-
147
- # Update the record with the uid after_commit
148
- @auth0_uid = response['user_id']
149
- end
150
-
151
- def update_user_in_auth0(uid)
152
- user_metadata = auth0_user_metadata
153
-
154
- auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client
155
-
156
- args = {
157
- 'app_metadata' => {
158
- 'name' => self.send(auth0_sync_options[:name_att]),
159
- 'nickname' => self.send(auth0_sync_options[:name_att]),
160
- 'given_name' => self.send(auth0_sync_options[:given_name_att]),
161
- 'family_name' => self.send(auth0_sync_options[:family_name_att])
162
- }
163
- }
164
-
165
- if (
166
- auth0_sync_options[:sync_atts].index(auth0_sync_options[:password_att]) and
167
- auth0_user_password_changed?
168
- )
169
- # The password should be sync'd and was changed
170
- args['password'] = self.send(auth0_sync_options[:password_att])
171
- args['verify_password'] = auth0_verify_password?
172
- end
173
-
174
- if (
175
- auth0_sync_options[:sync_atts].index(auth0_sync_options[:email_att]) and
176
- auth0_email_changed?
177
- )
178
- # The email should be sync'd and was changed
179
- args['email'] = self.send(auth0_sync_options[:email_att])
180
- args['verify_email'] = auth0_email_verified?
181
- end
182
-
183
- args['user_metadata'] = user_metadata
184
-
185
- begin
186
- auth0.patch_user(uid, args)
187
-
188
- rescue ::Auth0::NotFound => e
189
- response = find_user_in_auth0
190
- found_user = response.first
191
-
192
- if found_user.nil?
193
- # Could not find the user, create it in auth0
194
- create_user_in_auth0
195
- else
196
- # Update with the new uid and correct the one on file
197
- auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client
198
- auth0.patch_user(found_user['user_id'], args)
199
-
200
- # Update the record with the uid after_commit
201
- @auth0_uid = found_user['user_id']
202
- end
203
-
204
- rescue Exception => e
205
- ::Rails.logger.error e.message
206
- ::Rails.logger.error e.backtrace.join("\n")
207
-
208
- raise e
209
- end
210
- end
211
-
212
- def find_user_in_auth0
213
- auth0 = SyncAttrWithAuth0::Auth0.create_auth0_client(api_version: 1)
214
-
215
- response = auth0.users("email:#{self.send(auth0_sync_options[:email_att])}")
216
-
217
- return response
218
- end
219
-
220
- def auth0_user_password
221
- self.respond_to?(auth0_sync_options[:password_att]) ? self.send(auth0_sync_options[:password_att]) : auth0_default_password
222
- end
223
-
224
- def auth0_user_password_changed?
225
- if self.respond_to?(auth0_sync_options[:password_att])
226
- if self.respond_to?(:"#{auth0_sync_options[:password_att].to_s}_changed?")
227
- # We have a changed method, use it
228
- return self.send(:"#{auth0_sync_options[:password_att].to_s}_changed?")
229
- else
230
- # We don't have a changed method, check if the attribute was set.
231
- return !self.send(auth0_sync_options[:password_att]).nil?
232
- end
233
- end
234
-
235
- return false
236
- end
237
-
238
- def auth0_email_verified?
239
- !!(self.respond_to?(auth0_sync_options[:email_verified_att]) ? self.send(auth0_sync_options[:email_verified_att]) : false)
240
- end
241
-
242
- def auth0_email_changed?
243
- !!(self.respond_to?(auth0_sync_options[:email_att]) ? self.send("#{auth0_sync_options[:email_att]}_changed?") : false)
244
- end
245
-
246
- def auth0_verify_password?
247
- !!(self.respond_to?(auth0_sync_options[:verify_password_att]) ? self.send(auth0_sync_options[:verify_password_att]) : true)
248
- end
249
-
250
- def auth0_default_password
251
- # Need a9 or something similar to guarantee one letter and one number in the password
252
- "#{auth0_new_uuid[0..19]}aA9"
253
- end
254
-
255
- def auth0_new_uuid
256
- ::UUIDTools::UUID.random_create().to_s
257
- end
258
-
259
- def auth0_user_metadata
260
- user_metadata = {}
261
- app_metadata_keys = [auth0_sync_options[:family_name_att],
262
- auth0_sync_options[:given_name_att], auth0_sync_options[:email_att],
263
- auth0_sync_options[:password_att],
264
- auth0_sync_options[:email_verified_att], auth0_sync_options[:name_att]]
265
-
266
- auth0_sync_options[:sync_atts].each do |key|
267
- user_metadata[key] = self.send(key) if self.respond_to?(key) and app_metadata_keys.index(key).nil?
268
- end
269
-
270
- return user_metadata
271
- end
272
-
273
- def auth0_dirty?
274
- is_dirty = !!(auth0_sync_options[:sync_atts].inject(false) do |memo, attrib|
275
- memo || try("#{attrib}_changed?")
276
- end)
277
-
278
- # If the password was changed, force is_dirty to be true
279
- is_dirty = true if auth0_user_password_changed?
280
-
281
- return is_dirty
282
- end
283
-
284
- end
285
- end