Class: Clavem::Authorizer

Inherits:
Object
  • Object
show all
Includes:
R18n::Helpers
Defined in:
lib/clavem/authorizer.rb

Overview

A class to handle oAuth authorizations.

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Authorizer) initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)

Creates a new authorizer.

Parameters:

  • ip (String) (defaults to: "127.0.0.1")

    The IP address on which listening for replies. Default is 127.0.0.1.

  • port (Fixnum) (defaults to: 2501)

    The port on which listening for replies. Default is 2501.

  • command (String|nil) (defaults to: nil)

    The command to open the URL. {{URL}} is replaced with the specified URL. Default is open "{{URL}}".

  • title (String|nil) (defaults to: nil)

    The title for response template. Default is Clavem Authorization

  • template (String|nil) (defaults to: nil)

    Alternative template to show progress in user’s browser.

  • timeout (Fixnum) (defaults to: 0)

    The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is 0, which means disabled.

  • response_handler (Proc)

    A Ruby block to handle response and check for success. See #default_response_handler.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/clavem/authorizer.rb', line 91

def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
  @i18n = self.localize

  @ip = ip.ensure_string
  @port = port.to_integer
  @command = command.ensure_string
  @title = title.ensure_string
  @template = template.ensure_string
  @timeout = timeout.to_integer
  @response_handler = response_handler

  sanitize_arguments

  @token = nil
  @status = :waiting
  @compiled_template ||= ::ERB.new(@template)
  @timeout_expired = false
  @timeout_thread = nil

  self
end

Instance Attribute Details

- (String) command

The command to open the URL. {{URL}} is replaced with the specified URL. Default is open "{{URL}}".

Returns:

  • (String)

    The command to open the URL. {{URL}} is replaced with the specified URL. Default is open "{{URL}}".



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (Object) i18n

Returns the value of attribute i18n



62
63
64
# File 'lib/clavem/authorizer.rb', line 62

def i18n
  @i18n
end

- (String) ip

The IP address on which listening for replies. Default is 127.0.0.1.

Returns:

  • (String)

    The IP address on which listening for replies. Default is 127.0.0.1.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (R18N::Translation) localizer

A localizer object.

Returns:

  • (R18N::Translation)

    A localizer object.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (Fixnum) port

The port on which listening for replies. Default is 2501.

Returns:

  • (Fixnum)

    The port on which listening for replies. Default is 2501.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (Proc) response_handler

A Ruby block to handle response and check for success. @see #default_response_handler.

Returns:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (Object) status

Returns the value of attribute status



61
62
63
# File 'lib/clavem/authorizer.rb', line 61

def status
  @status
end

- (String) template

Alternative template to show progress in user’s browser.

Returns:

  • (String)

    Alternative template to show progress in user’s browser.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (Fixnum) timeout

The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is 0, which means disabled.

Returns:

  • (Fixnum)

    The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is 0, which means disabled.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (String) title

The title for response template. Default is Clavem Authorization.

Returns:

  • (String)

    The title for response template. Default is Clavem Authorization.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (Symbol) token

The status of the request. Can be :success, :denied, :failure and :waiting.

Returns:

  • (Symbol)

    The status of the request. Can be :success, :denied, :failure and :waiting.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

- (String) url

The URL where to send the user to start authorization..

Returns:

  • (String)

    The URL where to send the user to start authorization..



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/clavem/authorizer.rb', line 50

