analysand 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -1
- data/Rakefile +14 -0
- data/lib/analysand/change_watcher.rb +1 -1
- data/lib/analysand/config_response.rb +24 -0
- data/lib/analysand/database.rb +13 -74
- data/lib/analysand/errors.rb +16 -0
- data/lib/analysand/http.rb +80 -0
- data/lib/analysand/instance.rb +128 -83
- data/lib/analysand/response.rb +8 -18
- data/lib/analysand/response_headers.rb +18 -0
- data/lib/analysand/session_response.rb +16 -0
- data/lib/analysand/status_code_predicates.rb +17 -0
- data/lib/analysand/streaming_view_response.rb +6 -16
- data/lib/analysand/version.rb +1 -1
- data/lib/analysand/viewing.rb +4 -4
- data/spec/analysand/a_response.rb +45 -1
- data/spec/analysand/a_session_grantor.rb +1 -1
- data/spec/analysand/database_spec.rb +14 -0
- data/spec/analysand/instance_spec.rb +104 -37
- data/spec/analysand/view_streaming_spec.rb +1 -1
- data/spec/fixtures/vcr_cassettes/get_config.yml +40 -0
- data/spec/fixtures/vcr_cassettes/get_many_config.yml +40 -0
- data/spec/fixtures/vcr_cassettes/reload_config.yml +114 -0
- data/spec/fixtures/vcr_cassettes/set_config.yml +77 -0
- data/spec/fixtures/vcr_cassettes/unauthorized_set_config.yml +43 -0
- data/spec/fixtures/vcr_cassettes/view.yml +1 -1
- data/spec/smoke/database_thread_spec.rb +59 -0
- metadata +215 -123
- data/spec/fixtures/vcr_cassettes/get_session_does_not_refresh_cookie.yml +0 -73
- data/spec/fixtures/vcr_cassettes/get_session_refreshes_cookie.yml +0 -75
data/CHANGELOG
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
Issue numbers refer to issues on Analysand's Github tracker:
|
2
2
|
https://github.com/yipdw/analysand/issues
|
3
3
|
|
4
|
+
2.0.0 (2012-11-29)
|
5
|
+
------------------
|
6
|
+
* Instance#establish_session and Instance#renew_session now return a (session,
|
7
|
+
Analysand::Response pair)
|
8
|
+
* Share HTTP code between Database and Instance
|
9
|
+
* Session handling on Instance rewritten: #post_session, #get_session
|
10
|
+
* New response methods: #cookies, #session_cookie
|
11
|
+
|
4
12
|
1.1.0 (2012-11-03)
|
5
13
|
------------------
|
6
14
|
|
@@ -9,7 +17,6 @@ https://github.com/yipdw/analysand/issues
|
|
9
17
|
* Some code organization cleanups
|
10
18
|
* require "analysand" now loads the Database and Instance classes
|
11
19
|
|
12
|
-
|
13
20
|
1.0.1 (2012-10-01)
|
14
21
|
------------------
|
15
22
|
|
data/Rakefile
CHANGED
@@ -5,4 +5,18 @@ require 'rspec/core/rake_task'
|
|
5
5
|
|
6
6
|
RSpec::Core::RakeTask.new
|
7
7
|
|
8
|
+
namespace :git do
|
9
|
+
desc 'Strip trailing whitespace from tracked source files'
|
10
|
+
task :strip_spaces do
|
11
|
+
`git ls-files`.split("\n").each do |file|
|
12
|
+
puts file
|
13
|
+
|
14
|
+
if `file '#{file}'` =~ /text/
|
15
|
+
sh "git stripspace < '#{file}' > '#{file}.out'"
|
16
|
+
mv "#{file}.out", file
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
8
22
|
task :default => :spec
|
@@ -81,7 +81,7 @@ module Analysand
|
|
81
81
|
def initialize(database)
|
82
82
|
@db = database
|
83
83
|
@waiting = {}
|
84
|
-
@http_parser = Http::Parser.new(self)
|
84
|
+
@http_parser = ::Http::Parser.new(self)
|
85
85
|
@json_parser = Yajl::Parser.new
|
86
86
|
@json_parser.on_parse_complete = lambda { |doc| process(doc) }
|
87
87
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'analysand/response'
|
2
|
+
|
3
|
+
module Analysand
|
4
|
+
# Public: Wraps responses from /_config.
|
5
|
+
#
|
6
|
+
# Not all responses from sub-resources of _config return valid JSON objects,
|
7
|
+
# but JSON.parse expects to see a full JSON object. This object implements a
|
8
|
+
# bit of a hacky workaround if the body does not start with a '{' and end
|
9
|
+
# with a '}', then it is not run through the JSON parser.
|
10
|
+
class ConfigResponse < Response
|
11
|
+
alias_method :value, :body
|
12
|
+
|
13
|
+
def initialize(response)
|
14
|
+
body = response.body.chomp
|
15
|
+
|
16
|
+
if body.start_with?('{') && body.end_with?('}')
|
17
|
+
super
|
18
|
+
else
|
19
|
+
@response = response
|
20
|
+
@body = body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/analysand/database.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'analysand/errors'
|
2
|
+
require 'analysand/http'
|
2
3
|
require 'analysand/reading'
|
3
4
|
require 'analysand/response'
|
4
5
|
require 'analysand/viewing'
|
5
6
|
require 'analysand/writing'
|
6
|
-
require 'net/http/persistent'
|
7
|
-
require 'rack/utils'
|
8
|
-
require 'uri'
|
9
7
|
|
10
8
|
module Analysand
|
11
9
|
##
|
@@ -249,7 +247,13 @@ module Analysand
|
|
249
247
|
# as a cookie from CouchDB's Session API. The string is used as the
|
250
248
|
# value of a Cookie header.
|
251
249
|
#
|
252
|
-
#
|
250
|
+
# There are two ways to retrieve a token:
|
251
|
+
#
|
252
|
+
# 1. Establishing a session. You can use
|
253
|
+
# Analysand::Instance#establish_sesssion for this.
|
254
|
+
# 2. CouchDB may also issue updated session cookies as part of a response.
|
255
|
+
# You can access such cookies using #session_cookie on the response
|
256
|
+
# object.
|
253
257
|
#
|
254
258
|
# Omitting the credentials argument, or providing a form of credentials not
|
255
259
|
# listed here, will result in no credentials being passed in the request.
|
@@ -263,13 +267,15 @@ module Analysand
|
|
263
267
|
# connection per (uri.host, uri.port, thread) tuple, so connection pooling
|
264
268
|
# is also done.
|
265
269
|
class Database
|
266
|
-
include
|
270
|
+
include Errors
|
271
|
+
include Http
|
267
272
|
include Reading
|
268
273
|
include Viewing
|
269
274
|
include Writing
|
270
275
|
|
271
|
-
|
272
|
-
|
276
|
+
def initialize(uri)
|
277
|
+
init_http_client(uri)
|
278
|
+
end
|
273
279
|
|
274
280
|
def self.create!(uri, credentials = nil)
|
275
281
|
new(uri).tap { |db| db.create!(credentials) }
|
@@ -279,19 +285,6 @@ module Analysand
|
|
279
285
|
new(uri).drop(credentials)
|
280
286
|
end
|
281
287
|
|
282
|
-
def initialize(uri)
|
283
|
-
raise InvalidURIError, 'You must supply an absolute URI' unless uri.absolute?
|
284
|
-
|
285
|
-
@http = Net::HTTP::Persistent.new('analysand_database')
|
286
|
-
@uri = uri
|
287
|
-
|
288
|
-
# Document IDs and other database bits are appended to the URI path,
|
289
|
-
# so we need to make sure that it ends in a /.
|
290
|
-
unless uri.path.end_with?('/')
|
291
|
-
uri.path += '/'
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
288
|
def ping(credentials = nil)
|
296
289
|
Response.new _get('', credentials)
|
297
290
|
end
|
@@ -300,10 +293,6 @@ module Analysand
|
|
300
293
|
ping(credentials).body
|
301
294
|
end
|
302
295
|
|
303
|
-
def close
|
304
|
-
http.shutdown
|
305
|
-
end
|
306
|
-
|
307
296
|
def create(credentials = nil)
|
308
297
|
Response.new _put('', credentials)
|
309
298
|
end
|
@@ -324,59 +313,9 @@ module Analysand
|
|
324
313
|
end
|
325
314
|
end
|
326
315
|
|
327
|
-
%w(Head Get Put Post Delete Copy).each do |m|
|
328
|
-
str = <<-END
|
329
|
-
def _#{m.downcase}(doc_id, credentials, query = {}, headers = {}, body = nil, block = nil)
|
330
|
-
_req(Net::HTTP::#{m}, doc_id, credentials, query, headers, body, block)
|
331
|
-
end
|
332
|
-
END
|
333
|
-
|
334
|
-
class_eval str, __FILE__, __LINE__
|
335
|
-
end
|
336
|
-
|
337
|
-
##
|
338
|
-
# @private
|
339
|
-
def _req(klass, doc_id, credentials, query, headers, body, block)
|
340
|
-
uri = self.uri.dup
|
341
|
-
uri.path += URI.escape(doc_id)
|
342
|
-
uri.query = build_query(query) unless query.empty?
|
343
|
-
|
344
|
-
req = klass.new(uri.request_uri)
|
345
|
-
|
346
|
-
headers.each { |k, v| req.add_field(k, v) }
|
347
|
-
req.body = body if body && req.request_body_permitted?
|
348
|
-
set_credentials(req, credentials)
|
349
|
-
|
350
|
-
http.request(uri, req, &block)
|
351
|
-
end
|
352
|
-
|
353
|
-
##
|
354
|
-
# Sets credentials on a request object.
|
355
|
-
#
|
356
|
-
# If creds is a hash containing :username and :password keys, HTTP basic
|
357
|
-
# authorization is used. If creds is a string, the string is added as a
|
358
|
-
# cookie.
|
359
|
-
def set_credentials(req, creds)
|
360
|
-
return unless creds
|
361
|
-
|
362
|
-
if String === creds
|
363
|
-
req.add_field('Cookie', creds)
|
364
|
-
elsif creds[:username] && creds[:password]
|
365
|
-
req.basic_auth(creds[:username], creds[:password])
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
316
|
def json_headers
|
370
317
|
{ 'Content-Type' => 'application/json' }
|
371
318
|
end
|
372
|
-
|
373
|
-
##
|
374
|
-
# @private
|
375
|
-
def ex(klass, response)
|
376
|
-
klass.new("Expected response to have code 2xx, got #{response.code} instead").tap do |ex|
|
377
|
-
ex.response = response
|
378
|
-
end
|
379
|
-
end
|
380
319
|
end
|
381
320
|
end
|
382
321
|
|
data/lib/analysand/errors.rb
CHANGED
@@ -1,4 +1,17 @@
|
|
1
1
|
module Analysand
|
2
|
+
# Private: Methods to generate exceptions.
|
3
|
+
module Errors
|
4
|
+
# Instantiates an exception and fills in a response.
|
5
|
+
#
|
6
|
+
# klass - the exception class
|
7
|
+
# response - the response object that caused the error
|
8
|
+
def ex(klass, response)
|
9
|
+
klass.new("Expected response to have code 2xx, got #{response.code} instead").tap do |ex|
|
10
|
+
ex.response = response
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
2
15
|
class InvalidURIError < StandardError
|
3
16
|
end
|
4
17
|
|
@@ -6,6 +19,9 @@ module Analysand
|
|
6
19
|
attr_accessor :response
|
7
20
|
end
|
8
21
|
|
22
|
+
class ConfigurationNotSaved < DatabaseError
|
23
|
+
end
|
24
|
+
|
9
25
|
class DocumentNotSaved < DatabaseError
|
10
26
|
end
|
11
27
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'net/http/persistent'
|
2
|
+
require 'rack/utils'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Analysand
|
6
|
+
# Private: HTTP client methods for Database and Instance.
|
7
|
+
#
|
8
|
+
# Users of this module MUST set @http and @uri in their initializer. @http
|
9
|
+
# SHOULD be a Net::HTTP::Persistent instance, and @uri SHOULD be a URI
|
10
|
+
# instance.
|
11
|
+
module Http
|
12
|
+
include Rack::Utils
|
13
|
+
|
14
|
+
attr_reader :http
|
15
|
+
attr_reader :uri
|
16
|
+
|
17
|
+
def init_http_client(uri)
|
18
|
+
unless uri.respond_to?(:path) && uri.respond_to?(:absolute?)
|
19
|
+
uri = URI(uri)
|
20
|
+
end
|
21
|
+
|
22
|
+
raise InvalidURIError, 'You must supply an absolute URI' unless uri.absolute?
|
23
|
+
|
24
|
+
@http = Net::HTTP::Persistent.new('analysand')
|
25
|
+
@uri = uri
|
26
|
+
|
27
|
+
# Document IDs and other database bits are appended to the URI path,
|
28
|
+
# so we need to make sure that it ends in a /.
|
29
|
+
unless uri.path.end_with?('/')
|
30
|
+
uri.path += '/'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
http.shutdown
|
36
|
+
end
|
37
|
+
|
38
|
+
%w(Head Get Put Post Delete Copy).each do |m|
|
39
|
+
str = <<-END
|
40
|
+
def _#{m.downcase}(doc_id, credentials, query = {}, headers = {}, body = nil, block = nil)
|
41
|
+
_req(Net::HTTP::#{m}, doc_id, credentials, query, headers, body, block)
|
42
|
+
end
|
43
|
+
END
|
44
|
+
|
45
|
+
module_eval str, __FILE__, __LINE__
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# @private
|
50
|
+
def _req(klass, doc_id, credentials, query, headers, body, block)
|
51
|
+
uri = self.uri.dup
|
52
|
+
uri.path += URI.escape(doc_id)
|
53
|
+
uri.query = build_query(query) unless query.empty?
|
54
|
+
|
55
|
+
req = klass.new(uri.request_uri)
|
56
|
+
|
57
|
+
headers.each { |k, v| req.add_field(k, v) }
|
58
|
+
req.body = body if body && req.request_body_permitted?
|
59
|
+
set_credentials(req, credentials)
|
60
|
+
|
61
|
+
http.request(uri, req, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Sets credentials on a request object.
|
66
|
+
#
|
67
|
+
# If creds is a hash containing :username and :password keys, HTTP basic
|
68
|
+
# authorization is used. If creds is a string, the string is added as a
|
69
|
+
# cookie.
|
70
|
+
def set_credentials(req, creds)
|
71
|
+
return unless creds
|
72
|
+
|
73
|
+
if String === creds
|
74
|
+
req.add_field('Cookie', creds)
|
75
|
+
elsif creds[:username] && creds[:password]
|
76
|
+
req.basic_auth(creds[:username], creds[:password])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/analysand/instance.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
require 'analysand/config_response'
|
1
2
|
require 'analysand/errors'
|
3
|
+
require 'analysand/http'
|
4
|
+
require 'analysand/response'
|
5
|
+
require 'analysand/session_response'
|
2
6
|
require 'base64'
|
3
|
-
require 'json/ext'
|
4
7
|
require 'net/http/persistent'
|
8
|
+
require 'rack/utils'
|
5
9
|
require 'uri'
|
6
10
|
|
7
11
|
module Analysand
|
@@ -34,119 +38,160 @@ module Analysand
|
|
34
38
|
# Establishing a session
|
35
39
|
# ----------------------
|
36
40
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# # => [ {
|
40
|
-
# # :issued_at => (a UNIX timestamp),
|
41
|
-
# # :roles => [...roles...],
|
42
|
-
# # :token => 'AuthSession ...',
|
43
|
-
# # :username => (the supplied username)
|
44
|
-
# # },
|
45
|
-
# # the response
|
46
|
-
# # ]
|
47
|
-
# #
|
48
|
-
# # for incorrect credentials:
|
49
|
-
# # => [nil, the response]
|
41
|
+
# resp, = instance.post_session('username', 'password')
|
42
|
+
# cookie = resp.session_cookie
|
50
43
|
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# methods, e.g.
|
44
|
+
# For harmony, the same credentials hash accepted by database methods is
|
45
|
+
# also supported:
|
54
46
|
#
|
55
|
-
#
|
56
|
-
#
|
47
|
+
# resp = instance.post_session(:username => 'username',
|
48
|
+
# :password => 'password')
|
57
49
|
#
|
58
|
-
# db.put(doc, session[:token])
|
59
50
|
#
|
51
|
+
# resp.success? will be true if the session cookie is not empty, false
|
52
|
+
# otherwise.
|
60
53
|
#
|
61
|
-
# Renewing a session
|
62
|
-
# ------------------
|
63
54
|
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# session, resp = instance.renew_session(auth)
|
55
|
+
# Testing a session cookie for validity
|
56
|
+
# -------------------------------------
|
67
57
|
#
|
68
|
-
#
|
69
|
-
# documentation for #renew_session for more details.
|
58
|
+
# resp = instance.get_session(cookie)
|
70
59
|
#
|
60
|
+
# In CouchDB 1.2.0, the response body is a JSON object that looks like
|
61
|
+
#
|
62
|
+
# {
|
63
|
+
# "info": {
|
64
|
+
# "authentication_db": "_users",
|
65
|
+
# "authentication_handlers": [
|
66
|
+
# "oauth",
|
67
|
+
# "cookie",
|
68
|
+
# "default"
|
69
|
+
# ]
|
70
|
+
# },
|
71
|
+
# "ok": true,
|
72
|
+
# "userCtx": {
|
73
|
+
# "name": "username",
|
74
|
+
# "roles": ["member"]
|
75
|
+
# }
|
76
|
+
# }
|
77
|
+
#
|
78
|
+
# resp.valid? will be true if userCtx['name'] is non-null, false otherwise.
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# Getting and setting instance configuration
|
82
|
+
# ------------------------------------------
|
83
|
+
#
|
84
|
+
# v = instance.get_config('couchdb_httpd_auth/allow_persistent_cookies',
|
85
|
+
# credentials)
|
86
|
+
# v.value # => false
|
87
|
+
#
|
88
|
+
# instance.set_config('couchdb_httpd_auth/allow_persistent_cookies',
|
89
|
+
# true, credentials)
|
90
|
+
# # => #<Response code=200 ...>
|
91
|
+
#
|
92
|
+
# v = instance.get_config('couchdb_httpd_auth/allow_persistent_cookies',
|
93
|
+
# credentials)
|
94
|
+
# v.value #=> true
|
95
|
+
#
|
96
|
+
# You can get configuration at any level:
|
97
|
+
#
|
98
|
+
# v = instance.get_config('', credentials)
|
99
|
+
# v.body['stats']['rate'] # => "1000", or whatever you have it set to
|
100
|
+
#
|
101
|
+
# #get_config and #set_config both return Response-like objects. You can
|
102
|
+
# check for failure or success that way:
|
103
|
+
#
|
104
|
+
# v = instance.get_config('couchdb_httpd_auth/allow_persistent_cookies')
|
105
|
+
# v.code # => '403'
|
106
|
+
#
|
107
|
+
# instance.set_config('couchdb_httpd_auth/allow_persistent_cookies', false)
|
108
|
+
# # => #<Response code=403 ...>
|
109
|
+
#
|
110
|
+
# If you want to set configuration and just want to let errors bubble
|
111
|
+
# up the stack, you can use the bang-variants:
|
112
|
+
#
|
113
|
+
# instance.set_config!('stats/rate', 1000)
|
114
|
+
# # => on non-2xx response, raises ConfigurationNotSaved
|
71
115
|
class Instance
|
72
|
-
|
73
|
-
|
116
|
+
include Errors
|
117
|
+
include Http
|
118
|
+
include Rack::Utils
|
74
119
|
|
75
120
|
def initialize(uri)
|
76
|
-
|
121
|
+
init_http_client(uri)
|
122
|
+
end
|
77
123
|
|
78
|
-
|
79
|
-
|
124
|
+
def post_session(*args)
|
125
|
+
username, password = if args.length == 2
|
126
|
+
args
|
127
|
+
else
|
128
|
+
h = args.first
|
129
|
+
[h[:username], h[:password]]
|
130
|
+
end
|
131
|
+
|
132
|
+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
133
|
+
body = build_query('name' => username, 'password' => password)
|
134
|
+
|
135
|
+
Response.new _post('_session', nil, {}, headers, body)
|
80
136
|
end
|
81
137
|
|
82
|
-
def
|
83
|
-
|
138
|
+
def get_session(cookie)
|
139
|
+
headers = { 'Cookie' => cookie }
|
84
140
|
|
85
|
-
|
86
|
-
|
87
|
-
req.body =
|
88
|
-
"name=#{URI.encode(username)}&password=#{URI.encode(password)}"
|
141
|
+
SessionResponse.new _get('_session', nil, {}, headers, nil)
|
142
|
+
end
|
89
143
|
|
90
|
-
|
144
|
+
def get_config(key, credentials = nil)
|
145
|
+
ConfigResponse.new _get("_config/#{key}", credentials)
|
146
|
+
end
|
91
147
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
148
|
+
def set_config(key, value, credentials = nil)
|
149
|
+
# This is a bizarre transformation that deserves some explanation.
|
150
|
+
#
|
151
|
+
# CouchDB configuration is made available as strings containing JSON
|
152
|
+
# data. GET /_config/stats, for example, will return something like
|
153
|
+
# this:
|
154
|
+
#
|
155
|
+
# {"rate":"1000","samples":"[0, 60, 300, 900]"}
|
156
|
+
#
|
157
|
+
# However, I'd really like to write
|
158
|
+
#
|
159
|
+
# instance.set_config('stats/samples', [0, 60, 300, 900])
|
160
|
+
#
|
161
|
+
# and I'd also like to be able to use values from get_config directly,
|
162
|
+
# just for symmetry:
|
163
|
+
#
|
164
|
+
# v = instance1.get_config('stats/samples')
|
165
|
+
# instance2.set_config('stats/samples', v)
|
166
|
+
#
|
167
|
+
# To accomplish this, we convert non-string values to JSON twice.
|
168
|
+
# Strings are passed through.
|
169
|
+
body = (String === value) ? value : value.to_json.to_json
|
170
|
+
|
171
|
+
ConfigResponse.new _put("_config/#{key}", credentials, {}, {}, body)
|
97
172
|
end
|
98
173
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
# If the session was renewed, returns a session information hash identical
|
103
|
-
# in form to the hash returned by #establish_session. If the session was
|
104
|
-
# not renewed, returns the passed-in hash.
|
105
|
-
#
|
106
|
-
#
|
107
|
-
# Renewal behavior
|
108
|
-
# ================
|
109
|
-
#
|
110
|
-
# CouchDB will only send a new session cookie if the current time is
|
111
|
-
# close enough to the session timeout. For CouchDB, that means that the
|
112
|
-
# current time must be within a 10% timeout window (i.e. time left before
|
113
|
-
# timeout < timeout * 0.9).
|
114
|
-
def renew_session(old_session)
|
115
|
-
path = uri + "/_session"
|
116
|
-
|
117
|
-
req = Net::HTTP::Get.new(path.to_s)
|
118
|
-
req.add_field('Cookie', old_session[:token])
|
119
|
-
|
120
|
-
resp = http.request path, req
|
121
|
-
|
122
|
-
if Net::HTTPSuccess === resp
|
123
|
-
if !resp.get_fields('Set-Cookie')
|
124
|
-
[old_session, resp]
|
125
|
-
else
|
126
|
-
[session(resp), resp]
|
127
|
-
end
|
128
|
-
else
|
129
|
-
[nil, resp]
|
174
|
+
def set_config!(key, value, credentials = nil)
|
175
|
+
set_config(key, value, credentials).tap do |resp|
|
176
|
+
raise ex(ConfigurationNotSaved, resp) unless resp.success?
|
130
177
|
end
|
131
178
|
end
|
132
179
|
|
133
180
|
private
|
134
181
|
|
135
|
-
def session(resp)
|
136
|
-
token =
|
137
|
-
|
182
|
+
def session(cookie, resp)
|
183
|
+
token = cookie.split('=', 2).last
|
184
|
+
fields = Base64.decode64(token).split(':')
|
138
185
|
|
139
|
-
fields = Base64.decode64($1).split(':')
|
140
186
|
username = fields[0]
|
141
187
|
time = fields[1].to_i(16)
|
142
188
|
|
143
|
-
|
144
|
-
|
145
|
-
body['userCtx']['roles'] : body['roles']
|
189
|
+
roles = resp.body.has_key?('userCtx') ?
|
190
|
+
resp.body['userCtx']['roles'] : resp.body['roles']
|
146
191
|
|
147
192
|
{ :issued_at => time,
|
148
193
|
:roles => roles,
|
149
|
-
:token =>
|
194
|
+
:token => cookie,
|
150
195
|
:username => username
|
151
196
|
}
|
152
197
|
end
|