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