class Authorizer
  include R18n::Helpers
  attr_accessor :url
  attr_accessor :ip
  attr_accessor :port
  attr_accessor :command
  attr_accessor :title
  attr_accessor :template
  attr_accessor :timeout
  attr_accessor :response_handler
  attr_accessor :token
  attr_accessor :status
  attr_accessor :i18n

  # Returns a unique (singleton) instance of the authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Authorizer] The unique (singleton) instance of the authorizer.
  def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
    @instance = nil if force
    @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
    @instance
  end

  # Creates a new authorizer.
  #
  # @param ip [String] The IP address on which listening for replies. Default is `127.0.0.1`.
  # @param port [Fixnum] The port on which listening for replies. Default is `2501`.
  # @param command [String|nil] The command to open the URL. `{{URL}}` is replaced with the specified URL. Default is `open "{{URL}}"`.
  # @param title [String|nil] The title for response template. Default is `Clavem Authorization`
  # @param template [String|nil] Alternative template to show progress in user's browser.
  # @param timeout [Fixnum] The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is `0`, which means *disabled*.
  # @param response_handler [Proc] A Ruby block to handle response and check for success. See {#default_response_handler}.
  # @return [Authorizer] The new authorizer.
  def initialize(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, &response_handler)
    @i18n = self.localize

    @ip = ip.ensure_string
    @port = port.to_integer
    @command = command.ensure_string
    @title = title.ensure_string
    @template = template.ensure_string
    @timeout = timeout.to_integer
    @response_handler = response_handler

    sanitize_arguments

    @token = nil
    @status = :waiting
    @compiled_template ||= ::ERB.new(@template)
    @timeout_expired = false
    @timeout_thread = nil

    self
  end

  # Starts the authorization flow.
  #
  # @param url [String] The URL where to send the user to start authorization.
  # @return [Authorizer] The authorizer.
  def authorize(url)
    @url = url
    @status = :waiting

    begin
      # Setup stuff
      setup_webserver
      setup_interruptions_handling
      setup_timeout_handling

      # Open the endpoint then start the server
      open_endpoint
      @server.start

      raise Clavem::Exceptions::Timeout.new if @timeout_expired
    rescue Clavem::Exceptions::Timeout => t
      @status = :failure
      raise t
    rescue => e
      @status = :failure
      raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
    ensure
      cleanup
    end

    raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
    self
  end

  # Returns the callback_url for this authorizer.
  #
  # @return [String] The callback_url for this authorizer.
  def callback_url
    "http://#{ip}:#{port}/"
  end

  # Handles a response from the remote endpoint.
  #
  # @param [Authorizer] authorizer The current authorizer.
  # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
  # @param [WEBrick::HTTPResponse] response The request to send to the browser.
  # @return [String|nil] The oAuth access token. Returning `nil` means *authorization denied*.
  def default_response_handler(authorizer, request, response)
    request.query['oauth_token'].ensure_string
  end

  # Set the current locale for messages.
  #
  # @param locale [String] The new locale. Default is the current system locale.
  # @return [R18n::Translation] The new translations object.
  def localize(locale = nil)
    @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
    R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
  end

  private
    # sanitize_arguments
    def sanitize_arguments
      @ip = "127.0.0.1" if @ip.blank?
      @port = 2501 if @port.to_integer < 1
      @command = "open \"{{URL}}\"" if @command.blank?
      @title = @i18n.default_title if @title.blank?
      @template = File.read(File.dirname(__FILE__) + "/template.html.erb") if @template.blank?
      @timeout = 0 if @timeout < 0
    end

    # Open the remote endpoint
    def open_endpoint
      # Open the oAuth endpoint into the browser
      begin
        Kernel.system(@command.gsub("{{URL}}", @url.ensure_string))
      rescue => e
        raise Clavem::Exceptions::Failure.new(@i18n.errors.open_failure(@url.ensure_string, e.to_s))
      end
    end

    # Handle interruptions for the process.
    def setup_interruptions_handling
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal){ @server.shutdown if @server } }
    end

    # Handle timeout for the response.
    def setup_timeout_handling
      if @timeout > 0 then
        @timeout_thread = Thread.new do
          Kernel.sleep(@timeout.to_f / 1000)
          @timeout_expired = true
          Process.kill("USR2", 0)
        end
      end
    end

    # Prepare the webserver for handling the response.
    def setup_webserver
      @server = ::WEBrick::HTTPServer.new(:BindAddress => @ip, :Port => @port, :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil])
      @server.mount_proc("/"){ |request, response| dispatch_request(request, response) }
    end

    # Handles a response from the remote endpoint.
    #
    # @param [WEBrick::HTTPRequest] request The request that the remote endpoint made to notify authorization status.
    # @param [WEBrick::HTTPResponse] response The request to send to the browser.
    def dispatch_request(request, response)
      @token = @response_handler ? @response_handler.call(self, request, response) : self.default_response_handler(self, request, response)

      if @status == :waiting then
        if @token.present? then
          @status = :success
          response.status = 200
        else
          @status = :denied
          response.status = 403
        end

        response.body = @compiled_template.result(binding)
        @server.shutdown
      end
    end

    # Cleans up resources
    def cleanup
      @timeout_thread.exit if @timeout_thread
      @server.shutdown if @server
      ["USR2", "INT", "TERM", "KILL"].each {|signal| Kernel.trap(signal, "DEFAULT") }
    end
