Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Octokit for authorization and fix 2FA bug. #87

Merged
merged 2 commits into from
Mar 28, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion git-review.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'yajl-ruby'
s.add_runtime_dependency 'hashie'
s.add_runtime_dependency 'gli', '~> 2.8.0'
s.add_runtime_dependency 'octokit', '~> 2.0.0'
s.add_runtime_dependency 'octokit', '~> 2.7.2'
s.add_development_dependency 'rspec', '>= 2.13.0'
s.add_development_dependency 'guard', '>= 2.0.3'
s.add_development_dependency 'guard-rspec', '>= 3.1.0'
Expand Down
62 changes: 35 additions & 27 deletions lib/git-review/provider/github.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,28 +178,46 @@ def configure_access

def configure_oauth
begin
prepare_username_and_password
print_auth_message
prepare_username unless github_login
prepare_password
prepare_description
authorize
rescue ::GitReview::AuthenticationError => e
rescue Octokit::Unauthorized => e
warn e.message
rescue ::GitReview::UnprocessableState => e
warn e.message
exit 1
end
end

def prepare_username_and_password
def github_login
login = git_call 'config github.user'
@username = login.chomp if login && !login.empty?
end

def print_auth_message
puts "Requesting a OAuth token for git-review."
puts "This procedure will grant access to your public and private "\
"repositories."
puts "You can revoke this authorization by visiting the following page: "\
"https://github.com/settings/applications"
end

def prepare_username
print "Please enter your GitHub's username: "
@username = STDIN.gets.chomp
print "Please enter your GitHub's password (it won't be stored anywhere): "
end

def prepare_password
print "Please enter your GitHub's password for #{@username} "\
"(it won't be stored anywhere): "
@password = STDIN.noecho(&:gets).chomp
print "\n"
end

def prepare_otp
print "PLease enter your One-Time-Password for GitHub's 2 Factor Authorization:"
@otp = STDIN.gets.chomp
end

def prepare_description(chosen_description=nil)
Expand All @@ -217,31 +235,21 @@ def prepare_description(chosen_description=nil)
end

def authorize
uri = URI('https://api.github.com/authorizations')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new(uri.request_uri)
req.basic_auth(@username, @password)
req.body = Yajl::Encoder.encode(
{
scopes: %w(repo),
note: @description
}
)
response = http.request(req)
if response.code == '201'
parser_response = Yajl::Parser.parse(response.body)
save_oauth_token(parser_response['token'])
elsif response.code == '401'
raise ::GitReview::AuthenticationError
else
raise ::GitReview::UnprocessableState, response.body
client = Octokit::Client.new :login => @username, :password => @password
begin
auth = client.create_authorization(:scopes => %w(repo),
:note => @description)
rescue Octokit::OneTimePasswordRequired
prepare_otp
auth = client.create_authorization(:scopes => %w(repo),
:note => @description,
:headers => {'X-GitHub-OTP' => @otp})
end
save_oauth_token(auth)
end

def save_oauth_token(token)
settings = ::GitReview::Settings.instance
settings.oauth_token = token
def save_oauth_token(auth)
settings.oauth_token = auth.token
settings.username = @username
settings.save!
puts "OAuth token successfully created.\n"
Expand Down
4 changes: 4 additions & 0 deletions spec/git-review/local_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

subject { ::GitReview::Local.new }

before :each do
::GitReview::Provider::Github.any_instance.stub :configure_oauth
end

describe '.instance' do

it 'gives back the same instance' do
Expand Down
28 changes: 28 additions & 0 deletions spec/git-review/provider/github_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@

context '# Authentication' do

it 'identifies github user name if present' do
github = ::GitReview::Provider::Github.any_instance
settings.stub :oauth_token
settings.stub :username
github.stub :print_auth_message
github.stub :prepare_password
github.stub :prepare_description
github.stub :authorize
github.should_receive(:github_login).and_return('existing_user')
github.should_not_receive(:prepare_username)
subject
end

it 'configures access to GitHub' do
::GitReview::Provider::Github.any_instance.should_receive :configure_access
subject
Expand All @@ -40,6 +53,21 @@
subject.login.should == user_login
end

it 'asks for OTP if 2FA is enabled' do
github = ::GitReview::Provider::Github.any_instance
github.stub :save_oauth_token
github.stub :configure_oauth
github.should_receive :prepare_otp
a_count = 0
Octokit::Client.any_instance.should_receive(:create_authorization).twice {
# 1st attempt is OAuth without 2FA
# if 2FA is enabled, OneTimePasswordRequired will be raised and
# start second attempt with OTP in header.
raise Octokit::OneTimePasswordRequired if (a_count += 1) == 1
}
subject.send(:authorize)
end

end

context '# Pull Requests' do
Expand Down
4 changes: 4 additions & 0 deletions spec/models/request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

subject { request }

before :each do
::GitReview::Provider::Github.any_instance.stub :configure_oauth
end

it 'has accessible attributes' do
subject.should be_accessible
end
Expand Down