omniauth-mlh 1.0.0 → 4.0.1
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/.github/workflows/gem-push.yml +30 -0
- data/.github/workflows/test.yml +30 -0
- data/.rubocop.yml +25 -9
- data/.travis.yml +1 -1
- data/CONTRIBUTING.md +27 -0
- data/Gemfile +3 -0
- data/README.md +32 -14
- data/Rakefile +4 -0
- data/lib/omniauth/strategies/mlh.rb +80 -23
- data/lib/omniauth-mlh/version.rb +1 -1
- data/lib/{omniauth-mlh.rb → omniauth_mlh.rb} +0 -1
- data/omniauth-mlh.gemspec +8 -8
- data/spec/omni_auth/mlh_spec.rb +15 -0
- data/spec/omni_auth/strategies/mlh_spec.rb +196 -0
- data/spec/spec_helper.rb +21 -13
- data/spec/support/shared_examples.rb +69 -26
- metadata +55 -40
- data/spec/omniauth/mlh_spec.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 354aa7b0fc62afc09143d95359d8d9ee5f414bb8f38fbf5e9ee501ad2d547c98
|
4
|
+
data.tar.gz: d21f4b91b2a41f297350a6adedf60b33adf3390c255f1254b4741713b0f75042
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6302f3601f4338a87ccbdd0e77044742dd99b06cfe985af75373e089fe90102f3babb72a6fe22dacd674e9178f407d30baf34bb85bf3059fead97d466f7b2c6
|
7
|
+
data.tar.gz: 0302bfd088521db00c27954a7bc0454219f46acc1112166655b16e449342839fcb385a73c223296c9a0ad05997ebe41bf9b432fdbb4d1f529cc382cbf2fcdc05
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: Push Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- v*
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
build:
|
10
|
+
name: Build + Publish
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v3
|
15
|
+
- name: Set up Ruby 3.2
|
16
|
+
uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: '3.2'
|
19
|
+
bundler-cache: true
|
20
|
+
|
21
|
+
- name: Publish to RubyGems
|
22
|
+
run: |
|
23
|
+
mkdir -p $HOME/.gem
|
24
|
+
touch $HOME/.gem/credentials
|
25
|
+
chmod 0600 $HOME/.gem/credentials
|
26
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
27
|
+
gem build *.gemspec
|
28
|
+
gem push *.gem
|
29
|
+
env:
|
30
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- 'main'
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- 'main'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
ruby: ['3.2']
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby }}
|
23
|
+
- name: Install dependencies
|
24
|
+
run: |
|
25
|
+
gem install bundler
|
26
|
+
bundle install
|
27
|
+
- name: Run Spec
|
28
|
+
run: bundle exec rake spec
|
29
|
+
- name: Run Rubocop
|
30
|
+
run: bundle exec rake rubocop
|
data/.rubocop.yml
CHANGED
@@ -1,12 +1,28 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
- rubocop-performance
|
3
4
|
|
4
|
-
|
5
|
+
AllCops:
|
6
|
+
# Ruby 2.7 is the MINIMUM Ruby version supported
|
7
|
+
TargetRubyVersion: '2.7.0'
|
5
8
|
Exclude:
|
6
|
-
-
|
9
|
+
- vendor/**/*
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
Style/WordArray:
|
12
|
+
Description: Force arrays of words to use bracket notation instead of %w
|
13
|
+
EnforcedStyle: brackets
|
14
|
+
Enabled: true
|
15
|
+
|
16
|
+
Style/SymbolArray:
|
17
|
+
Description: Force symbol arrays to use bracket notation instead of %i
|
18
|
+
EnforcedStyle: brackets
|
19
|
+
Enabled: true
|
20
|
+
|
21
|
+
Style/RegexpLiteral:
|
22
|
+
Description: Allow forward slashes within regular expressions
|
23
|
+
AllowInnerSlashes: true
|
24
|
+
Enabled: true
|
25
|
+
|
26
|
+
RSpec/MultipleExpectations:
|
27
|
+
Description: Allow tests to contain multiple expectations
|
28
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Contributing Guidelines
|
2
|
+
|
3
|
+
Thank you for considering contributing to omniauth-mlh.
|
4
|
+
|
5
|
+
## Contributor Checklist
|
6
|
+
|
7
|
+
- Fork the Repo ( https://github.com/mlh/omniauth-mlh/fork )
|
8
|
+
- Create your feature branch (`git checkout -b my-new-feature`)
|
9
|
+
- Commit your changes (`git commit -am 'Add some feature'`)
|
10
|
+
- Push to the branch (`git push origin my-new-feature`)
|
11
|
+
- Create a new Pull Request
|
12
|
+
|
13
|
+
### Running tests
|
14
|
+
|
15
|
+
Following command can be used to run the test
|
16
|
+
|
17
|
+
```bash
|
18
|
+
$ bundle exec rake spec
|
19
|
+
```
|
20
|
+
|
21
|
+
## Code quality tools
|
22
|
+
|
23
|
+
Code quality is enforced across the entire gem with Rubocop:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ bundle exec rubocop
|
27
|
+
```
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
# MLH/
|
1
|
+
# MLH/omniauth-mlh
|
2
2
|
|
3
|
-
[](https://badge.fury.io/rb/omniauth-mlh)
|
4
|
+
[](https://github.com/MLH/omniauth-mlh/actions/workflows/test.yml)
|
4
5
|
|
5
6
|
This is the official [OmniAuth](https://github.com/omniauth/omniauth) strategy for
|
6
|
-
authenticating with [MyMLH](https://my.mlh.io). To use it, you'll need to
|
7
|
-
[register an application](https://my.mlh.io/
|
7
|
+
authenticating with [MyMLH](https://my.mlh.io) in Ruby applications. To use it, you'll need to
|
8
|
+
[register an application](https://my.mlh.io/developers) and obtain a OAuth Application ID and Secret from MyMLH.
|
8
9
|
|
9
|
-
It now supports MyMLH API
|
10
|
+
It now supports MyMLH API V4. [Read the MyMLH V4 docs here](https://my.mlh.io/developers/docs).
|
10
11
|
|
11
12
|
Once you have done so, you can follow the instructions below:
|
12
13
|
|
13
14
|
## Requirements
|
14
15
|
|
15
|
-
|
16
|
+
This Gem requires your Ruby version to be at least `3.2.0`.
|
16
17
|
|
17
18
|
## Installation
|
18
19
|
|
@@ -32,9 +33,13 @@ Or install it yourself as:
|
|
32
33
|
|
33
34
|
## Usage (Rack)
|
34
35
|
|
36
|
+
You can find a list of potential scopes and expandable fields in the [docs](https://my.mlh.io/developers/docs). The below defaults are provided simply as an example.
|
37
|
+
|
35
38
|
```ruby
|
36
39
|
use OmniAuth::Builder do
|
37
|
-
provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
40
|
+
provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
41
|
+
scope: 'public offline_access user:read:profile',
|
42
|
+
expand_fields: ['education']
|
38
43
|
end
|
39
44
|
```
|
40
45
|
|
@@ -44,17 +49,30 @@ end
|
|
44
49
|
# config/devise.rb
|
45
50
|
|
46
51
|
Devise.setup do |config|
|
47
|
-
config.provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
52
|
+
config.provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
53
|
+
scope: 'public offline_access user:read:profile',
|
54
|
+
expand_fields: ['education']
|
48
55
|
end
|
49
56
|
```
|
50
57
|
|
58
|
+
## Accessing User Data
|
59
|
+
Once a user has been authorized and you have received a token in your callback, you may access the scoped information for that user via the info key on the request data, as per the below example from a simple Sinatra app:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
get '/auth/mlh/callback' do
|
63
|
+
auth = request.env['omniauth.auth']
|
64
|
+
user_data = auth['info']
|
65
|
+
first_name = user_data['first_name']
|
66
|
+
erb "
|
67
|
+
<h1>Hello #{first_name}</h1>"
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
You can find the full User object in the [docs](https://my.mlh.io/developers/docs).
|
72
|
+
|
51
73
|
## Contributing
|
52
74
|
|
53
|
-
|
54
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
55
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
56
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
57
|
-
5. Create a new Pull Request
|
75
|
+
For guidance on setting up a development environment and how to make a contribution to omniauth-mlh, see the [contributing guidelines](https://github.com/MLH/omniauth-mlh/blob/main/CONTRIBUTING.md).
|
58
76
|
|
59
77
|
## Credit
|
60
78
|
|
@@ -63,6 +81,6 @@ We used part of [datariot/omniauth-paypal](http://github.com/datariot/omniauth-p
|
|
63
81
|
## Questions?
|
64
82
|
|
65
83
|
Have a question about the API or this library? Start by checking out the
|
66
|
-
[official MyMLH documentation](https://my.mlh.io/docs). If you still can't
|
84
|
+
[official MyMLH documentation](https://my.mlh.io/developers/docs). If you still can't
|
67
85
|
find an answer, tweet at [@MLHacks](http://twitter.com/mlhacks) or drop an
|
68
86
|
email to [engineering@mlh.io](mailto:engineering@mlh.io).
|
data/Rakefile
CHANGED
@@ -5,42 +5,99 @@ require 'ostruct'
|
|
5
5
|
|
6
6
|
module OmniAuth
|
7
7
|
module Strategies
|
8
|
+
# MLH OAuth2 Strategy
|
9
|
+
#
|
10
|
+
# @example Basic Usage
|
11
|
+
# use OmniAuth::Builder do
|
12
|
+
# provider :mlh, ENV['MLH_KEY'], ENV['MLH_SECRET']
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example With Expandable Fields
|
16
|
+
# use OmniAuth::Builder do
|
17
|
+
# provider :mlh, ENV['MLH_KEY'], ENV['MLH_SECRET'],
|
18
|
+
# expand_fields: ['education', 'professional_experience']
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example With Refresh Tokens (offline access)
|
22
|
+
# use OmniAuth::Builder do
|
23
|
+
# provider :mlh, ENV['MLH_KEY'], ENV['MLH_SECRET'],
|
24
|
+
# scope: 'user:read:profile offline_access'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# When offline_access scope is requested, the strategy will include
|
28
|
+
# refresh_token in the credentials hash if provided by the server.
|
8
29
|
class MLH < OmniAuth::Strategies::OAuth2 # :nodoc:
|
9
30
|
option :name, :mlh
|
10
31
|
|
11
32
|
option :client_options, {
|
12
33
|
site: 'https://my.mlh.io',
|
13
34
|
authorize_url: 'oauth/authorize',
|
14
|
-
token_url: 'oauth/token'
|
35
|
+
token_url: 'oauth/token',
|
36
|
+
auth_scheme: :request_body # Change from basic auth to request body
|
15
37
|
}
|
16
38
|
|
39
|
+
# Support expandable fields through options
|
40
|
+
option :expand_fields, []
|
41
|
+
|
17
42
|
uid { data[:id] }
|
18
43
|
|
19
44
|
info do
|
20
|
-
|
21
|
-
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
|
31
|
-
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
|
45
|
+
{
|
46
|
+
# Basic fields
|
47
|
+
id: data[:id],
|
48
|
+
created_at: data[:created_at],
|
49
|
+
updated_at: data[:updated_at],
|
50
|
+
first_name: data[:first_name],
|
51
|
+
last_name: data[:last_name],
|
52
|
+
email: data[:email],
|
53
|
+
phone_number: data[:phone_number],
|
54
|
+
roles: data[:roles],
|
55
|
+
|
56
|
+
# Expandable fields
|
57
|
+
profile: data[:profile],
|
58
|
+
address: data[:address],
|
59
|
+
social_profiles: data[:social_profiles],
|
60
|
+
professional_experience: data[:professional_experience],
|
61
|
+
education: data[:education],
|
62
|
+
identifiers: data[:identifiers]
|
63
|
+
}
|
37
64
|
end
|
38
65
|
|
39
66
|
def data
|
40
|
-
@data ||=
|
41
|
-
|
42
|
-
|
43
|
-
|
67
|
+
@data ||= fetch_and_process_data.compact
|
68
|
+
rescue StandardError
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def fetch_and_process_data
|
75
|
+
response = access_token.get(build_api_url)
|
76
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
77
|
+
return {} unless data.is_a?(Hash)
|
78
|
+
|
79
|
+
symbolize_nested_arrays(data)
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_api_url
|
83
|
+
url = 'https://api.mlh.com/v4/users/me'
|
84
|
+
expand_fields = options[:expand_fields] || []
|
85
|
+
return url if expand_fields.empty?
|
86
|
+
|
87
|
+
expand_query = expand_fields.map { |f| "expand[]=#{f}" }.join('&')
|
88
|
+
"#{url}?#{expand_query}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def symbolize_nested_arrays(hash)
|
92
|
+
hash.transform_values do |value|
|
93
|
+
case value
|
94
|
+
when Hash
|
95
|
+
symbolize_nested_arrays(value)
|
96
|
+
when Array
|
97
|
+
value.map { |item| item.is_a?(Hash) ? symbolize_nested_arrays(item) : item }
|
98
|
+
else
|
99
|
+
value
|
100
|
+
end
|
44
101
|
end
|
45
102
|
end
|
46
103
|
end
|
@@ -48,5 +105,5 @@ module OmniAuth
|
|
48
105
|
end
|
49
106
|
|
50
107
|
OmniAuth.config.add_camelization 'mlh', 'MLH'
|
51
|
-
OmniAuth.config.allowed_request_methods =
|
108
|
+
OmniAuth.config.allowed_request_methods = [:post, :get]
|
52
109
|
OmniAuth.config.silence_get_warning = true
|
data/lib/omniauth-mlh/version.rb
CHANGED
data/omniauth-mlh.gemspec
CHANGED
@@ -8,15 +8,15 @@ require 'omniauth-mlh/version'
|
|
8
8
|
Gem::Specification.new do |spec|
|
9
9
|
spec.name = 'omniauth-mlh'
|
10
10
|
spec.version = OmniAuth::MLH::VERSION
|
11
|
-
spec.authors = ['
|
12
|
-
spec.email = ['
|
11
|
+
spec.authors = ['Major League Hacking (MLH)']
|
12
|
+
spec.email = ['hi@mlh.io']
|
13
13
|
|
14
14
|
spec.summary = 'Official OmniAuth strategy for MyMLH.'
|
15
15
|
spec.description = 'Official OmniAuth strategy for MyMLH.'
|
16
16
|
spec.homepage = 'http://github.com/mlh/omniauth-mlh'
|
17
17
|
spec.license = 'MIT'
|
18
18
|
|
19
|
-
spec.required_ruby_version = '>= 2.
|
19
|
+
spec.required_ruby_version = '>= 2.7.0'
|
20
20
|
|
21
21
|
spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
22
22
|
spec.files = `git ls-files`.split("\n")
|
@@ -26,13 +26,13 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency 'oauth2', '~> 2.0.9'
|
27
27
|
spec.add_dependency 'omniauth', '~> 2.1.1'
|
28
28
|
spec.add_dependency 'omniauth-oauth2', '~> 1.8.0'
|
29
|
-
spec.add_dependency 'omniauth-rails_csrf_protection', '~> 1.0.1'
|
30
|
-
|
31
|
-
spec.add_dependency 'activesupport'
|
32
29
|
|
33
30
|
spec.add_development_dependency 'rack-test'
|
34
|
-
spec.add_development_dependency 'rake', '~>
|
35
|
-
spec.add_development_dependency 'rspec', '~>
|
31
|
+
spec.add_development_dependency 'rake', '~> 12.3.3'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
33
|
+
spec.add_development_dependency 'rubocop', '~> 1.0'
|
34
|
+
spec.add_development_dependency 'rubocop-performance'
|
35
|
+
spec.add_development_dependency 'rubocop-rspec'
|
36
36
|
spec.add_development_dependency 'simplecov'
|
37
37
|
spec.add_development_dependency 'webmock'
|
38
38
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe OmniAuth::MLH do
|
6
|
+
it 'has a version number' do
|
7
|
+
expect(OmniAuth::MLH::VERSION).not_to be_nil
|
8
|
+
expect(OmniAuth::MLH::VERSION).to eq('4.0.1')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'loads the MLH strategy' do
|
12
|
+
expect(OmniAuth::Strategies::MLH).to be_a(Class)
|
13
|
+
expect(OmniAuth::Strategies::MLH.superclass).to eq(OmniAuth::Strategies::OAuth2)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe OmniAuth::Strategies::MLH do
|
6
|
+
let(:strategy) { described_class.new(app, 'client_id', 'client_secret') }
|
7
|
+
|
8
|
+
let(:app) { ->(_env) { [200, {}, ['Hello.']] } }
|
9
|
+
let(:access_token) { instance_double(OAuth2::AccessToken, options: {}) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(strategy).to receive(:access_token).and_return(access_token)
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_context 'with oauth response' do |response_data|
|
16
|
+
let(:oauth_response) do
|
17
|
+
instance_double(OAuth2::Response,
|
18
|
+
body: response_data.to_json,
|
19
|
+
parsed: response_data)
|
20
|
+
end
|
21
|
+
before do
|
22
|
+
allow(access_token).to receive(:get)
|
23
|
+
.with('https://api.mlh.com/v4/users/me')
|
24
|
+
.and_return(oauth_response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#data' do
|
29
|
+
context 'with expandable fields' do
|
30
|
+
let(:response) do
|
31
|
+
instance_double(OAuth2::Response, body: {}.to_json, parsed: {})
|
32
|
+
end
|
33
|
+
let(:expand_url) { 'https://api.mlh.com/v4/users/me?expand[]=profile&expand[]=education' }
|
34
|
+
|
35
|
+
before do
|
36
|
+
allow(strategy).to receive(:options).and_return(expand_fields: ['profile', 'education'])
|
37
|
+
allow(access_token).to receive(:get).with(expand_url).and_return(response)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'constructs the correct URL with expand parameters' do
|
41
|
+
strategy.data
|
42
|
+
|
43
|
+
expect(access_token).to have_received(:get).with(expand_url)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns an empty hash for empty response' do
|
47
|
+
expect(strategy.data).to eq({})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with v4 API nested profile data' do
|
52
|
+
include_context 'with oauth response', {
|
53
|
+
'id' => 'test-id',
|
54
|
+
'first_name' => 'Jane',
|
55
|
+
'profile' => {
|
56
|
+
'age' => 22,
|
57
|
+
'gender' => 'Female'
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
it 'correctly parses nested profile data' do
|
62
|
+
result = strategy.data
|
63
|
+
|
64
|
+
expect(result).to be_a(Hash)
|
65
|
+
expect(result[:profile]).to eq({ age: 22, gender: 'Female' })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with v4 API empty response' do
|
70
|
+
include_context 'with oauth response', {}
|
71
|
+
|
72
|
+
it 'returns an empty hash for empty data' do
|
73
|
+
expect(strategy.data).to eq({})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with v4 API complex data structures' do
|
78
|
+
include_context 'with oauth response', {
|
79
|
+
'id' => 'test-id',
|
80
|
+
'education' => [{
|
81
|
+
'school' => {
|
82
|
+
'name' => 'Test University',
|
83
|
+
'location' => 'Test City'
|
84
|
+
},
|
85
|
+
'graduation_year' => 2024
|
86
|
+
}],
|
87
|
+
'social_profiles' => [
|
88
|
+
{ 'platform' => 'github', 'url' => 'https://github.com' },
|
89
|
+
'https://twitter.com'
|
90
|
+
],
|
91
|
+
'professional_experience' => [{
|
92
|
+
'company' => 'Tech Corp',
|
93
|
+
'positions' => [
|
94
|
+
{ 'title' => 'Engineer', 'years' => [2022, 2023] }
|
95
|
+
]
|
96
|
+
}]
|
97
|
+
}
|
98
|
+
|
99
|
+
it 'correctly processes complex nested structures' do
|
100
|
+
result = strategy.data
|
101
|
+
|
102
|
+
expect(result).to be_a(Hash)
|
103
|
+
expect(result[:education].first[:school]).to eq({ name: 'Test University', location: 'Test City' })
|
104
|
+
expect(result[:social_profiles]).to eq([{ platform: 'github', url: 'https://github.com' }, 'https://twitter.com'])
|
105
|
+
expect(result[:professional_experience].first[:positions].first[:years]).to eq([2022, 2023])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'with v4 API nil and empty values' do
|
110
|
+
include_context 'with oauth response', {
|
111
|
+
'id' => 'test-id',
|
112
|
+
'first_name' => 'Jane',
|
113
|
+
'last_name' => nil,
|
114
|
+
'profile' => {
|
115
|
+
'age' => 22,
|
116
|
+
'gender' => nil,
|
117
|
+
'location' => {
|
118
|
+
'city' => nil,
|
119
|
+
'country' => 'USA'
|
120
|
+
}
|
121
|
+
},
|
122
|
+
'education' => nil,
|
123
|
+
'social_profiles' => {
|
124
|
+
'github' => {},
|
125
|
+
'linkedin' => {}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
it 'handles nil values correctly' do
|
130
|
+
result = strategy.data
|
131
|
+
|
132
|
+
expect(result[:last_name]).to be_nil
|
133
|
+
expect(result[:profile][:gender]).to be_nil
|
134
|
+
expect(result[:profile][:location]).to eq({ city: nil, country: 'USA' })
|
135
|
+
expect(result[:education]).to be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'handles empty hash structures' do
|
139
|
+
result = strategy.data
|
140
|
+
|
141
|
+
expect(result[:social_profiles]).to eq({ github: {}, linkedin: {} })
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'with API error' do
|
146
|
+
it 'returns empty hash on error' do
|
147
|
+
allow(access_token).to receive(:get).and_raise(StandardError)
|
148
|
+
|
149
|
+
expect(strategy.data).to eq({})
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#uid' do
|
155
|
+
context 'with valid data' do
|
156
|
+
it 'returns the id from the data hash' do
|
157
|
+
allow(strategy).to receive(:data).and_return({ id: 'test-123' })
|
158
|
+
expect(strategy.uid).to eq('test-123')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'with missing id' do
|
163
|
+
it 'returns nil when id is not present' do
|
164
|
+
allow(strategy).to receive(:data).and_return({})
|
165
|
+
expect(strategy.uid).to be_nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe '#info' do
|
171
|
+
let(:user_data) do
|
172
|
+
{
|
173
|
+
first_name: 'Jane',
|
174
|
+
last_name: 'Hacker',
|
175
|
+
email: 'jane@example.com',
|
176
|
+
roles: ['hacker']
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
before do
|
181
|
+
allow(strategy).to receive(:data).and_return(user_data)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'includes basic user information' do
|
185
|
+
expect(strategy.info).to include(
|
186
|
+
first_name: 'Jane',
|
187
|
+
last_name: 'Hacker',
|
188
|
+
email: 'jane@example.com'
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'includes user roles' do
|
193
|
+
expect(strategy.info[:roles]).to eq(['hacker'])
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,23 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
4
|
-
|
5
|
-
require 'simplecov'
|
6
|
-
SimpleCov.start
|
7
|
-
|
8
4
|
require 'bundler/setup'
|
9
|
-
require 'rspec'
|
10
|
-
require 'rack/test'
|
11
5
|
require 'webmock/rspec'
|
6
|
+
require 'simplecov'
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/core_ext/hash'
|
9
|
+
|
10
|
+
# Configure SimpleCov before requiring our library
|
11
|
+
SimpleCov.start do
|
12
|
+
add_filter '/spec/'
|
13
|
+
add_filter '/vendor/'
|
14
|
+
track_files 'lib/**/*.rb'
|
15
|
+
enable_coverage :branch
|
16
|
+
end
|
17
|
+
|
12
18
|
require 'omniauth'
|
13
|
-
require 'omniauth-
|
19
|
+
require 'omniauth-oauth2'
|
20
|
+
require 'omniauth_mlh'
|
21
|
+
require 'omniauth/strategies/mlh'
|
14
22
|
|
15
|
-
Dir[File.expand_path('support
|
23
|
+
Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
|
16
24
|
|
17
25
|
RSpec.configure do |config|
|
18
|
-
config.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
OmniAuth.config.test_mode = true
|
26
|
+
config.expect_with :rspec do |c|
|
27
|
+
c.syntax = :expect
|
28
|
+
end
|
23
29
|
end
|
30
|
+
|
31
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
@@ -1,41 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
RSpec.shared_examples 'basic data retrieval tests' do
|
4
|
+
context 'with successful API response' do
|
5
|
+
before do
|
6
|
+
allow(access_token).to receive(:get)
|
7
|
+
.with('https://api.mlh.com/v4/users/me')
|
8
|
+
.and_return(instance_double(OAuth2::Response, parsed: { 'data' => base_user_data }))
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns symbolized user data' do
|
12
|
+
expect(strategy.data).to include(
|
13
|
+
id: 'c2ac35c6-aa8c-11ed-afa1-0242ac120002',
|
14
|
+
first_name: 'Jane'
|
15
|
+
)
|
11
16
|
end
|
12
17
|
end
|
18
|
+
end
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
20
|
+
RSpec.shared_examples 'expandable fields tests' do
|
21
|
+
let(:expanded_user_data) do
|
22
|
+
base_user_data.merge(
|
23
|
+
'profile' => {
|
24
|
+
'age' => 22,
|
25
|
+
'gender' => 'Female'
|
26
|
+
},
|
27
|
+
'education' => [{
|
28
|
+
'current' => true,
|
29
|
+
'school_name' => 'Hacker University',
|
30
|
+
'major' => 'Computer Science'
|
31
|
+
}]
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with expandable fields' do
|
36
|
+
before do
|
37
|
+
allow(access_token).to receive(:get)
|
38
|
+
.with('https://api.mlh.com/v4/users/me?expand[]=profile&expand[]=education')
|
39
|
+
.and_return(instance_double(OAuth2::Response, parsed: { 'data' => expanded_user_data }))
|
40
|
+
allow(strategy).to receive(:options).and_return(expand_fields: ['profile', 'education'])
|
19
41
|
end
|
20
42
|
|
21
|
-
it '
|
22
|
-
|
23
|
-
|
24
|
-
|
43
|
+
it 'fetches expanded fields' do
|
44
|
+
expect(strategy.data).to include(
|
45
|
+
profile: include(age: 22),
|
46
|
+
education: include(hash_including(school_name: 'Hacker University'))
|
47
|
+
)
|
25
48
|
end
|
26
49
|
end
|
50
|
+
end
|
27
51
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
expect(subject.token_params['baz']).to eq('zip')
|
52
|
+
RSpec.shared_examples 'error handling tests' do
|
53
|
+
context 'when API returns error' do
|
54
|
+
before do
|
55
|
+
allow(access_token).to receive(:get).and_raise(StandardError)
|
33
56
|
end
|
34
57
|
|
35
|
-
it '
|
36
|
-
|
37
|
-
expect(subject.token_params['scope']).to eq('bar')
|
38
|
-
expect(subject.token_params['foo']).to eq('baz')
|
58
|
+
it 'returns empty hash on error' do
|
59
|
+
expect(strategy.data).to eq({})
|
39
60
|
end
|
40
61
|
end
|
41
62
|
end
|
63
|
+
|
64
|
+
RSpec.shared_examples 'info hash tests' do
|
65
|
+
let(:user_info) do
|
66
|
+
{
|
67
|
+
id: 'c2ac35c6-aa8c-11ed-afa1-0242ac120002',
|
68
|
+
first_name: 'Jane',
|
69
|
+
last_name: 'Hacker',
|
70
|
+
email: 'jane.hacker@example.com'
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
before do
|
75
|
+
allow(strategy).to receive(:data).and_return(user_info)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns formatted info hash' do
|
79
|
+
expect(strategy.info).to include(
|
80
|
+
name: 'Jane Hacker',
|
81
|
+
email: 'jane.hacker@example.com'
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-mlh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- Major League Hacking (MLH)
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oauth2
|
@@ -53,75 +53,89 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.8.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rack-test
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
62
|
-
type: :
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
76
|
-
type: :
|
75
|
+
version: 12.3.3
|
76
|
+
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 12.3.3
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '3.10'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '3.10'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rubocop
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '1.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '1.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: rubocop-performance
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: simplecov
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -152,31 +166,35 @@ dependencies:
|
|
152
166
|
version: '0'
|
153
167
|
description: Official OmniAuth strategy for MyMLH.
|
154
168
|
email:
|
155
|
-
-
|
169
|
+
- hi@mlh.io
|
156
170
|
executables: []
|
157
171
|
extensions: []
|
158
172
|
extra_rdoc_files: []
|
159
173
|
files:
|
174
|
+
- ".github/workflows/gem-push.yml"
|
175
|
+
- ".github/workflows/test.yml"
|
160
176
|
- ".gitignore"
|
161
177
|
- ".rspec"
|
162
178
|
- ".rubocop.yml"
|
163
179
|
- ".travis.yml"
|
180
|
+
- CONTRIBUTING.md
|
164
181
|
- Gemfile
|
165
182
|
- LICENSE.txt
|
166
183
|
- README.md
|
167
184
|
- Rakefile
|
168
|
-
- lib/omniauth-mlh.rb
|
169
185
|
- lib/omniauth-mlh/version.rb
|
170
186
|
- lib/omniauth/strategies/mlh.rb
|
187
|
+
- lib/omniauth_mlh.rb
|
171
188
|
- omniauth-mlh.gemspec
|
172
|
-
- spec/
|
189
|
+
- spec/omni_auth/mlh_spec.rb
|
190
|
+
- spec/omni_auth/strategies/mlh_spec.rb
|
173
191
|
- spec/spec_helper.rb
|
174
192
|
- spec/support/shared_examples.rb
|
175
193
|
homepage: http://github.com/mlh/omniauth-mlh
|
176
194
|
licenses:
|
177
195
|
- MIT
|
178
196
|
metadata: {}
|
179
|
-
post_install_message:
|
197
|
+
post_install_message:
|
180
198
|
rdoc_options: []
|
181
199
|
require_paths:
|
182
200
|
- lib
|
@@ -184,18 +202,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
184
202
|
requirements:
|
185
203
|
- - ">="
|
186
204
|
- !ruby/object:Gem::Version
|
187
|
-
version: 2.
|
205
|
+
version: 2.7.0
|
188
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
207
|
requirements:
|
190
208
|
- - ">="
|
191
209
|
- !ruby/object:Gem::Version
|
192
210
|
version: '0'
|
193
211
|
requirements: []
|
194
|
-
rubygems_version: 3.
|
195
|
-
signing_key:
|
212
|
+
rubygems_version: 3.4.19
|
213
|
+
signing_key:
|
196
214
|
specification_version: 4
|
197
215
|
summary: Official OmniAuth strategy for MyMLH.
|
198
|
-
test_files:
|
199
|
-
- spec/omniauth/mlh_spec.rb
|
200
|
-
- spec/spec_helper.rb
|
201
|
-
- spec/support/shared_examples.rb
|
216
|
+
test_files: []
|
data/spec/omniauth/mlh_spec.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe OmniAuth::MLH do
|
6
|
-
subject do
|
7
|
-
OmniAuth::Strategies::MLH.new(nil, @options || {})
|
8
|
-
end
|
9
|
-
|
10
|
-
it_should_behave_like 'an oauth2 strategy'
|
11
|
-
|
12
|
-
describe '#client' do
|
13
|
-
it 'has correct MyMLH site' do
|
14
|
-
expect(subject.client.site).to eq('https://my.mlh.io')
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'has correct authorize url' do
|
18
|
-
expect(subject.client.options[:authorize_url]).to eq('oauth/authorize')
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'has correct token url' do
|
22
|
-
expect(subject.client.options[:token_url]).to eq('oauth/token')
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'runs the setup block if passed one' do
|
26
|
-
counter = ''
|
27
|
-
@options = { setup: proc { |_env| counter = 'ok' } }
|
28
|
-
subject.setup_phase
|
29
|
-
expect(counter).to eq('ok')
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
describe '#callback_path' do
|
34
|
-
it 'has the correct callback path' do
|
35
|
-
expect(subject.callback_path).to eq('/auth/mlh/callback')
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|