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 +4 -4
- data/lib/sync_attr_with_auth0/adapters/active_record/sync.rb +198 -0
- data/lib/sync_attr_with_auth0/adapters/active_record/validation.rb +31 -0
- data/lib/sync_attr_with_auth0/adapters/active_record.rb +161 -0
- data/lib/sync_attr_with_auth0/auth0.rb +55 -10
- data/lib/sync_attr_with_auth0/configuration.rb +56 -0
- data/lib/sync_attr_with_auth0.rb +6 -2
- metadata +7 -4
- data/lib/sync_attr_with_auth0/model.rb +0 -285
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 472500ed61386a758384a9f866359b1a6e600eb2
|
4
|
+
data.tar.gz: 6b8c4af3b271483ac0166386e024083d0627c6dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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:
|
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:
|
36
|
-
auth0 = Auth0Client.new(api_version: 2, access_token: jwt, 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
|
data/lib/sync_attr_with_auth0.rb
CHANGED
@@ -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
|
-
|
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
|
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-
|
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/
|
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.
|
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
|