summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2013-05-15 22:03:08 -0700
committerelijah <elijah@riseup.net>2013-05-15 22:03:08 -0700
commit51dc03481a9be5148f16e8022a1b00b658739ff3 (patch)
treee370f488b6ce4b79c09778621d9f8fc2d95ee88d
parentb078659defeead5b68ca2f387d0e308fa7511eb4 (diff)
add CouchDB support.
-rw-r--r--config/default.yml2
-rw-r--r--lib/nickserver.rb2
-rw-r--r--lib/nickserver/config.rb2
-rw-r--r--lib/nickserver/couch/fetch_key.rb62
-rw-r--r--lib/nickserver/hkp/fetch_key.rb8
-rw-r--r--lib/nickserver/hkp/fetch_key_info.rb4
-rw-r--r--lib/nickserver/server.rb23
-rw-r--r--test/files/bananas@localhost3
-rw-r--r--test/files/blue_couchdb_result11
-rw-r--r--test/files/blue_nickserver_result1
-rw-r--r--test/test_helper.rb14
-rw-r--r--test/unit/nickserver_test.rb53
12 files changed, 150 insertions, 35 deletions
diff --git a/config/default.yml b/config/default.yml
index c72e3b7..83bfa48 100644
--- a/config/default.yml
+++ b/config/default.yml
@@ -1,5 +1,7 @@
couch_host: 'localhost'
couch_port: 5984
couch_database: 'users'
+#couch_user: 'nickserver'
+#couch_password: 'blahblah'
hkp_url: 'https://hkps.pool.sks-keyservers.net:/pks/lookup'
port: 6425
diff --git a/lib/nickserver.rb b/lib/nickserver.rb
index 9ddc839..f97a95f 100644
--- a/lib/nickserver.rb
+++ b/lib/nickserver.rb
@@ -2,6 +2,8 @@
require "nickserver/version"
require "nickserver/config"
+require "nickserver/couch/fetch_key"
+
require "nickserver/hkp/key_info"
require "nickserver/hkp/fetch_key_info"
require "nickserver/hkp/fetch_key"
diff --git a/lib/nickserver/config.rb b/lib/nickserver/config.rb
index 779f0a8..3f92186 100644
--- a/lib/nickserver/config.rb
+++ b/lib/nickserver/config.rb
@@ -12,6 +12,8 @@ module Nickserver
attr_accessor :couch_port
attr_accessor :couch_host
attr_accessor :couch_database
+ attr_accessor :couch_user
+ attr_accessor :couch_password
attr_accessor :port
attr_accessor :loaded
end
diff --git a/lib/nickserver/couch/fetch_key.rb b/lib/nickserver/couch/fetch_key.rb
new file mode 100644
index 0000000..d729ebc
--- /dev/null
+++ b/lib/nickserver/couch/fetch_key.rb
@@ -0,0 +1,62 @@
+require 'em-http'
+require 'json'
+
+module Nickserver; module Couch
+ class FetchKey
+ include EM::Deferrable
+
+ def initialize(options={})
+ @timeout = 5
+ end
+
+ def get(uid)
+ uid = uid.split('@').first # TEMPORARY HACK FOR NOW. in the future
+ # the database should be able to be searchable by full address
+ couch_request(uid)
+ self
+ end
+
+ protected
+
+ #
+ # curl http://localhost:5984/users/_design/User/_view/pgp_key_by_handle?key=%22bla%22\&reduce=false
+ #
+ def couch_request(uid)
+ query = {"reduce" => "false", "key" => "\"#{uid}\""}
+ request = EventMachine::HttpRequest.new("#{FetchKey.couch_url}/#{FetchKey.couch_view}").get(:timeout => @timeout, :query => query)
+ request.callback {|http|
+ if http.response_header.status != 200
+ self.fail http.response_header.status, 'Unknown Error'
+ else
+ self.succeed parse_key_from_response(uid, http.response)
+ end
+ }.errback {|http|
+ self.fail 0, http.error
+ }
+ end
+
+ def parse_key_from_response(uid, response)
+ json = JSON.load(response)
+ if json["offset"] == 0
+ self.fail 404, "Not Found"
+ else
+ return json["rows"].first["value"]
+ end
+ rescue Exception
+ self.fail 0, "Error parsing CouchDB reply"
+ end
+
+ def self.couch_view
+ "_design/User/_view/pgp_key_by_handle"
+ end
+
+ def self.couch_url
+ if Config.couch_user
+ ['http://', Config.couch_user, ':', Config.couch_password, '@', Config.couch_host, ':', Config.couch_port, '/', Config.couch_database].join
+ else
+ ['http://', Config.couch_host, ':', Config.couch_port, '/', Config.couch_database].join
+ end
+ end
+
+ end
+end; end \ No newline at end of file
diff --git a/lib/nickserver/hkp/fetch_key.rb b/lib/nickserver/hkp/fetch_key.rb
index 211ae92..6f91d8c 100644
--- a/lib/nickserver/hkp/fetch_key.rb
+++ b/lib/nickserver/hkp/fetch_key.rb
@@ -16,8 +16,8 @@ module Nickserver; module HKP
get_key_by_fingerprint(best.keyid) {|key|
self.succeed key
}
- }.errback {|msg|
- self.fail msg
+ }.errback {|status, msg|
+ self.fail status, msg
}
self
end
@@ -30,13 +30,13 @@ module Nickserver; module HKP
http = EventMachine::HttpRequest.new(Config.hkp_url).get(:query => params)
http.callback {
if http.response_header.status != 200
- self.fail http.response_header.status #"Request failed with #{http.response_header.status}: #{http.response}"
+ self.fail http.response_header.status, "HKP Request failed"
else
yield http.response
end
}
http.errback {
- self.fail http.error
+ self.fail 0, http.error
}
end
diff --git a/lib/nickserver/hkp/fetch_key_info.rb b/lib/nickserver/hkp/fetch_key_info.rb
index dce0326..b341aad 100644
--- a/lib/nickserver/hkp/fetch_key_info.rb
+++ b/lib/nickserver/hkp/fetch_key_info.rb
@@ -17,12 +17,12 @@ module Nickserver; module HKP
params = {:op => 'vindex', :search => uid, :exact => 'on', :options => 'mr', :fingerprint => 'on'}
EventMachine::HttpRequest.new(Config.hkp_url).get(:query => params).callback {|http|
if http.response_header.status != 200
- self.fail http.response_header.status
+ self.fail http.response_header.status, "Could net fetch keyinfo"
else
self.succeed parse(uid, http.response)
end
}.errback {|http|
- self.fail http.error
+ self.fail 0, http.error
}
self
end
diff --git a/lib/nickserver/server.rb b/lib/nickserver/server.rb
index 7e79b98..0bda4f1 100644
--- a/lib/nickserver/server.rb
+++ b/lib/nickserver/server.rb
@@ -5,6 +5,8 @@ require 'json'
#
# This is the main HTTP server that clients connect to in order to fetch keys
#
+# For info on EM::HttpServer, see https://github.com/eventmachine/evma_httpserver
+#
module Nickserver
class Server < EM::Connection
include EM::HttpServer
@@ -43,7 +45,7 @@ module Nickserver
send_response(:status => 500, :content => msg)
end
- def send_not_found(msg = "not found")
+ def send_not_found(msg = "404 Not Found")
send_response(:status => 404, :content => msg)
end
@@ -74,15 +76,20 @@ module Nickserver
end
def get_key_from_uid(uid)
- if local_address?(uid)
- send_not_found
+ fetcher = if local_address?(uid)
+ Nickserver::Couch::FetchKey.new
else
- Nickserver::HKP::FetchKey.new.get(uid).callback {|key|
- yield key
- }.errback {|status|
- send_response(:status => status, :content => 'could not fetch key')
- }
+ Nickserver::HKP::FetchKey.new
end
+ fetcher.get(uid).callback {|key|
+ yield key
+ }.errback {|status, msg|
+ if status == 404
+ send_not_found
+ else
+ send_response(:status => status, :content => msg)
+ end
+ }
end
def format_response(map)
diff --git a/test/files/bananas@localhost b/test/files/bananas@localhost
deleted file mode 100644
index 69df2bf..0000000
--- a/test/files/bananas@localhost
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "openpgp": "hi"
-} \ No newline at end of file
diff --git a/test/files/blue_couchdb_result b/test/files/blue_couchdb_result
new file mode 100644
index 0000000..1e164b8
--- /dev/null
+++ b/test/files/blue_couchdb_result
@@ -0,0 +1,11 @@
+{
+ "total_rows":2,
+ "offset":1,
+ "rows":[
+ {
+ "id":"c58e909ee1291985b9c5954efa1f91e7",
+ "key":"blue",
+ "value":"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVmQGiBEDsRrURBACwk7zArsD89vGDlCoBV55RzBeeIyoiNwgQuVlq+t8nGqRdPr+B\r\nZY1mCN6Bj/SOvPgG1DjBbh5CSXuYGzLEw9lkGlAY4VGVvQteHXf3ZpRRtj0SbzEq\r\nUUECLAU4m3L/nOwbTU4Osi5zilQZOTqIooMB+Wq5qCz7rvvzDHPBHF6OHwCg9Wrb\r\n9LT7e0PuRB0qTCyFjIhjB6ED/Ax2ma2D/at4/CVhno0hfZqd3bCCkQDvnArxQfZr\r\nFF3CpnF4djzouGiFYNi3h6y33qaD+ms9/TrewItvvHXgdtPTxHe+12mqWjYTyJPa\r\nN9fXun29YWQrycHLR1NcVo7bLJJ6ESSIhb2Ingo+rsCd5bdqfmMZ7Y0sb0jMrpqB\r\n/MNMA/9EfaKHi/eTREe0/04ZV5rS9tUYAz4elL0yOfwaSbDAgoUatgGAQaQpSBMG\r\n/e+pMyEu07u4Ft2MqG3hPu3Wph0TwWJMANOeFjG4HbJj/9TLYQB1J5mjZa3D091T\r\nxhu7oHzLT5gQL0chCCxHp61lx9tSMqQ/j7F0yZ9WZ8YFV8QDbYhJBCARCgAJBQJP\r\nv/MkAh0CAAoJEKTufqYlGMO1YscAnjrP1HfW10nbOJxxGtRjzdSmESMHAJwIcJMv\r\nX9uCVqwo45h514vu94/N7TPveF2rIEq4yW5IBqxG5aJ0dJDdGjg5GjvmVJtnNIux\r\nonXCYXLcPjkmO+yw8VHZWFUapeIDaWcsXlaBLv/L6uKrrgtrj8v3pUJuOfAuw9td\r\n5nwXz7JXWHEjjMk37HGQHa/fJVwHrRZm99c21jAD4tziZYl4lTM7afBPYiFDO8A0\r\nxBAy4R3dNf2CSBn0\r\n=qogX\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n"
+ }
+ ]
+}
diff --git a/test/files/blue_nickserver_result b/test/files/blue_nickserver_result
new file mode 100644
index 0000000..611e0df
--- /dev/null
+++ b/test/files/blue_nickserver_result
@@ -0,0 +1 @@
+{"address":"blue@example.org","openpgp":"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVmQGiBEDsRrURBACwk7zArsD89vGDlCoBV55RzBeeIyoiNwgQuVlq+t8nGqRdPr+B\r\nZY1mCN6Bj/SOvPgG1DjBbh5CSXuYGzLEw9lkGlAY4VGVvQteHXf3ZpRRtj0SbzEq\r\nUUECLAU4m3L/nOwbTU4Osi5zilQZOTqIooMB+Wq5qCz7rvvzDHPBHF6OHwCg9Wrb\r\n9LT7e0PuRB0qTCyFjIhjB6ED/Ax2ma2D/at4/CVhno0hfZqd3bCCkQDvnArxQfZr\r\nFF3CpnF4djzouGiFYNi3h6y33qaD+ms9/TrewItvvHXgdtPTxHe+12mqWjYTyJPa\r\nN9fXun29YWQrycHLR1NcVo7bLJJ6ESSIhb2Ingo+rsCd5bdqfmMZ7Y0sb0jMrpqB\r\n/MNMA/9EfaKHi/eTREe0/04ZV5rS9tUYAz4elL0yOfwaSbDAgoUatgGAQaQpSBMG\r\n/e+pMyEu07u4Ft2MqG3hPu3Wph0TwWJMANOeFjG4HbJj/9TLYQB1J5mjZa3D091T\r\nxhu7oHzLT5gQL0chCCxHp61lx9tSMqQ/j7F0yZ9WZ8YFV8QDbYhJBCARCgAJBQJP\r\nv/MkAh0CAAoJEKTufqYlGMO1YscAnjrP1HfW10nbOJxxGtRjzdSmESMHAJwIcJMv\r\nX9uCVqwo45h514vu94/N7TPveF2rIEq4yW5IBqxG5aJ0dJDdGjg5GjvmVJtnNIux\r\nonXCYXLcPjkmO+yw8VHZWFUapeIDaWcsXlaBLv/L6uKrrgtrj8v3pUJuOfAuw9td\r\n5nwXz7JXWHEjjMk37HGQHa/fJVwHrRZm99c21jAD4tziZYl4lTM7afBPYiFDO8A0\r\nxBAy4R3dNf2CSBn0\r\n=qogX\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n"} \ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 757bcd1..187ee05 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -42,11 +42,15 @@ class MiniTest::Unit::TestCase
end
def stub_couch_response(uid, opts = {})
- options = {:status => 200, :body => ""}.merge(opts)
- url = ['http://', Nickserver::Config.couch_host, ':', Nickserver::Config.couch_port].join
- stub_http_request(:get, url).with(
- :query => {:address => uid}
- ).to_return(options)
+ # can't stub localhost, so set couch_host to anything else
+ Nickserver::Config.stub :couch_host, 'notlocalhost' do
+ uid = uid.split('@').first # TEMPORARY HACK FOR NOW. in the future
+ # the database should be able to be searchable by full address
+ options = {:status => 200, :body => ""}.merge(opts)
+ query = "\?key=#{"%22#{uid}%22"}&reduce=false"
+ stub_http_request(:get, /#{Regexp.escape(Nickserver::Couch::FetchKey.couch_url)}.*#{query}/).to_return(options)
+ yield
+ end
end
end
diff --git a/test/unit/nickserver_test.rb b/test/unit/nickserver_test.rb
index 5753ec3..c746ef5 100644
--- a/test/unit/nickserver_test.rb
+++ b/test/unit/nickserver_test.rb
@@ -1,6 +1,21 @@
require File.expand_path('test_helper', File.dirname(__FILE__))
require 'json'
+#
+# Some important notes to understanding these tests:
+#
+# (1) Requests to localhost always bypass HTTP stub.
+#
+# (2) All requests to nickserver are to localhost.
+#
+# (3) the "Host" header for requests to nickserver must be set, because this
+# is how it decides if a request is local. I am not happy about this design,
+# but that is how it works for now.
+#
+# (4) When stubbing requests to couchdb, the couchdb host is changed from the
+# default (localhost) to a dummy value (notlocalhost).
+#
+
class NickserverTest < MiniTest::Unit::TestCase
def test_GET_served_via_SKS
@@ -34,15 +49,29 @@ class NickserverTest < MiniTest::Unit::TestCase
end
def test_GET_served_via_couch_not_found
- uid = 'bananas@localhost'
- key_id = 'E818C478D3141282F7590D29D041EB11B1647490'
- stub_couch_response(uid, :body => file_content(uid))
+ domain = "example.org"
+ uid = "bananas@" + domain
+ stub_couch_response(uid, :status => 404) do
+ start do
+ params = {:query => {"address" => uid}, :head => {:host => domain}}
+ get(params) do |http|
+ assert_equal 404, http.response_header.status
+ stop
+ end
+ end
+ end
+ end
- start do
- params = {:query => {"address" => uid}}
- get(params) do |http|
- assert_equal 404, http.response_header.status
- stop
+ def test_GET_served_via_couch_success
+ domain = "example.org"
+ uid = "blue@" + domain
+ stub_couch_response(uid, :body => file_content(:blue_couchdb_result)) do
+ start do
+ params = {:query => {"address" => uid}, :head => {:host => domain}}
+ get(params) do |http|
+ assert_equal file_content(:blue_nickserver_result), http.response
+ stop
+ end
end
end
end
@@ -61,7 +90,7 @@ class NickserverTest < MiniTest::Unit::TestCase
end
end
rescue Timeout::Error
- flunk 'Eventmachine was not stopped before the timeout expired'
+ flunk 'EventMachine was not stopped before the timeout expired'
end
#
@@ -84,14 +113,12 @@ class NickserverTest < MiniTest::Unit::TestCase
# this works because http requests to localhost are not stubbed, but requests to other domains are.
#
def request(method, params)
- http = EventMachine::HttpRequest.new("http://localhost:#{Nickserver::Config.port}/").send(method,params)
-
- http.callback {
+ EventMachine::HttpRequest.new("http://localhost:#{Nickserver::Config.port}/").send(method,params).callback {|http|
# p http.response_header.status
# p http.response_header
# p http.response
yield http
- }.errback {
+ }.errback {|http|
flunk(http.error) if http.error
EM.stop
}