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

Replace OAuth 1 with OAuth 2 #25

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ gem 'algorithms'
#gem 'bzip2-ruby'
gem 'libxml-ruby'
gem 'mechanize'
gem 'oauth'
gem "oauth2", "~> 2.0"
gem 'pg'
gem 'text'
gem 'typhoeus'
Expand Down
21 changes: 16 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ GEM
domain_name (0.6.20240107)
ethon (0.16.0)
ffi (>= 1.15.0)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0)
net-http
ffi (1.16.3)
hashie (5.0.0)
http-cookie (1.0.5)
domain_name (~> 0.5)
jwt (2.7.1)
libxml-ruby (5.0.2)
mechanize (2.9.1)
addressable (~> 2.8)
Expand All @@ -29,28 +34,34 @@ GEM
mime-types-data (3.2023.1205)
mini_portile2 (2.8.5)
minitest (5.21.1)
multi_xml (0.6.0)
net-http (0.4.1)
uri
net-http-digest_auth (1.4.1)
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
nokogiri (1.16.0)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
oauth-tty (1.0.5)
version_gem (~> 1.1, >= 1.1.1)
pg (1.5.4)
public_suffix (5.0.4)
racc (1.7.3)
rack (3.0.9)
rubyntlm (0.6.3)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
text (1.3.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
uri (0.13.0)
version_gem (1.1.3)
webrick (1.8.1)
webrobots (0.1.2)
Expand All @@ -63,7 +74,7 @@ DEPENDENCIES
libxml-ruby
mechanize
minitest
oauth
oauth2 (~> 2.0)
pg
text
typhoeus
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ this against an `apidb` format database, or possibly a live API.

# Requirements

To run this, you'll need Ruby (probably >=1.9.3) and some gems,
To run this, you'll need Ruby (probably >=3.0) and some gems,
which you can install with `bundle install`. Then you'll be able
to run:

Expand Down
18 changes: 18 additions & 0 deletions auth.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'oauth2'

module Auth
def self.client(y)
OAuth2::Client.new y["oauth2"]["client_id"],
nil,
{ :site => y["oauth2"]["site"],
:redirect_uri => "urn:ietf:wg:oauth:2.0:oob",
:authorize_url => "/oauth2/authorize",
:token_url => "/oauth2/token",
:raise_errors => false,
:connection_opts =>{ :request => { :open_timeout => 320, :read_timeout => 320 } } }
end

def self.access_token(y)
OAuth2::AccessToken.new client(y), y["oauth2"]["token"]
end
end
8 changes: 3 additions & 5 deletions example.auth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ database:
dbname: openstreetmap
user: openstreetmap
password: osm
oauth:
consumer_key:
consumer_secret:
oauth2:
client_id:
token:
token_secret:
site: http://localhost:3000
site: http://127.0.0.1:3000
tracker:
host: localhost
dbname: tracker
Expand Down
49 changes: 23 additions & 26 deletions get_auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,40 @@
# application, and each user gets the access_token stuff
# But hey, this is just a demo.

require './auth'
require 'rubygems'
require 'oauth'
require 'yaml'

y = YAML.load(File.open('auth.yaml'))

# Format of auth.yml:
# consumer_key: (from osm.org)
# consumer_secret: (from osm.org)
# token: (use oauth setup flow to get this)
# token_secret: (use oauth setup flow to get this)

puts "First, go register a new application at "
puts y["oauth"]["site"]
puts "Tick the appropriate boxes"
puts "Enter the consumer key you are assigned:"
y["oauth"]["consumer_key"] = gets.strip
puts "Enter the consumer secret you are assigned:"
y["oauth"]["consumer_secret"] = gets.strip
# client_id: (from osm.org)
# token: (use Authorization Code Grant to get this)

puts "First, go to a new app registration page at #{y['oauth2']['site']}/oauth2/applications/new"
puts "Fill in the following:"
puts "* Name: enter 'DWG redaction bot' or something else, depending on how you plan to use this bot"
puts "* Redirect URIs: enter 'urn:ietf:wg:oauth:2.0:oob' without quotes"
puts "* Confidential application?: untick this checkbox"
puts "* Permissions: tick the following:"
puts "** Modify the map"
puts "** Redact map data"
puts "Register the app"
puts "Enter the Client ID you are assigned:"
y["oauth2"]["client_id"] = gets.strip
puts "Your application is now set up, but you need to register"
puts "this instance of it with your user account."

@consumer=OAuth::Consumer.new y["oauth"]["consumer_key"],
y["oauth"]["consumer_secret"],
{:site=>y["oauth"]["site"]}

@request_token = @consumer.get_request_token
@client = Auth.client y

puts "Visit the following URL, log in if you need to, and authorize the app"
puts @request_token.authorize_url
puts "When you've authorized that token, enter the verifier code you are assigned:"
verifier = gets.strip
puts "Converting request token into access token..."
@access_token=@request_token.get_access_token(:oauth_verifier => verifier)

y["oauth"]["token"] = @access_token.token
y["oauth"]["token_secret"] = @access_token.secret
puts @client.auth_code.authorize_url(:scope => "write_api write_redactions")
puts "When you've authorized the app, enter the Authorization code you are assigned:"
code = gets.strip
puts "Converting code into token..."
@token=@client.auth_code.get_token(code)

y["oauth2"]["token"] = @token.token

File.open('auth.yaml', 'w') {|f| YAML.dump(y, f)}

Expand Down
35 changes: 13 additions & 22 deletions redact_changeset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
require './user'
require './db'
require './change_bot'
require './auth'
require 'net/http'
require 'nokogiri'
require 'set'
require 'oauth'
require 'yaml'
require 'optparse'
require 'typhoeus'
Expand All @@ -17,21 +17,12 @@

class Server
def initialize(file, dry_run)
auth = YAML.load(File.open(file))
oauth = auth['oauth']
@server = oauth['site']
y = YAML.load(File.open(file))
@server = y["oauth2"]["site"]
@dry_run = dry_run

unless @dry_run
@consumer=OAuth::Consumer.new(oauth['consumer_key'],
oauth['consumer_secret'],
{:site=>oauth['site']})

@consumer.http.open_timeout = 320
@consumer.http.read_timeout = 320

# Create the access_token for all traffic
@access_token = OAuth::AccessToken.new(@consumer, oauth['token'], oauth['token_secret'])
@access_token = Auth.access_token y
end

@max_retries = 3
Expand Down Expand Up @@ -80,11 +71,11 @@ def create_changeset(comment, input_changesets)

else
loop do
response = @access_token.put('/api/0.6/changeset/create', changeset_request, {'Content-Type' => 'text/xml' })
break if response.code == '200'
response = @access_token.put('/api/0.6/changeset/create', :body => changeset_request, :headers => {'Content-Type' => 'text/xml' }).response
break if response.success?
tries += 1
if tries >= @max_retries
raise "Failed to open changeset. Most recent response code: #{response.code}:\n#{response.body}"
raise "Failed to open changeset. Most recent response status: #{response.status}:\n#{response.body}"
end
end

Expand All @@ -101,12 +92,12 @@ def upload(change_doc, changeset_id)
else
tries = 0
loop do
response = @access_token.post("/api/0.6/changeset/#{changeset_id}/upload", change_doc, {'Content-Type' => 'text/xml' })
break if response.code == '200'
response = @access_token.post("/api/0.6/changeset/#{changeset_id}/upload", :body => change_doc, :headers => {'Content-Type' => 'text/xml' }).response
break if response.success?
tries += 1
if tries >= @max_retries
# It's quite likely for a changeset to fail, if someone else is editing in the area being processed
raise "Changeset failed to apply. Most recent response code: #{response.code}:\n#{response.body}"
raise "Changeset failed to apply. Most recent response status: #{response.status}:\n#{response.body}"
end
end
end
Expand All @@ -125,11 +116,11 @@ def redact(klass, elt_id, version, red_id)
else
tries = 0
loop do
response = @access_token.post("/api/0.6/#{name}/#{elt_id}/#{version}/redact?redaction=#{red_id}")
break if response.code == '200'
response = @access_token.post("/api/0.6/#{name}/#{elt_id}/#{version}/redact?redaction=#{red_id}").response
break if response.success?
tries += 1
if tries >= @max_retries
raise "Failed to redact element. Most recent response code: #{response.code} (#{response.body})"
raise "Failed to redact element. Most recent response status: #{response.status} (#{response.body})"
end
end
end
Expand Down
32 changes: 11 additions & 21 deletions run_bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
require './pg_db'
require './change_bot'
require './osm_print'
require './auth'

require 'pg'
require 'getoptlong'
require 'oauth'
require 'yaml'
require 'xml/libxml'
require 'logger'
Expand Down Expand Up @@ -137,8 +137,8 @@ def process_changeset(changeset)
<tag k="comment" v="Updates based on the redaction process"/>
</changeset>
</osm>'
response = @access_token.put('/api/0.6/changeset/create', changeset_request, {'Content-Type' => 'text/xml' })
unless response.code == '200'
response = @access_token.put('/api/0.6/changeset/create', changeset_request, {'Content-Type' => 'text/xml' }).response
unless response.success?
@log.error("Failed to open changeset!")
raise "Failed to open changeset"
end
Expand All @@ -151,10 +151,10 @@ def process_changeset(changeset)
@log.debug( "Changeset:\n" + change_doc )

unless @no_action
response = @access_token.post("/api/0.6/changeset/#{changeset_id}/upload", change_doc, {'Content-Type' => 'text/xml' })
unless response.code == '200'
response = @access_token.post("/api/0.6/changeset/#{changeset_id}/upload", change_doc, {'Content-Type' => 'text/xml' }).response
unless response.success?
# It's quite likely for a changeset to fail, if someone else is editing in the area being processed
@log.error("Changeset failed to apply. Response #{response.code}:\n#{response.body}")
@log.error("Changeset failed to apply. Response #{response.status}:\n#{response.body}")
@summary_changeset_fails += 1
raise "Changeset failed to apply"
end
Expand Down Expand Up @@ -220,9 +220,9 @@ def process_redactions(bot)
@log.info("Redaction for #{klass} #{redaction.element_id} v#{redaction.version} #{redaction.mode}")
unless @no_action
redaction_id = redaction.mode == :visible ? @redaction_id_visible : @redaction_id_hidden
response = @access_token.post("/api/0.6/#{klass}/#{redaction.element_id}/#{redaction.version}/redact?redaction=#{redaction_id}") if not @no_action
unless response.code == '200'
@log.error("Failed to redact element - response: #{response.code} \n #{response.body}")
response = @access_token.post("/api/0.6/#{klass}/#{redaction.element_id}/#{redaction.version}/redact?redaction=#{redaction_id}").response if not @no_action
unless response.success?
@log.error("Failed to redact element - response: #{response.status} \n #{response.body}")
raise "Failed to redact element"
end
end
Expand Down Expand Up @@ -341,24 +341,14 @@ def print_summary()
MAX_CHANGESET_ELEMENTS = 500

auth = YAML.load(File.open('auth.yaml'))
oauth = auth['oauth']
dbauth = auth['database']
trackerauth = auth['tracker']

@api_site = oauth['site']
@api_site = auth['oauth2']['site']

@tracker_conn = PGconn.open( :dbname => trackerauth['dbname'] )

# The consumer key and consumer secret are the identifiers for this particular application, and are
# issued when the application is registered with the site. Use your own.
@consumer=OAuth::Consumer.new oauth['consumer_key'],
oauth['consumer_secret'],
{:site=>oauth['site']}

@consumer.http.read_timeout = 320

# Create the access_token for all traffic
@access_token = OAuth::AccessToken.new(@consumer, oauth['token'], oauth['token_secret'])
@access_token = Auth.access_token auth

opts.each do |opt, arg|
case opt
Expand Down
22 changes: 5 additions & 17 deletions run_mega_relation.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
# Redacts our favourite mega relation

require 'oauth'
require './auth'
require 'yaml'
require 'logger'

Expand All @@ -16,20 +16,8 @@
@redaction_id_hidden = 1

auth = YAML.load(File.open('auth.yaml'))
oauth = auth['oauth']

@api_site = oauth['site']

# The consumer key and consumer secret are the identifiers for this particular application, and are
# issued when the application is registered with the site. Use your own.
@consumer=OAuth::Consumer.new oauth['consumer_key'],
oauth['consumer_secret'],
{:site=>oauth['site']}

@consumer.http.read_timeout = 320

# Create the access_token for all traffic
@access_token = OAuth::AccessToken.new(@consumer, oauth['token'], oauth['token_secret'])
@access_token = Auth.access_token auth

LOG_DIR = 'logs'
log_name = "#{Time.now.strftime('%Y%m%dT%H%M%S')}-#{$$}.log"
Expand All @@ -40,9 +28,9 @@
# Don't include the highest version
(205...HIGHEST_VERSION).each do |version|
puts "Redaction for relation #{ENTITY_ID} v#{version} hidden"
response = @access_token.post("/api/0.6/relation/#{ENTITY_ID}/#{version}/redact?redaction=#{@redaction_id_hidden}")
unless response.code == '200'
puts "Failed to redact element - response: #{response.code} \n #{response.body}"
response = @access_token.post("/api/0.6/relation/#{ENTITY_ID}/#{version}/redact?redaction=#{@redaction_id_hidden}").response
unless response.success?
puts "Failed to redact element - response: #{response.status} \n #{response.body}"
raise "Failed to redact element"
end
end