sync_attr_with_auth0 0.0.25 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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