diff --git a/Gemfile b/Gemfile index 17a4563fa123b1eb7e51b668a5b9d7328c62a153..e907e4d405d0be4d408ba85a30d4475be5acbfbd 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source 'http://rubygems.org' +source 'https://rubygems.org' # Specify your gem's dependencies in omniauth-github.gemspec gemspec @@ -9,4 +9,5 @@ group :development, :test do gem 'guard-bundler' gem 'rb-fsevent' gem 'growl' + gem 'rake' end diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..86230ac37a7a2bec843d90d11b7783f4814bad08 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2011 Michael Bleigh and Intridea, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 2f422fea6a58f266a5fd30a36187148db634509a..8e53d7e21228d48d16b727cda74729c82baca916 100644 --- a/README.md +++ b/README.md @@ -6,28 +6,34 @@ on the [GitHub Applications Page](https://github.com/settings/applications). ## Basic Usage - use OmniAuth::Builder do - provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'] - end +```ruby +use OmniAuth::Builder do + provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'] +end +``` ## Github Enterprise Usage - provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], - { - :client_options => { - :site => 'https://github.YOURDOMAIN.com/api/v3', - :authorize_url => 'https://github.YOURDOMAIN.com/login/oauth/authorize', - :token_url => 'https://github.YOURDOMAIN.com/login/oauth/access_token', - } - } +```ruby +provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], + { + :client_options => { + :site => 'https://github.YOURDOMAIN.com/api/v3', + :authorize_url => 'https://github.YOURDOMAIN.com/login/oauth/authorize', + :token_url => 'https://github.YOURDOMAIN.com/login/oauth/access_token', + } + } +``` ## Scopes GitHub API v3 lets you set scopes to provide granular access to different types of data: - use OmniAuth::Builder do - provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: "user,repo,gist" - end +```ruby +use OmniAuth::Builder do + provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: "user,repo,gist" +end +``` More info on [Scopes](http://developer.github.com/v3/oauth/#scopes). diff --git a/checksums.yaml.gz b/checksums.yaml.gz deleted file mode 100644 index 56bc240296b2265f794853f749dcc465a0c8e2e1..0000000000000000000000000000000000000000 Binary files a/checksums.yaml.gz and /dev/null differ diff --git a/lib/omniauth-github/version.rb b/lib/omniauth-github/version.rb index b35518df8c00efa76f1e2b8bcd2f6c7a80ccb18a..0a188189dd8fdb14f0a68ae65ac8b4da47fbd92f 100644 --- a/lib/omniauth-github/version.rb +++ b/lib/omniauth-github/version.rb @@ -1,5 +1,5 @@ module OmniAuth module GitHub - VERSION = "1.1.2" + VERSION = "1.3.0" end end diff --git a/lib/omniauth/strategies/github.rb b/lib/omniauth/strategies/github.rb index 21c824ae404669860d7396906abc12973acd5d76..c60b2d1dcc28cdb6f2f27b5bbf6c4263611af178 100644 --- a/lib/omniauth/strategies/github.rb +++ b/lib/omniauth/strategies/github.rb @@ -12,7 +12,7 @@ module OmniAuth def request_phase super end - + def authorize_params super.tap do |params| %w[scope client_options].each do |v| @@ -32,14 +32,14 @@ module OmniAuth 'name' => raw_info['name'], 'image' => raw_info['avatar_url'], 'urls' => { - 'GitHub' => "https://github.com/#{raw_info['login']}", + 'GitHub' => raw_info['html_url'], 'Blog' => raw_info['blog'], }, } end extra do - {:raw_info => raw_info} + {:raw_info => raw_info, :all_emails => emails} end def raw_info @@ -48,12 +48,12 @@ module OmniAuth end def email - (email_access_allowed?) ? primary_email : raw_info['email'] + (email_access_allowed?) ? primary_email : raw_info['email'] end def primary_email - primary = emails.find{|i| i['primary'] } - primary && primary['email'] || emails.first && emails.first['email'] + primary = emails.find{ |i| i['primary'] && i['verified'] } + primary && primary['email'] || nil end # The new /user/emails API - http://developer.github.com/v3/users/emails/#future-response @@ -64,9 +64,15 @@ module OmniAuth end def email_access_allowed? - options['scope'] =~ /user/ + return false unless options['scope'] + email_scopes = ['user', 'user:email'] + scopes = options['scope'].split(',') + (scopes & email_scopes).any? end + def callback_url + full_host + script_name + callback_path + end end end end diff --git a/metadata.yml b/metadata.yml deleted file mode 100644 index 7a5770c50f8b5b663d046aeacbb2b266f1f33ae6..0000000000000000000000000000000000000000 --- a/metadata.yml +++ /dev/null @@ -1,140 +0,0 @@ ---- !ruby/object:Gem::Specification -name: omniauth-github -version: !ruby/object:Gem::Version - version: 1.1.2 -platform: ruby -authors: -- Michael Bleigh -autorequire: -bindir: bin -cert_chain: [] -date: 2014-04-10 00:00:00.000000000 Z -dependencies: -- !ruby/object:Gem::Dependency - name: omniauth - requirement: !ruby/object:Gem::Requirement - requirements: - - - ~> - - !ruby/object:Gem::Version - version: '1.0' - type: :runtime - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ~> - - !ruby/object:Gem::Version - version: '1.0' -- !ruby/object:Gem::Dependency - name: omniauth-oauth2 - requirement: !ruby/object:Gem::Requirement - requirements: - - - ~> - - !ruby/object:Gem::Version - version: '1.1' - type: :runtime - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ~> - - !ruby/object:Gem::Version - version: '1.1' -- !ruby/object:Gem::Dependency - name: rspec - requirement: !ruby/object:Gem::Requirement - requirements: - - - ~> - - !ruby/object:Gem::Version - version: '2.7' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ~> - - !ruby/object:Gem::Version - version: '2.7' -- !ruby/object:Gem::Dependency - name: rack-test - requirement: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' -- !ruby/object:Gem::Dependency - name: simplecov - requirement: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' -- !ruby/object:Gem::Dependency - name: webmock - requirement: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' -description: Official OmniAuth strategy for GitHub. -email: -- michael@intridea.com -executables: [] -extensions: [] -extra_rdoc_files: [] -files: -- .gitignore -- .rspec -- Gemfile -- Guardfile -- README.md -- Rakefile -- lib/omniauth-github.rb -- lib/omniauth-github/version.rb -- lib/omniauth/strategies/github.rb -- omniauth-github.gemspec -- spec/omniauth/strategies/github_spec.rb -- spec/spec_helper.rb -homepage: https://github.com/intridea/omniauth-github -licenses: [] -metadata: {} -post_install_message: -rdoc_options: [] -require_paths: -- lib -required_ruby_version: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' -required_rubygems_version: !ruby/object:Gem::Requirement - requirements: - - - '>=' - - !ruby/object:Gem::Version - version: '0' -requirements: [] -rubyforge_project: -rubygems_version: 2.0.3 -signing_key: -specification_version: 4 -summary: Official OmniAuth strategy for GitHub. -test_files: [] -has_rdoc: diff --git a/omniauth-github.gemspec b/omniauth-github.gemspec index 957a2c7ada5dea08dd22a1f2c5aca3abcb696214..675f8ff216a7838b67fe69af41428ce2c4ca2b58 100644 --- a/omniauth-github.gemspec +++ b/omniauth-github.gemspec @@ -7,6 +7,7 @@ Gem::Specification.new do |gem| gem.description = %q{Official OmniAuth strategy for GitHub.} gem.summary = %q{Official OmniAuth strategy for GitHub.} gem.homepage = "https://github.com/intridea/omniauth-github" + gem.license = "MIT" gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.files = `git ls-files`.split("\n") @@ -15,9 +16,9 @@ Gem::Specification.new do |gem| gem.require_paths = ["lib"] gem.version = OmniAuth::GitHub::VERSION - gem.add_dependency 'omniauth', '~> 1.0' - gem.add_dependency 'omniauth-oauth2', '~> 1.1' - gem.add_development_dependency 'rspec', '~> 2.7' + gem.add_dependency 'omniauth', '~> 1.5' + gem.add_dependency 'omniauth-oauth2', '>= 1.4.0', '< 2.0' + gem.add_development_dependency 'rspec', '~> 3.5' gem.add_development_dependency 'rack-test' gem.add_development_dependency 'simplecov' gem.add_development_dependency 'webmock' diff --git a/spec/omniauth/strategies/github_spec.rb b/spec/omniauth/strategies/github_spec.rb index 68dbbb73163ab613dfc3297c06d46fd4c1861965..302ee617a0cee02441015148c12555128f57c4b0 100644 --- a/spec/omniauth/strategies/github_spec.rb +++ b/spec/omniauth/strategies/github_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' describe OmniAuth::Strategies::GitHub do - let(:access_token) { stub('AccessToken', :options => {}) } - let(:parsed_response) { stub('ParsedResponse') } - let(:response) { stub('Response', :parsed => parsed_response) } + let(:access_token) { instance_double('AccessToken', :options => {}) } + let(:parsed_response) { instance_double('ParsedResponse') } + let(:response) { instance_double('Response', :parsed => parsed_response) } let(:enterprise_site) { 'https://some.other.site.com/api/v3' } let(:enterprise_authorize_url) { 'https://some.other.site.com/login/oauth/authorize' } @@ -25,111 +25,137 @@ describe OmniAuth::Strategies::GitHub do end before(:each) do - subject.stub!(:access_token).and_return(access_token) + allow(subject).to receive(:access_token).and_return(access_token) end - context "client options" do + context 'client options' do it 'should have correct site' do - subject.options.client_options.site.should eq("https://api.github.com") + expect(subject.options.client_options.site).to eq('https://api.github.com') end it 'should have correct authorize url' do - subject.options.client_options.authorize_url.should eq('https://github.com/login/oauth/authorize') + expect(subject.options.client_options.authorize_url).to eq('https://github.com/login/oauth/authorize') end it 'should have correct token url' do - subject.options.client_options.token_url.should eq('https://github.com/login/oauth/access_token') + expect(subject.options.client_options.token_url).to eq('https://github.com/login/oauth/access_token') end - describe "should be overrideable" do - it "for site" do - enterprise.options.client_options.site.should eq(enterprise_site) + describe 'should be overrideable' do + it 'for site' do + expect(enterprise.options.client_options.site).to eq(enterprise_site) end - it "for authorize url" do - enterprise.options.client_options.authorize_url.should eq(enterprise_authorize_url) + it 'for authorize url' do + expect(enterprise.options.client_options.authorize_url).to eq(enterprise_authorize_url) end - it "for token url" do - enterprise.options.client_options.token_url.should eq(enterprise_token_url) + it 'for token url' do + expect(enterprise.options.client_options.token_url).to eq(enterprise_token_url) end end end - context "#email_access_allowed?" do - it "should not allow email if scope is nil" do - subject.options['scope'].should be_nil - subject.should_not be_email_access_allowed + context '#email_access_allowed?' do + it 'should not allow email if scope is nil' do + expect(subject.options['scope']).to be_nil + expect(subject).to_not be_email_access_allowed end - it "should allow email if scope is user" do + it 'should allow email if scope is user' do subject.options['scope'] = 'user' - subject.should be_email_access_allowed + expect(subject).to be_email_access_allowed end - it "should allow email if scope is a bunch of stuff including user" do + it 'should allow email if scope is a bunch of stuff including user' do subject.options['scope'] = 'public_repo,user,repo,delete_repo,gist' - subject.should be_email_access_allowed + expect(subject).to be_email_access_allowed end - it "should not allow email if scope is other than user" do - subject.options['scope'] = 'repo' - subject.should_not be_email_access_allowed + it 'should not allow email if scope does not grant email access' do + subject.options['scope'] = 'repo,user:follow' + expect(subject).to_not be_email_access_allowed end - it "should assume email access not allowed if scope is something currently not documented " do + it 'should assume email access not allowed if scope is something currently not documented' do subject.options['scope'] = 'currently_not_documented' - subject.should_not be_email_access_allowed + expect(subject).to_not be_email_access_allowed end end - context "#email" do - it "should return email from raw_info if available" do - subject.stub!(:raw_info).and_return({'email' => 'you@example.com'}) - subject.email.should eq('you@example.com') + context '#email' do + it 'should return email from raw_info if available' do + allow(subject).to receive(:raw_info).and_return({ 'email' => 'you@example.com' }) + expect(subject.email).to eq('you@example.com') end - it "should return nil if there is no raw_info and email access is not allowed" do - subject.stub!(:raw_info).and_return({}) - subject.email.should be_nil + it 'should return nil if there is no raw_info and email access is not allowed' do + allow(subject).to receive(:raw_info).and_return({}) + expect(subject.email).to be_nil end - it "should return the primary email if there is no raw_info and email access is allowed" do + it 'should not return the primary email if there is no raw_info and email access is allowed' do emails = [ { 'email' => 'secondary@example.com', 'primary' => false }, { 'email' => 'primary@example.com', 'primary' => true } ] - subject.stub!(:raw_info).and_return({}) + allow(subject).to receive(:raw_info).and_return({}) subject.options['scope'] = 'user' - subject.stub!(:emails).and_return(emails) - subject.email.should eq('primary@example.com') + allow(subject).to receive(:emails).and_return(emails) + expect(subject.email).to be_nil end - it "should return the first email if there is no raw_info and email access is allowed" do + it 'should not return the first email if there is no raw_info and email access is allowed' do emails = [ { 'email' => 'first@example.com', 'primary' => false }, { 'email' => 'second@example.com', 'primary' => false } ] - subject.stub!(:raw_info).and_return({}) + allow(subject).to receive(:raw_info).and_return({}) subject.options['scope'] = 'user' - subject.stub!(:emails).and_return(emails) - subject.email.should eq('first@example.com') + allow(subject).to receive(:emails).and_return(emails) + expect(subject.email).to be_nil end end - context "#raw_info" do - it "should use relative paths" do - access_token.should_receive(:get).with('user').and_return(response) - subject.raw_info.should eq(parsed_response) + context '#raw_info' do + it 'should use relative paths' do + expect(access_token).to receive(:get).with('user').and_return(response) + expect(subject.raw_info).to eq(parsed_response) end end - context "#emails" do - it "should use relative paths" do - access_token.should_receive(:get).with('user/emails', :headers=>{"Accept"=>"application/vnd.github.v3"}).and_return(response) + context '#emails' do + it 'should use relative paths' do + expect(access_token).to receive(:get).with('user/emails', :headers => { + 'Accept' => 'application/vnd.github.v3' + }).and_return(response) + subject.options['scope'] = 'user' - subject.emails.should eq(parsed_response) + expect(subject.emails).to eq(parsed_response) + end + end + + context '#info.email' do + it 'should use any available email' do + allow(subject).to receive(:raw_info).and_return({}) + allow(subject).to receive(:email).and_return('you@example.com') + expect(subject.info['email']).to eq('you@example.com') + end + end + + context '#info.urls' do + it 'should use html_url from raw_info' do + allow(subject).to receive(:raw_info).and_return({ 'login' => 'me', 'html_url' => 'http://enterprise/me' }) + expect(subject.info['urls']['GitHub']).to eq('http://enterprise/me') end end + describe '#callback_url' do + it 'is a combination of host, script name, and callback path' do + allow(subject).to receive(:full_host).and_return('https://example.com') + allow(subject).to receive(:script_name).and_return('/sub_uri') + + expect(subject.callback_url).to eq('https://example.com/sub_uri/auth/github/callback') + end + end end