clavem 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/doc/index.html CHANGED
@@ -6,7 +6,7 @@
6
6
  <title>
7
7
  File: README
8
8
 
9
- &mdash; Documentation by YARD 0.8.7
9
+ &mdash; Documentation by YARD 0.8.7.4
10
10
 
11
11
  </title>
12
12
 
@@ -17,7 +17,7 @@
17
17
  <script type="text/javascript" charset="utf-8">
18
18
  hasFrames = window.top.frames.main ? true : false;
19
19
  relpath = '';
20
- framesUrl = "frames.html#!" + escape(window.location.href);
20
+ framesUrl = "frames.html#!file.README.html";
21
21
  </script>
22
22
 
23
23
 
@@ -67,7 +67,9 @@
67
67
  <a href="https://gemnasium.com/ShogunPanda/clavem"><img src="https://gemnasium.com/ShogunPanda/clavem.png?travis" alt="Dependency Status" /></a>
68
68
  <a href="http://travis-ci.org/ShogunPanda/clavem"><img src="https://secure.travis-ci.org/ShogunPanda/clavem.png?branch=master" alt="Build Status" /></a>
69
69
  <a href="https://codeclimate.com/github/ShogunPanda/clavem"><img src="https://codeclimate.com/github/ShogunPanda/clavem.png" alt="Code Climate" /></a>
70
- <a href="https://coveralls.io/r/ShogunPanda/clavem"><img src="https://coveralls.io/repos/ShogunPanda/clavem/badge.png" alt="Coverage Status" /></a></p>
70
+ <a href="https://coveralls.io/r/ShogunPanda/clavem"><img src="https://coveralls.io/repos/ShogunPanda/clavem/badge.png" alt="Coverage Status" /></a>
71
+ <a href="https://bitdeli.com/free" title="Bitdeli Badge"><img src="https://d2weczhvl823v0.cloudfront.net/ShogunPanda/clavem/trend.png" alt="Bitdeli Trend" /></a>
72
+ &lt;iframe src="http://ghbtns.com/github-btn.html?user=ShogunPanda&amp;repo=clavem&amp;type=fork&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="135" height="20"&gt;&lt;/iframe&gt;</p>
71
73
 
72
74
  <p>A local callback server for oAuth web-flow.</p>
73
75
 
@@ -90,7 +92,7 @@ require “clavem”</p>
90
92
 
91
93
  <h1 id="get-the-token">Get the token</h1>
92
94
  <p># You can also handle callback parameter by yourself.
93
- # url += “?oauth_callback=#authorizerauthorizer.callback_url
95
+ # url += “?oauth_callback=” + authorizer.callback_url
94
96
  # authorizer.authorize(url, false)
95
97
  authorizer.authorize(url)</p>
96
98
 
@@ -133,9 +135,9 @@ end
133
135
  </div></div>
134
136
 
135
137
  <div id="footer">
136
- Generated on Wed Aug 21 16:17:53 2013 by
138
+ Generated on Sun Apr 6 15:36:21 2014 by
137
139
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
138
- 0.8.7 (ruby-2.0.0).
140
+ 0.8.7.4 (ruby-2.1.0).
139
141
  </div>
140
142
 
141
143
  </body>