end

Class Method Details

+ (Authorizer) instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)

Returns a unique (singleton) instance of the authorizer.

Parameters:

  • ip (String) (defaults to: "127.0.0.1")

    The IP address on which listening for replies. Default is 127.0.0.1.

  • port (Fixnum) (defaults to: 2501)

    The port on which listening for replies. Default is 2501.

  • command (String|nil) (defaults to: nil)

    The command to open the URL. {{URL}} is replaced with the specified URL. Default is open "{{URL}}".

  • title (String|nil) (defaults to: nil)

    The title for response template. Default is Clavem Authorization

  • template (String|nil) (defaults to: nil)

    Alternative template to show progress in user’s browser.

  • timeout (Fixnum) (defaults to: 0)

    The amount of milliseconds to wait for response from the remote endpoint before returning a failure. Default is 0, which means disabled.

  • response_handler (Proc)

    A Ruby block to handle response and check for success. See #default_response_handler.

  • force (Boolean) (defaults to: false)

    If to force recreation of the instance.

Returns:

  • (Authorizer)

    The unique (singleton) instance of the authorizer.



75
76
77
78
79
# File 'lib/clavem/authorizer.rb', line 75

def self.instance(ip = "127.0.0.1", port = 2501, command = nil, title = nil, template = nil, timeout = 0, force = false, &response_handler)
  @instance = nil if force
  @instance ||= Clavem::Authorizer.new(ip, port, command, title, template, timeout, &response_handler)
  @instance
end

Instance Method Details

- (Authorizer) authorize(url)

Starts the authorization flow.

Parameters:

  • url (String)

    The URL where to send the user to start authorization.

Returns:

Raises:



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/clavem/authorizer.rb', line 117

def authorize(url)
  @url = url
  @status = :waiting

  begin
    # Setup stuff
    setup_webserver
    setup_interruptions_handling
    setup_timeout_handling

    # Open the endpoint then start the server
    open_endpoint
    @server.start

    raise Clavem::Exceptions::Timeout.new if @timeout_expired
  rescue Clavem::Exceptions::Timeout => t
    @status = :failure
    raise t
  rescue => e
    @status = :failure
    raise Clavem::Exceptions::Failure.new(@i18n.errors.response_failure(e.to_s))
  ensure
    cleanup
  end

  raise Clavem::Exceptions::AuthorizationDenied.new if @status == :denied
  self
end

- (String) callback_url

Returns the callback_url for this authorizer.

Returns:

  • (String)

    The callback_url for this authorizer.



149
150
151
# File 'lib/clavem/authorizer.rb', line 149

def callback_url
  "http://#{ip}:#{port}/"
end

- (String|nil) default_response_handler(authorizer, request, response)

Handles a response from the remote endpoint.

Parameters:

  • authorizer (Authorizer)

    The current authorizer.

  • request (WEBrick::HTTPRequest)

    The request that the remote endpoint made to notify authorization status.

  • response (WEBrick::HTTPResponse)

    The request to send to the browser.

Returns:

  • (String|nil)

    The oAuth access token. Returning nil means authorization denied.



159
160
161
# File 'lib/clavem/authorizer.rb', line 159

def default_response_handler(authorizer, request, response)
  request.query['oauth_token'].ensure_string
end

- (R18n::Translation) localize(locale = nil)

Set the current locale for messages.

Parameters:

  • locale (String) (defaults to: nil)

    The new locale. Default is the current system locale.

Returns:

  • (R18n::Translation)

    The new translations object.



167
168
169
170
# File 'lib/clavem/authorizer.rb', line 167

def localize(locale = nil)
  @i18n_locales_path ||= ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")
  R18n::I18n.new([locale || :en, ENV["LANG"], R18n::I18n.system_locale].compact, @i18n_locales_path).t.clavem
end