clavem 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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|