data/doc/js/app.js CHANGED
@@ -78,7 +78,12 @@ function framesInit() {
78
78
  if (hasFrames) {
79
79
  document.body.className = 'frames';
80
80
  $('#menu .noframes a').attr('href', document.location);
81
- window.top.document.title = $('html head title').text();
81
+ try {
82
+ window.top.document.title = $('html head title').text();
83
+ } catch(error) {
84
+ // some browsers will not allow this when serving from file://
85
+ // but we don't want to stop the world.
86
+ }
82
87
  }
83
88
  else {
84
89
  $('#menu .noframes a').text('frames').attr('href', framesUrl);
@@ -211,4 +216,4 @@ $(linkSummaries);
211
216
  $(keyboardShortcuts);
212
217
  $(summaryToggle);
213
218
  $(fixOutsideWorldLinks);
214
- $(generateTOC);
219
+ $(generateTOC);
@@ -6,7 +6,7 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.8.7
9
+ &mdash; Documentation by YARD 0.8.7.4
10
10
 
11
11
  </title>
12
12
 
@@ -17,7 +17,7 @@
17
17
  <script type="text/javascript" charset="utf-8">
18
18
  hasFrames = window.top.frames.main ? true : false;
19
19
  relpath = '';
20
- framesUrl = "frames.html#!" + escape(window.location.href);
20
+ framesUrl = "frames.html#!top-level-namespace.html";
21
21
  </script>
22
22
 
23
23
 
@@ -103,9 +103,9 @@
103
103
  </div>
104
104
 
105
105
  <div id="footer">
106
- Generated on Wed Aug 21 16:17:53 2013 by
106
+ Generated on Sun Apr 6 15:36:21 2014 by
107
107
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
- 0.8.7 (ruby-2.0.0).
108
+ 0.8.7.4 (ruby-2.1.0).
109
109
  </div>
110
110
 
111
111
  </body>
data/lib/clavem.rb CHANGED
@@ -4,10 +4,8 @@
4
4
  # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
5
  #
6
6
 
7
- require "r18n-desktop"
8
7
  require "lazier"
9
- require "eventmachine"
10
- require "evma_httpserver"
8
+ require "addressable/uri"
11
9
 
12
10
  require "clavem/version" if !defined?(Clavem::Version)
13
11
  require "clavem/server"
@@ -28,7 +28,8 @@ module Clavem
28
28
  # @attribute timeout
29
29
  # @return [Fixnum] The amount of seconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
30
30
  # @attribute response_handler
31
- # @return [Proc] A Ruby block to handle response and check for success. The block must accept a querystring hash (which all values are arrays) and return a token or `nil` if the authentication was denied.
31
+ # @return [Proc] A Ruby block to handle response and check for success.
32
+ # The block must accept a querystring hash (which all values are arrays) and return a token or `nil` if the authentication was denied.
32
33
  # @attribute token
33
34
  # @return [String] The token obtained by the remote endpoint.
34
35
  # @attribute status
@@ -52,7 +53,8 @@ module Clavem
52
53
  # @param host [String] The host address on which listening for replies. Default is `localhost`.
53
54
  # @param port [Fixnum] The port on which listening for replies. Default is `7772`.
54
55
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
55
- # @param timeout [Fixnum] The amount of seconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
56
+ # @param timeout [Fixnum] The amount of seconds to wait for response from the remote endpoint before returning a failure.
57
+ # Default is `0`, which means *disabled*.
56
58
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#response_handler}.
57
59
  # @param force [Boolean] If to force recreation of the instance.
58
60
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
@@ -67,7 +69,8 @@ module Clavem
67
69
  # @param host [String] The host address on which listening for replies. Default is `localhost`.
68
70
  # @param port [Fixnum] The port on which listening for replies. Default is `7772`.
69
71
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
70
- # @param timeout [Fixnum] The amount of seconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
72
+ # @param timeout [Fixnum] The amount of seconds to wait for response from the remote endpoint before returning a failure.
73
+ # Default is `0`, which means *disabled*.
71
74
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#response_handler}.
72
75
  # @return [Authorizer] The new authorizer.
73
76
  def initialize(host = "localhost", port = 7772, command = nil, timeout = 0, &response_handler)
@@ -90,8 +93,10 @@ module Clavem
90
93
  # @param append_callback [Boolean] If to append the callback to the url using `oauth_callback` parameter.
91
94
  # @return [Boolean] `true` if authorization succeeded, `false` otherwise.
92
95
  def authorize(url, append_callback = true)
93
- url += "#{url.index("?") ? "&" : "?"}oauth_callback=#{callback_url}" if append_callback
94
- @url = url
96
+ url = Addressable::URI.parse(url)
97
+ url.query_values = (url.query_values || {}).merge({oauth_callback: callback_url}) if append_callback
98
+
99
+ @url = url.to_s
95
100
  @status = :waiting
96
101
  @token = nil
97
102
 
@@ -110,13 +115,13 @@ module Clavem
110
115
  #
111
116
  # @return [String] The callback_url for this authorizer.
112
117
  def callback_url
113
- "http://#{host}:#{port}/"
118
+ Addressable::URI.new(scheme: "http", host: host, port: port).to_s
114
119
  end
115
120
 
116
121
  # Returns the response handler for the authorizer.
117
122
  #
118
123
  def response_handler
119
- @response_handler || Proc.new {|querystring| (querystring || {}).fetch("oauth_token", []).ensure_array.first }
124
+ @response_handler || Proc.new {|querystring| (querystring || {})["oauth_token"] }
120
125
  end
121
126
 
122
127
  # Set the current locale for messages.
@@ -177,21 +182,15 @@ module Clavem
177
182
 
178
183
  # Processes the authentication response.
179
184
  def process_response
180
- # Signal handling
181
- ["INT", "TERM", "KILL"].each {|signal|
182
- Kernel.trap(signal){ EM.stop }
183
- }
184
-
185
- # Start eventmachine
186
- EM.run do
187
- EM.add_timer(@timeout){ handle_timeout } if @timeout > 0
188
- EM.start_server(@host, @port, Clavem::Server, self)
189
- end
190
- end
185
+ begin
186
+ server = Thread.new do
187
+ Clavem::Server.new(self)
188
+ end
191
189
 
192
- # Handle timeouts.
193
- def handle_timeout
194
- EM.stop
190
+ @timeout > 0 ? server.join(@timeout) : server.join
191
+ rescue Interrupt
192
+ raise Clavem::Exceptions::Failure.new(@i18n.errors.interrupted)
193
+ end
195
194
  end
196
195
  end
197
196
  end
data/lib/clavem/server.rb CHANGED
@@ -6,17 +6,17 @@
6
6
 
7
7
  module Clavem
8
8
  # A class to handle oAuth callbacks on the browser via HTTP.
9
- class Server < EM::Connection
10
- include EM::HttpServer
11
-
9
+ class Server
12
10
  # The template to send to the browser.
13
11
  TEMPLATE = <<-EOTEMPLATE
14
12
  <html>
15
13
  <head>
16
14
  <title>Clavem</title>
17
- <script type="text/javascript"> close_window = (function(){ window.open("", "_self", ""); window.close(); })(); </script>
15
+ <script type="text/javascript" charset="utf8">(function(){ window.open("", "_self", ""); window.close(); })();</script>
18
16
  </head>
19
- <body><h4>%s</h4></body>
17
+ <body>
18
+ %s
19
+ </body>
20
20
  </html>
21
21
  EOTEMPLATE
22
22
 
@@ -25,12 +25,25 @@ module Clavem
25
25
  # @param authorizer [Authorizer] The authorizer of this server.
26
26
  def initialize(authorizer)
27
27
  @authorizer = authorizer
28
+
29
+ process_http_request
28
30
  end
29
31
 
30
32
  # Save the token and sends a response back to the user.
31
33
  def process_http_request
34
+ server = create_server
35
+ socket = server.accept
36
+
37
+ # Get the request
38
+ request = socket.gets.gsub(/^[A-Z]+\s(.+)\sHTTP.+$/, "\\1")
39
+ querystring = Addressable::URI.parse(("%s%s" % [@authorizer.callback_url, request]).strip).query_values
40
+
41
+ # Send the response and close the socket
42
+ send_response(socket)
43
+
32
44
  # Handle the token
33
- token = @authorizer.response_handler.call(CGI::parse(@http_query_string))
45
+ token = @authorizer.response_handler.call(querystring)
46
+
34
47
  if token then
35
48
  @authorizer.token = token
36
49
  @authorizer.status = :succeeded
@@ -38,15 +51,28 @@ module Clavem
38
51
  @authorizer.status = :denied
39
52
  end
40
53
 
41
- # Build the request
42
- response = EM::DelegatedHttpResponse.new(self)
43
- response.status = 200
44
- response.content_type("text/html")
45
- response.content = TEMPLATE % [@authorizer.i18n.template]
46
- response.send_response
47
-
48
- # Stop after serving the request.
49
- EM.add_timer(0.1) { EM.stop }
54
+ server.close
50
55
  end
56
+
57
+ private
58
+ # Creates the server.
59
+ #
60
+ # @return [TCPServer] The TCP server
61
+ def create_server
62
+ server = TCPServer.new(@authorizer.host, @authorizer.port)
63
+ server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
64
+ server.setsockopt(:SOCKET, :REUSEADDR, 1)
65
+ server
66
+ end
67
+
68
+ # Sends the response to the client.
69
+ #
70
+ # @param socket [TCPSocket] The socket to the client.
71
+ def send_response(socket)
72
+ response = TEMPLATE % [@authorizer.i18n.template]
73
+ socket.print(["HTTP/1.1 200 OK", "Content-Type: text/html; charset=utf-8", "Content-Length: #{response.bytesize}", "Connection: close"].join("\r\n"))
74
+ socket.print("\r\n\r\n" + response)
75
+ socket.close
76
+ end
51
77
  end
52
78
  end
@@ -13,7 +13,7 @@ module Clavem
13
13
  MAJOR = 2
14
14
 
15
15
  # The minor version.
16
- MINOR = 0
16
+ MINOR = 1
17
17
 
18
18
  # The patch version.
19
19
  PATCH = 0
data/locales/en.yml CHANGED
@@ -9,12 +9,14 @@
9
9
  errors:
10
10
  response_failure: "Cannot handle response: %1"
11
11
  open_failure: "Cannot open URL %1: %2"
12
+ interrupted: "Authentication interrupted by the user."
12
13
  template: "This window will close in few moments. If this doesn't happens you can close it by yourself, this won't cause any problem."
13
14
  application_description: "A local callback server for oAuth web-flow."
14
15
  application_help_url: "The URL where to send the user to start authorization."
15
16
  application_help_host: "The host address on which listening for the reply. Default is localhost."
16
17
  application_help_port: "The port on which listening for the reply. Default is 777`."
17
18
  application_help_command: "The command to open the URL. {{URL} is replaced with the specified URL. Default is 'open \"{{URL}}\"'."
19
+ application_help_no_callback: "Do not append the \"oauth_callback\" parameter to the URL."
18
20
  application_help_timeout: "The amount of seconds to wait for response from the remote endpoint before returning a failure."
19
21
  application_help_quiet: "Do not print anything but errors."
20
22
  application_meta_port: "PORT"
@@ -22,4 +24,4 @@
22
24
  cli_running: "Clavem is waiting for a response at URL {mark=bright}%1{/mark} ..."
23
25
  cli_obtaining: "Obtaining token from URL {mark=bright}%1{/mark}."
24
26
  cli_succeeded: "Obtained access token is: {mark=bright}%1{/mark}"
25
- cli_failed: "No access token obtained."
27
+ cli_failed: "No access token obtained."
data/locales/it.yml CHANGED
@@ -9,12 +9,14 @@
9
9
  errors:
10
10
  response_failure: "Impossibile gestire una risposta: %1"
11
11
  open_failure: "Impossible aprire l'URL %1: %2"
12
+ interrupted: "Autenticazione interrotta dall'utente."
12
13
  template: "Questa finestra si chiuderà in pochi momenti. Se non succede puoi chiuderla da solo, ciò non causerà alcun problema."
13
14
  application_description: "Un server callback locale per il web-flow oAuth."
14
15
  application_help_url: "L'URL dove mandare l'utente per iniziare l'autenticazione."
15
16
  application_help_host: "L'indirizzo host dove rimanere in ascolto per la risposta. Il default è localhost."
16
17
  application_help_port: "La porta dove rimanere in ascolto per la risposta. La porta di default è 7772."
17
18
  application_help_command: "Il comando per aprire l'URL. {{URL}} è sostituito con l'URL specificato. Il default è 'open \"{{URL}}\"'."
19
+ application_help_no_callback: "Non aggiungere il parametro \"oauth_callback\" all'URL."
18
20
  application_help_timeout: "Il numero di secondi da attendere per la risposta dall'endpoint remoto prima di ritornare un fallimento."
19
21
  application_help_quiet: "Non mostrare nulla tranne che gli errori."
20
22
  application_meta_port: "PORTA"
@@ -78,17 +78,17 @@ describe Clavem::Authorizer do
78
78
 
79
79
  expect(Kernel).to receive(:system).with("open \"URL?oauth_callback=CALLBACK\"")
80
80
  subject.authorize("URL")
81
- expect(Kernel).to receive(:system).with("open \"URL?&oauth_callback=CALLBACK\"")
82
- subject.authorize("URL?")
81
+ expect(Kernel).to receive(:system).with("open \"URL?a=b&oauth_callback=CALLBACK\"")
82
+ subject.authorize("URL?a=b")
83
83
  subject.command = "browse {{URL}}"
84
- expect(Kernel).to receive(:system).with("browse URL?oauth_callback=CALLBACK")
85
- subject.authorize("URL")
84
+ expect(Kernel).to receive(:system).with("browse URL")
85
+ subject.authorize("URL", false)
86
86
  end
87
87
 
88
88
  it "should start a server" do
89
89
  allow(subject).to receive(:perform_request)
90
90
  subject.timeout = 1
91
- expect(EM).to receive(:start_server).with(subject.host, subject.port, Clavem::Server, subject)
91
+ expect(Clavem::Server).to receive(:new).with(subject)
92
92
  subject.authorize("URL")
93
93
  end
94
94
 
@@ -109,18 +109,25 @@ describe Clavem::Authorizer do
109
109
  end
110
110
 
111
111
  it "should handle timeouts" do
112
+ allow(Kernel).to receive(:system)
112
113
  allow(subject).to receive(:perform_request)
113
114
 
114
115
  subject.timeout = 1
115
- expect(EM).to receive(:stop).and_call_original
116
116
  subject.authorize("URL")
117
117
  end
118
+
119
+ it "should handle interruptions" do
120
+ allow(Kernel).to receive(:system)
121
+ allow(Clavem::Server).to receive(:new).and_raise(Interrupt)
122
+ expect { subject.authorize("URL") }.to raise_error(Clavem::Exceptions::Failure)
123
+ expect(subject.failed?).to be_true
124
+ end
118
125
  end
119
126
 
120
127
  describe "#callback_url" do
121
128
  it "should return the correct callback" do
122
- expect(::Clavem::Authorizer.new.callback_url).to eq("http://localhost:7772/")
123
- expect(::Clavem::Authorizer.new("10.0.0.1", "80").callback_url).to eq("http://10.0.0.1:80/")
129
+ expect(::Clavem::Authorizer.new.callback_url).to eq("http://localhost:7772")
130
+ expect(::Clavem::Authorizer.new("10.0.0.1", "80").callback_url).to eq("http://10.0.0.1:80")
124
131
  end
125
132
  end
126
133
 
@@ -128,7 +135,6 @@ describe Clavem::Authorizer do
128
135
  it "should return the token as default implementation" do
129
136
  expect(subject.response_handler.call(nil)).to be_nil
130
137
  expect(subject.response_handler.call({"oauth_token" => "TOKEN"})).to eq("TOKEN")
131
- expect(subject.response_handler.call({"oauth_token" => ["TOKEN 1", "TOKEN 2"]})).to eq("TOKEN 1")
132
138
  end
133
139
 
134
140
  it "should work as a getter" do
@@ -9,30 +9,39 @@ require "spec_helper"
9
9
  describe Clavem::Server do
10
10
  let(:authorizer) { Clavem::Authorizer.new }
11
11
 
12
- around(:each) do |example|
13
- EM.run do
14
- EM.start_server("localhost", 7772, Clavem::Server, authorizer)
15
- EM.add_timer(0.1) do
16
- @request = EventMachine::HttpRequest.new("http://localhost:7772/?#{example.metadata[:query]}").get
17
- @request.callback { example.call }
18
- end
19
- end
20
- end
21
-
22
12
  describe "#initialize", query: "oauth_token=TOKEN" do
23
13
  it "should save the authorizer" do
24
- server = Clavem::Server.new("UNUSED", authorizer)
14
+ allow_any_instance_of(Clavem::Server).to receive(:process_http_request)
15
+ server = Clavem::Server.new(authorizer)
25
16
  expect(server.instance_variable_get(:@authorizer)).to be(authorizer)
26
17
  end
27
18
  end
28
19
 
29
20
  describe "#process_http_request" do
21
+ before(:each) do
22
+ allow(Kernel).to receive(:system)
23
+
24
+ socket = double(TCPSocket)
25
+ expect(socket).to receive(:gets).and_return("GET /?#{example.metadata[:query]} HTTP/1.1")
26
+ allow(socket).to receive(:print)
27
+ allow(socket).to receive(:close)
28
+
29
+ server = double(TCPServer)
30
+ expect(server).to receive(:accept).and_return(socket)
31
+ allow(server).to receive(:close)
32
+
33
+ allow_any_instance_of(Clavem::Server).to receive(:create_server).and_return(server)
34
+ end
35
+
30
36
  it "should save the token and report success", query: "oauth_token=TOKEN" do
37
+ authorizer.authorize("URL")
31
38
  expect(authorizer.succeeded?).to be_true
32
39
  expect(authorizer.token).to eq("TOKEN")
33
40
  end
34
41
 
35
- it "should save the token and report failure", query: "notoken=TOKEN" do
42
+ it "should report failure", query: "notoken=TOKEN" do
43
+ authorizer.authorize("URL")
44
+
36
45
  expect(authorizer.denied?).to be_true
37
46
  expect(authorizer.token).to eq(nil)
38
47
  end
data/spec/spec_helper.rb CHANGED
@@ -7,7 +7,6 @@
7
7
  require "rubygems"
8
8
  require "bundler/setup"
9
9
  require "clavem"
10
- require "em-http-request"
11
10
 
12
11
  RSpec.configure do |config|
13
12
  config.expect_with :rspec do |c|