omniauth_syncer 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 +7 -0
- data/.rspec_status +8 -0
- data/.rubocop.yml +48 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +100 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/app/controllers/users/omniauth_callbacks_controller.rb +13 -0
- data/config/initializers/omniauth_syncer.rb +16 -0
- data/lib/omniauth_syncer/configuration.rb +23 -0
- data/lib/omniauth_syncer/controller_helpers.rb +17 -0
- data/lib/omniauth_syncer/engine.rb +25 -0
- data/lib/omniauth_syncer/sync_service.rb +57 -0
- data/lib/omniauth_syncer/version.rb +3 -0
- data/lib/omniauth_syncer.rb +9 -0
- metadata +143 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6a819b9cbe1e4eac56eeb9504aaa0b61efd924995f9a98036a346de8a4032fe7
|
|
4
|
+
data.tar.gz: df5d6f52e868a53c096de1f0dae3e57f777b41ccf6fdd25a7e9967802feccafa
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 885231fb38457b3b9284f80081c60b87ccfb9a1672164df8e5efd657a7165087b4bfef266efdf4a1206fa9ed83e0406a5cc2d1099e0a952588eddc85e01333f2
|
|
7
|
+
data.tar.gz: f7aecc03a67a0f66aa77d854708e57a34955feabc32f7225dfe8496c046c31e67e13b9ff36bcc806d4453a04ec5adab2d66add266403325cf9464df725825d03
|
data/.rspec_status
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
example_id | status | run_time |
|
|
2
|
+
---------------------------------------------------- | ------ | --------------- |
|
|
3
|
+
./spec/omniauth_syncer/sync_service_spec.rb[1:1:1:1] | passed | 0.00427 seconds |
|
|
4
|
+
./spec/omniauth_syncer/sync_service_spec.rb[1:1:2:1] | passed | 0.00022 seconds |
|
|
5
|
+
./spec/omniauth_syncer/sync_service_spec.rb[1:1:3:1] | passed | 0.00079 seconds |
|
|
6
|
+
./spec/omniauth_syncer/sync_service_spec.rb[1:1:3:2] | passed | 0.00019 seconds |
|
|
7
|
+
./spec/omniauth_syncer/sync_service_spec.rb[1:1:4:1] | passed | 0.00072 seconds |
|
|
8
|
+
./spec/omniauth_syncer/sync_service_spec.rb[1:1:5:1] | passed | 0.00029 seconds |
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require:
|
|
2
|
+
- rubocop-performance
|
|
3
|
+
- rubocop-rails # Even if this is a non-Rails gem, many conventions apply
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
# Exclude common files that aren't source code
|
|
7
|
+
Exclude:
|
|
8
|
+
- 'bin/*'
|
|
9
|
+
- 'vendor/**/*'
|
|
10
|
+
- 'spec/fixtures/**/*'
|
|
11
|
+
- 'Gemfile'
|
|
12
|
+
- 'Rakefile'
|
|
13
|
+
- '**/*.gemspec'
|
|
14
|
+
# Target the Ruby version you are developing against (e.g., 3.2 is modern)
|
|
15
|
+
TargetRubyVersion: 3.2
|
|
16
|
+
NewCops: enable
|
|
17
|
+
|
|
18
|
+
# === Layout Cops (Formatting) ===
|
|
19
|
+
Layout/LineLength:
|
|
20
|
+
# Relax line length for documentation/complex setup in specs
|
|
21
|
+
Max: 120
|
|
22
|
+
IgnoredPatterns: ['\.gemspec']
|
|
23
|
+
|
|
24
|
+
# === Style Cops (Idiomatic Ruby) ===
|
|
25
|
+
Style/Documentation:
|
|
26
|
+
# Gems often skip documenting simple classes/modules
|
|
27
|
+
Enabled: false
|
|
28
|
+
Style/ClassAndModuleChildren:
|
|
29
|
+
# Use the standard, compact (nested) style:
|
|
30
|
+
# e.g., 'module A; class B; end; end' instead of 'class A::B'
|
|
31
|
+
EnforcedStyle: nested
|
|
32
|
+
Style/FrozenStringLiteralComment:
|
|
33
|
+
# Enable the magic comment for performance in modern Ruby
|
|
34
|
+
Enabled: true
|
|
35
|
+
Style/ClassMethods:
|
|
36
|
+
# Use self.method_name for class methods
|
|
37
|
+
EnforcedStyle: self_name
|
|
38
|
+
|
|
39
|
+
# === Performance Cops ===
|
|
40
|
+
# Performance cops are helpful for writing efficient code
|
|
41
|
+
Performance/FlatMap:
|
|
42
|
+
Enabled: true
|
|
43
|
+
|
|
44
|
+
# === Naming Cops ===
|
|
45
|
+
Naming/FileName:
|
|
46
|
+
# Allow the standard gem entry point file to have a hyphen
|
|
47
|
+
Exclude:
|
|
48
|
+
- 'exe/omniauth-syncer'
|
data/Gemfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
source "https://rubygems.org"
|
|
2
|
+
gemspec
|
|
3
|
+
# Add development and testing tools
|
|
4
|
+
group :development, :test do
|
|
5
|
+
# ... (other tools like rspec, pry)
|
|
6
|
+
|
|
7
|
+
# Core RuboCop gem
|
|
8
|
+
gem 'rubocop', '~> 1.0'
|
|
9
|
+
|
|
10
|
+
# Extensions for common Ruby idioms and performance checks
|
|
11
|
+
gem 'rubocop-performance'
|
|
12
|
+
gem 'rubocop-rails' # Good to include even for a general gem
|
|
13
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
omniauth_syncer (0.1.0)
|
|
5
|
+
activesupport (>= 6.0)
|
|
6
|
+
omniauth (~> 2.0)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
activesupport (6.1.7.10)
|
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
13
|
+
i18n (>= 1.6, < 2)
|
|
14
|
+
minitest (>= 5.1)
|
|
15
|
+
tzinfo (~> 2.0)
|
|
16
|
+
zeitwerk (~> 2.3)
|
|
17
|
+
ast (2.4.3)
|
|
18
|
+
coderay (1.1.3)
|
|
19
|
+
concurrent-ruby (1.3.5)
|
|
20
|
+
diff-lcs (1.6.2)
|
|
21
|
+
hashie (5.0.0)
|
|
22
|
+
i18n (1.14.7)
|
|
23
|
+
concurrent-ruby (~> 1.0)
|
|
24
|
+
json (2.7.6)
|
|
25
|
+
logger (1.7.0)
|
|
26
|
+
method_source (1.1.0)
|
|
27
|
+
minitest (5.25.4)
|
|
28
|
+
omniauth (2.1.4)
|
|
29
|
+
hashie (>= 3.4.6)
|
|
30
|
+
logger
|
|
31
|
+
rack (>= 2.2.3)
|
|
32
|
+
rack-protection
|
|
33
|
+
parallel (1.24.0)
|
|
34
|
+
parser (3.3.9.0)
|
|
35
|
+
ast (~> 2.4.1)
|
|
36
|
+
racc
|
|
37
|
+
pry (0.15.2)
|
|
38
|
+
coderay (~> 1.1)
|
|
39
|
+
method_source (~> 1.0)
|
|
40
|
+
racc (1.8.1)
|
|
41
|
+
rack (3.2.3)
|
|
42
|
+
rack-protection (3.0.6)
|
|
43
|
+
rack
|
|
44
|
+
rainbow (3.1.1)
|
|
45
|
+
rake (13.3.0)
|
|
46
|
+
regexp_parser (2.11.3)
|
|
47
|
+
rexml (3.4.4)
|
|
48
|
+
rspec (3.13.2)
|
|
49
|
+
rspec-core (~> 3.13.0)
|
|
50
|
+
rspec-expectations (~> 3.13.0)
|
|
51
|
+
rspec-mocks (~> 3.13.0)
|
|
52
|
+
rspec-core (3.13.6)
|
|
53
|
+
rspec-support (~> 3.13.0)
|
|
54
|
+
rspec-expectations (3.13.5)
|
|
55
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
56
|
+
rspec-support (~> 3.13.0)
|
|
57
|
+
rspec-mocks (3.13.6)
|
|
58
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
59
|
+
rspec-support (~> 3.13.0)
|
|
60
|
+
rspec-support (3.13.6)
|
|
61
|
+
rubocop (1.50.2)
|
|
62
|
+
json (~> 2.3)
|
|
63
|
+
parallel (~> 1.10)
|
|
64
|
+
parser (>= 3.2.0.0)
|
|
65
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
66
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
67
|
+
rexml (>= 3.2.5, < 4.0)
|
|
68
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
|
69
|
+
ruby-progressbar (~> 1.7)
|
|
70
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
71
|
+
rubocop-ast (1.30.0)
|
|
72
|
+
parser (>= 3.2.1.0)
|
|
73
|
+
rubocop-performance (1.17.1)
|
|
74
|
+
rubocop (>= 1.7.0, < 2.0)
|
|
75
|
+
rubocop-ast (>= 0.4.0)
|
|
76
|
+
rubocop-rails (2.19.1)
|
|
77
|
+
activesupport (>= 4.2.0)
|
|
78
|
+
rack (>= 1.1)
|
|
79
|
+
rubocop (>= 1.33.0, < 2.0)
|
|
80
|
+
ruby-progressbar (1.13.0)
|
|
81
|
+
tzinfo (2.0.6)
|
|
82
|
+
concurrent-ruby (~> 1.0)
|
|
83
|
+
unicode-display_width (2.6.0)
|
|
84
|
+
zeitwerk (2.6.18)
|
|
85
|
+
|
|
86
|
+
PLATFORMS
|
|
87
|
+
x86_64-linux
|
|
88
|
+
|
|
89
|
+
DEPENDENCIES
|
|
90
|
+
bundler (~> 2.0)
|
|
91
|
+
omniauth_syncer!
|
|
92
|
+
pry
|
|
93
|
+
rake (~> 13.0)
|
|
94
|
+
rspec (~> 3.0)
|
|
95
|
+
rubocop (~> 1.0)
|
|
96
|
+
rubocop-performance
|
|
97
|
+
rubocop-rails
|
|
98
|
+
|
|
99
|
+
BUNDLED WITH
|
|
100
|
+
2.4.22
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Daniele Frisanco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
omniauth\_syncer 🚀
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
A plug-and-play Ruby gem designed for applications using **OmniAuth** (often with SSO providers) to reliably and safely synchronize user profile data into a local $\\text{ActiveRecord}$ model.
|
|
5
|
+
|
|
6
|
+
This gem prevents data drift by ensuring the local user record is always up-to-date with the latest information fetched from the SSO provider during a successful login.
|
|
7
|
+
|
|
8
|
+
Installation
|
|
9
|
+
------------
|
|
10
|
+
|
|
11
|
+
Add this line to your application's Gemfile:
|
|
12
|
+
|
|
13
|
+
Ruby
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'omniauth_syncer'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then execute:
|
|
20
|
+
|
|
21
|
+
Bash
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bundle install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Configuration
|
|
28
|
+
-------------
|
|
29
|
+
|
|
30
|
+
The gem requires a basic setup to know which local model to use and how to map fields from the OmniAuth hash.
|
|
31
|
+
|
|
32
|
+
Create an initializer file at config/initializers/omniauth\_syncer.rb:
|
|
33
|
+
|
|
34
|
+
Ruby
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# config/initializers/omniauth_syncer.rb
|
|
38
|
+
OmniauthSyncer.configure do |config|
|
|
39
|
+
# 1. Local User Model
|
|
40
|
+
# The name of the local ActiveRecord class to sync data to (e.g., 'User')
|
|
41
|
+
config.user_model = 'User'
|
|
42
|
+
# 2. Unique Identifier Field
|
|
43
|
+
# The column in your local User model used for lookup (must be unique).
|
|
44
|
+
config.uid_field = :sso_uid
|
|
45
|
+
# 3. Path to UID in Auth Hash
|
|
46
|
+
# The string path in the OmniAuth Auth Hash that holds the unique identifier.
|
|
47
|
+
# Example: 'uid' is used for the top-level hash['uid'].
|
|
48
|
+
config.uid_field_in_auth = 'uid'
|
|
49
|
+
# 4. Attribute Mappings
|
|
50
|
+
# Map local attributes (keys) to the path in the OmniAuth Auth Hash (values).
|
|
51
|
+
# Use dot notation for nested fields (e.g., 'info.email').
|
|
52
|
+
config.mappings = {
|
|
53
|
+
email: 'info.email',
|
|
54
|
+
full_name: 'info.name',
|
|
55
|
+
# Example for nested data like roles provided by the SSO server:
|
|
56
|
+
roles: 'extra.raw_info.roles'
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Usage
|
|
62
|
+
-----
|
|
63
|
+
|
|
64
|
+
The gem provides the core synchronization logic via a helper module, which should be included in your application's **OmniAuth Callback Controller**.
|
|
65
|
+
|
|
66
|
+
### Step 1: Include the Helper
|
|
67
|
+
|
|
68
|
+
In your OmniAuth Callback Controller (e.g., app/controllers/users/omniauth\_callbacks\_controller.rb):
|
|
69
|
+
|
|
70
|
+
Ruby
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|
74
|
+
# Include the helper module
|
|
75
|
+
include OmniauthSyncer::ControllerHelpers
|
|
76
|
+
# ...
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Step 2: Sync the User
|
|
81
|
+
|
|
82
|
+
Call the **sync\_sso\_user** method inside your provider action (e.g., sso\_provider). This method will handle the following automatically:
|
|
83
|
+
|
|
84
|
+
1. Retrieving the current $\\text{OmniAuth Auth Hash}$.
|
|
85
|
+
|
|
86
|
+
2. Calling the $\\text{SyncService}$ to $\\text{find or create}$ the local user record based on the configured $\\text{uid}$.
|
|
87
|
+
|
|
88
|
+
3. Updating all configured attributes.
|
|
89
|
+
|
|
90
|
+
4. Safely **preserving** existing local data if a field is missing or nil in the incoming SSO data.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
Ruby
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
# app/controllers/users/omniauth_callbacks_controller.rb
|
|
97
|
+
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|
98
|
+
include OmniauthSyncer::ControllerHelpers
|
|
99
|
+
def sso_provider
|
|
100
|
+
begin
|
|
101
|
+
# 1. Sync the user and get the local user object
|
|
102
|
+
@user = sync_sso_user
|
|
103
|
+
# 2. Sign in the user (e.g., using Devise or a custom method)
|
|
104
|
+
if @user.persisted?
|
|
105
|
+
sign_in_and_redirect @user, event: :authentication
|
|
106
|
+
else
|
|
107
|
+
# This occurs if the user record fails local validations (e.g., missing mandatory field)
|
|
108
|
+
redirect_to root_path, alert: "Could not sync user profile due to data issues."
|
|
109
|
+
end
|
|
110
|
+
rescue => e
|
|
111
|
+
# Log any synchronization or validation errors
|
|
112
|
+
logger.error "SSO Sync Failure: #{e.message}"
|
|
113
|
+
redirect_to root_path, alert: "Authentication failed due to internal error."
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Contributing
|
|
120
|
+
------------
|
|
121
|
+
|
|
122
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/danielefrisanco/omniauth_syncer.
|
|
123
|
+
|
|
124
|
+
License
|
|
125
|
+
-------
|
|
126
|
+
|
|
127
|
+
The gem is available as open source under the terms of the **MIT License**.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# The final controller setup in the user's application
|
|
2
|
+
class Users::OmniauthCallbacksController < ApplicationController
|
|
3
|
+
include OmniauthSyncer::ControllerHelpers # Includes the sync_sso_user method
|
|
4
|
+
|
|
5
|
+
def sso_provider
|
|
6
|
+
# Use the helper to sync the user and retrieve the record
|
|
7
|
+
@user = sync_sso_user
|
|
8
|
+
|
|
9
|
+
sign_in_and_redirect @user, event: :authentication
|
|
10
|
+
rescue StandardError
|
|
11
|
+
redirect_to root_path, alert: 'Authentication failed.'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
OmniauthSyncer.configure do |config|
|
|
2
|
+
# The ActiveRecord model class name
|
|
3
|
+
config.user_model = 'User'
|
|
4
|
+
|
|
5
|
+
# The column used for the unique identifier
|
|
6
|
+
config.uid_field = :sso_id
|
|
7
|
+
|
|
8
|
+
# Mapping: local_attribute => path_in_auth_hash
|
|
9
|
+
# 'info.email' looks into auth_hash.info['email']
|
|
10
|
+
config.mappings = {
|
|
11
|
+
email: 'info.email',
|
|
12
|
+
full_name: 'info.name',
|
|
13
|
+
# Example for nested/extra data: auth_hash.extra['raw_info']['roles']
|
|
14
|
+
roles: 'extra.raw_info.roles'
|
|
15
|
+
}
|
|
16
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module OmniauthSyncer
|
|
2
|
+
class Configuration
|
|
3
|
+
# Define settings with default values or required accessors
|
|
4
|
+
attr_accessor :user_model, :uid_field, :uid_field_in_auth, :mappings
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
# Sensible defaults
|
|
8
|
+
@user_model = 'User'
|
|
9
|
+
@uid_field = :uid
|
|
10
|
+
@uid_field_in_auth = 'uid' # Default path in AuthHash
|
|
11
|
+
@mappings = {}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Class method to expose the configuration object and the configuration block
|
|
16
|
+
def self.configuration
|
|
17
|
+
@configuration ||= Configuration.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.configure
|
|
21
|
+
yield(configuration)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module OmniauthSyncer
|
|
2
|
+
module ControllerHelpers
|
|
3
|
+
# Call this method inside the OmniAuth callback action
|
|
4
|
+
def sync_sso_user
|
|
5
|
+
# Retrieve the auth hash from the Rack environment
|
|
6
|
+
auth_hash = request.env['omniauth.auth']
|
|
7
|
+
|
|
8
|
+
# Execute the core sync logic
|
|
9
|
+
OmniauthSyncer::SyncService.call(auth_hash)
|
|
10
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
11
|
+
# Provide a clear, common way to handle sync validation errors
|
|
12
|
+
logger.error "OmniauthSyncer Validation Error: #{e.message}"
|
|
13
|
+
# Optionally, you might raise a custom error here
|
|
14
|
+
raise e
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'rails/engine'
|
|
2
|
+
|
|
3
|
+
module OmniauthSyncer
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
# Allows the host application to use OmniauthSyncer configurations
|
|
6
|
+
isolate_namespace OmniauthSyncer
|
|
7
|
+
|
|
8
|
+
# This initializer runs before all other application initializers.
|
|
9
|
+
# It ensures that our configuration block is loaded early.
|
|
10
|
+
initializer 'omniauth_syncer.configure_defaults' do
|
|
11
|
+
# Load your configuration settings here if needed,
|
|
12
|
+
# but typically, we rely on the host app's initializer.
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Optionally, you can include logic to automatically integrate
|
|
16
|
+
# the SyncService into the host app's controller.
|
|
17
|
+
initializer 'omniauth_syncer.controller_mixin' do
|
|
18
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
19
|
+
# This is where you would define a helper method
|
|
20
|
+
# that the user can call in their OmniAuth controller:
|
|
21
|
+
# include OmniauthSyncer::ControllerHelpers
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module OmniauthSyncer
|
|
2
|
+
class SyncService
|
|
3
|
+
# Use the .call convention for single-purpose service objects
|
|
4
|
+
def self.call(auth_hash)
|
|
5
|
+
new(auth_hash).sync_user
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def sync_user
|
|
9
|
+
# Step 1: Find or Create the User
|
|
10
|
+
user = find_or_initialize_user
|
|
11
|
+
|
|
12
|
+
# Step 2: Update Mapped Attributes
|
|
13
|
+
@config.mappings.each do |local_attr, auth_path|
|
|
14
|
+
# Use a helper to safely retrieve nested data (e.g., 'info.email')
|
|
15
|
+
auth_value = get_auth_value(auth_path)
|
|
16
|
+
|
|
17
|
+
# Only set if the value is present and the local attribute is writable
|
|
18
|
+
user.send("#{local_attr}=", auth_value) if auth_value.present?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Step 3: Save and Return
|
|
22
|
+
user.save! # Use save! to ensure we raise an error on validation failure
|
|
23
|
+
user
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def initialize(auth_hash)
|
|
29
|
+
@auth_hash = auth_hash
|
|
30
|
+
@config = OmniauthSyncer.configuration
|
|
31
|
+
# Dynamically get the User model class
|
|
32
|
+
@user_class = @config.user_model.constantize
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def find_or_initialize_user
|
|
36
|
+
# Get the unique identifier value from the auth hash (e.g., the UID)
|
|
37
|
+
uid_value = get_auth_value(@config.uid_field_in_auth) # Assume config holds the path to UID
|
|
38
|
+
|
|
39
|
+
# Use the configured uid_field for the database lookup
|
|
40
|
+
@user_class.find_or_initialize_by(@config.uid_field => uid_value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Helper method to safely traverse the nested AuthHash
|
|
44
|
+
# (Simplified for demonstration; a real implementation would be more robust)
|
|
45
|
+
def get_auth_value(path)
|
|
46
|
+
parts = path.to_s.split('.')
|
|
47
|
+
current = @auth_hash.dup
|
|
48
|
+
|
|
49
|
+
parts.each do |part|
|
|
50
|
+
# Use dig for safety, converting to symbol or string keys
|
|
51
|
+
current = current.respond_to?(:dig) ? current.dig(part.to_sym) || current.dig(part.to_s) : current[part]
|
|
52
|
+
return nil unless current
|
|
53
|
+
end
|
|
54
|
+
current
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require 'active_support/core_ext/string/inflections' # For constantize
|
|
2
|
+
require 'omniauth_syncer/version'
|
|
3
|
+
require 'omniauth_syncer/configuration'
|
|
4
|
+
require 'omniauth_syncer/sync_service'
|
|
5
|
+
|
|
6
|
+
module OmniauthSyncer
|
|
7
|
+
# No code here, just module definition.
|
|
8
|
+
# All logic is in SyncService and Configuration.
|
|
9
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: omniauth_syncer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Daniele Frisanco
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-10-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: omniauth
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '6.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '6.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '13.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '13.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: pry
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
description: Ensures local user records are created or updated with the latest data
|
|
98
|
+
from the SSO provider after a successful OmniAuth login.
|
|
99
|
+
email:
|
|
100
|
+
- daniele.frisanco@gmail.com
|
|
101
|
+
executables: []
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- ".rspec_status"
|
|
106
|
+
- ".rubocop.yml"
|
|
107
|
+
- Gemfile
|
|
108
|
+
- Gemfile.lock
|
|
109
|
+
- LICENSE.txt
|
|
110
|
+
- README.md
|
|
111
|
+
- app/controllers/users/omniauth_callbacks_controller.rb
|
|
112
|
+
- config/initializers/omniauth_syncer.rb
|
|
113
|
+
- lib/omniauth_syncer.rb
|
|
114
|
+
- lib/omniauth_syncer/configuration.rb
|
|
115
|
+
- lib/omniauth_syncer/controller_helpers.rb
|
|
116
|
+
- lib/omniauth_syncer/engine.rb
|
|
117
|
+
- lib/omniauth_syncer/sync_service.rb
|
|
118
|
+
- lib/omniauth_syncer/version.rb
|
|
119
|
+
homepage: https://github.com/danielefrisanco/omniauth_syncer
|
|
120
|
+
licenses:
|
|
121
|
+
- MIT
|
|
122
|
+
metadata: {}
|
|
123
|
+
post_install_message:
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
require_paths:
|
|
126
|
+
- lib
|
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - ">="
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '0'
|
|
137
|
+
requirements: []
|
|
138
|
+
rubygems_version: 3.2.3
|
|
139
|
+
signing_key:
|
|
140
|
+
specification_version: 4
|
|
141
|
+
summary: A Ruby gem for synchronizing user profile data from OmniAuth to local ActiveRecord
|
|
142
|
+
models.
|
|
143
|
+
test_files: []
|