right_support 1.0.11 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/right_support/crypto/signed_hash.rb +130 -0
- data/lib/right_support/crypto.rb +32 -0
- data/lib/right_support/net/request_balancer.rb +24 -13
- data/lib/right_support/net/string_encoder.rb +99 -0
- data/lib/right_support.rb +1 -0
- data/right_support.gemspec +7 -13
- metadata +18 -104
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module RightSupport::Crypto
|
4
|
+
class SignedHash
|
5
|
+
DEFAULT_OPTIONS = {
|
6
|
+
:digest => Digest::SHA1,
|
7
|
+
:encoding => JSON
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize(hash={}, options={})
|
11
|
+
options = DEFAULT_OPTIONS.merge(options)
|
12
|
+
@hash = hash
|
13
|
+
@digest = options[:digest]
|
14
|
+
@encoding = options[:encoding]
|
15
|
+
@public_key = options[:public_key]
|
16
|
+
@private_key = options[:private_key]
|
17
|
+
duck_type_check
|
18
|
+
end
|
19
|
+
|
20
|
+
def sign(expires_at)
|
21
|
+
raise ArgumentError, "Cannot sign; missing private_key" unless @private_key
|
22
|
+
raise ArgumentError, "expires_at must be a Time in the future" unless time_check(expires_at)
|
23
|
+
|
24
|
+
metadata = {:expires_at => expires_at}
|
25
|
+
@private_key.private_encrypt( digest( encode( canonicalize( frame(@hash, metadata) ) ) ) )
|
26
|
+
end
|
27
|
+
|
28
|
+
def verify!(signature, expires_at)
|
29
|
+
raise ArgumentError, "Cannot verify; missing public_key" unless @public_key
|
30
|
+
|
31
|
+
metadata = {:expires_at => expires_at}
|
32
|
+
expected = digest( encode( canonicalize( frame(@hash, metadata) ) ) )
|
33
|
+
actual = @public_key.public_decrypt(signature)
|
34
|
+
raise SecurityError, "Signature mismatch: expected #{expected}, got #{actual}" unless actual == expected
|
35
|
+
raise SecurityError, "The signature has expired (or expires_at is not a Time)" unless time_check(expires_at)
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify(signature, expires_at)
|
39
|
+
verify!(signature, expires_at)
|
40
|
+
true
|
41
|
+
rescue Exception => e
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(meth, *args)
|
46
|
+
@hash.__send__(meth, *args)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def duck_type_check
|
52
|
+
unless @digest.is_a?(Class) &&
|
53
|
+
@digest.instance_methods.include?(str_or_symb('update')) &&
|
54
|
+
@digest.instance_methods.include?(str_or_symb('digest'))
|
55
|
+
raise ArgumentError, "Digest class must respond to #update and #digest instance methods"
|
56
|
+
end
|
57
|
+
unless @encoding.respond_to?(str_or_symb('dump'))
|
58
|
+
raise ArgumentError, "Encoding class/module/object must respond to .dump method"
|
59
|
+
end
|
60
|
+
if @public_key && !@public_key.respond_to?(str_or_symb('public_decrypt'))
|
61
|
+
raise ArgumentError, "Public key must respond to :public_decrypt (e.g. an OpenSSL::PKey instance)"
|
62
|
+
end
|
63
|
+
if @private_key && !@private_key.respond_to?(str_or_symb('private_encrypt'))
|
64
|
+
raise ArgumentError, "Private key must respond to :private_encrypt (e.g. an OpenSSL::PKey instance)"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def str_or_symb(method)
|
69
|
+
RUBY_VERSION > '1.9' ? method.to_sym : method.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def time_check(t)
|
73
|
+
t.is_a?(Time) && (t >= Time.now)
|
74
|
+
end
|
75
|
+
|
76
|
+
def frame(data, metadata) # :nodoc:
|
77
|
+
{:data => data, :metadata => metadata}
|
78
|
+
end
|
79
|
+
|
80
|
+
def digest(input) # :nodoc:
|
81
|
+
@digest.new.update(input).digest
|
82
|
+
end
|
83
|
+
|
84
|
+
def encode(input)
|
85
|
+
@encoding.dump(input)
|
86
|
+
end
|
87
|
+
|
88
|
+
def canonicalize(input) # :nodoc:
|
89
|
+
case input
|
90
|
+
when Hash
|
91
|
+
# Hash is the only complex case. We canonicalize a Hash as an Array of pairs, each of which
|
92
|
+
# consists of one key and one value. The ordering of the pairs is consistent with the
|
93
|
+
# ordering of the keys.
|
94
|
+
output = Array.new
|
95
|
+
|
96
|
+
# First, transform the original input hash into something that has canonicalized keys
|
97
|
+
# (which should make them sortable, too). Also canonicalize the values while we are
|
98
|
+
# at it...
|
99
|
+
sortable_input = {}
|
100
|
+
input.each { |k,v| sortable_input[canonicalize(k)] = canonicalize(v) }
|
101
|
+
|
102
|
+
# Sort the keys; guard this operation so we can raise an intelligent error if
|
103
|
+
# something is still not sortable even after canonicalization.
|
104
|
+
begin
|
105
|
+
ordered_keys = sortable_input.keys.sort
|
106
|
+
rescue Exception => e
|
107
|
+
msg = "SignedHash requires sortable hash keys; cannot sort #{sortable_input.keys.inspect} " +
|
108
|
+
"due to #{e.class.name}: #{e.message}"
|
109
|
+
e2 = ArgumentError.new(msg)
|
110
|
+
e2.set_backtrace(e.backtrace)
|
111
|
+
raise e2
|
112
|
+
end
|
113
|
+
|
114
|
+
ordered_keys.each do |key|
|
115
|
+
output << [ key, sortable_input[key] ]
|
116
|
+
end
|
117
|
+
when Array
|
118
|
+
output = input.collect { |x| canonicalize(x) }
|
119
|
+
when Time
|
120
|
+
output = input.to_i
|
121
|
+
when Symbol
|
122
|
+
output = input.to_s
|
123
|
+
else
|
124
|
+
output = input
|
125
|
+
end
|
126
|
+
|
127
|
+
output
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2011 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
module RightSupport
|
24
|
+
#
|
25
|
+
# A namespace for cryptographic functionality.
|
26
|
+
#
|
27
|
+
module Crypto
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'right_support/crypto/signed_hash'
|
@@ -13,20 +13,33 @@ module RightSupport::Net
|
|
13
13
|
#
|
14
14
|
# The balancer does not actually perform requests by itself, which makes this
|
15
15
|
# class usable for various network protocols, and potentially even for non-
|
16
|
-
# networking purposes. The block does all the work; the
|
17
|
-
# a
|
16
|
+
# networking purposes. The block passed to #request does all the work; the
|
17
|
+
# balancer merely selects a suitable endpoint to pass to its block.
|
18
18
|
#
|
19
|
-
# PLEASE NOTE that
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
19
|
+
# PLEASE NOTE that it is VERY IMPORTANT that the balancer is able to properly
|
20
|
+
# distinguish between fatal and non-fatal (retryable) errors. Before you pass
|
21
|
+
# a :fatal option to the RequestBalancer constructor, carefully examine its
|
22
|
+
# default list of fatal exceptions and default logic for deciding whether a
|
23
|
+
# given exception is fatal! There are some subtleties.
|
24
24
|
class RequestBalancer
|
25
25
|
DEFAULT_RETRY_PROC = lambda do |ep, n|
|
26
26
|
n < ep.size
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
# Built-in Ruby exceptions that should be considered fatal. Normally one would be
|
30
|
+
# inclined to simply say RuntimeError or StandardError, but because gem authors
|
31
|
+
# frequently make unwise choices of exception base class, including these top-level
|
32
|
+
# base classes could cause us to falsely think that retryable exceptions are fatal.
|
33
|
+
#
|
34
|
+
# A good example of this phenomenon is the rest-client gem, whose base exception
|
35
|
+
# class is derived from RuntimeError!!
|
36
|
+
DEFAULT_FATAL_EXCEPTIONS = [
|
37
|
+
NoMemoryError, SystemStackError, SignalException, SystemExit,
|
38
|
+
ScriptError,
|
39
|
+
#Subclasses of StandardError, which we can't mention directly
|
40
|
+
ArgumentError, IndexError, LocalJumpError, NameError, RangeError,
|
41
|
+
RegexpError, ThreadError, TypeError, ZeroDivisionError
|
42
|
+
]
|
30
43
|
|
31
44
|
DEFAULT_FATAL_PROC = lambda do |e|
|
32
45
|
if DEFAULT_FATAL_EXCEPTIONS.any? { |c| e.is_a?(c) }
|
@@ -136,15 +149,13 @@ module RightSupport::Net
|
|
136
149
|
complete = false
|
137
150
|
n = 0
|
138
151
|
|
139
|
-
retry_opt = @options[:retry] || DEFAULT_RETRY_PROC
|
140
|
-
|
141
152
|
loop do
|
142
153
|
if complete
|
143
154
|
break
|
144
155
|
else
|
145
|
-
|
146
|
-
|
147
|
-
break if (
|
156
|
+
do_retry = @options[:retry] || DEFAULT_RETRY_PROC
|
157
|
+
do_retry = do_retry.call(@endpoints, n) if do_retry.respond_to?(:call)
|
158
|
+
break if (do_retry.is_a?(Integer) && n >= do_retry) || [nil, false].include?(do_retry)
|
148
159
|
end
|
149
160
|
|
150
161
|
endpoint, need_health_check = @policy.next
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Copyright (c) 2011 RightScale Inc
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'cgi'
|
23
|
+
require 'base64'
|
24
|
+
require 'zlib'
|
25
|
+
|
26
|
+
#
|
27
|
+
# A tool that encodes (binary or ASCII) strings into 7-bit ASCII
|
28
|
+
# using one or more encoding algorithms which are applied sequentially.
|
29
|
+
# The order of algorithms is reversed on decode, naturally!
|
30
|
+
#
|
31
|
+
# This class is designed to be used with network protocols implemented
|
32
|
+
# on top of HTTP, where binary data needs to be encapsulated in URL query
|
33
|
+
# strings, request bodies or other textual payloads. Sometimes multiple
|
34
|
+
# encodings are necessary in order to prevent unnecessary expansion of
|
35
|
+
# the encoded text.
|
36
|
+
#
|
37
|
+
module RightSupport::Net
|
38
|
+
class StringEncoder
|
39
|
+
ENCODINGS = [:base64, :url]
|
40
|
+
ENCODINGS.freeze
|
41
|
+
|
42
|
+
#
|
43
|
+
# Create a new instance.
|
44
|
+
#
|
45
|
+
# === Parameters
|
46
|
+
# *encodings:: list of Symbols representing an ordered sequence of encodings
|
47
|
+
#
|
48
|
+
def initialize(*args)
|
49
|
+
args = args.flatten
|
50
|
+
args.each do |enc|
|
51
|
+
raise ArgumentError, "Unknown encoding #{enc}" unless ENCODINGS.include?(enc)
|
52
|
+
end
|
53
|
+
|
54
|
+
@encodings = args
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Encode a binary or textual string.
|
59
|
+
#
|
60
|
+
# === Parameters
|
61
|
+
# value(String):: the value to be encoded
|
62
|
+
#
|
63
|
+
# === Return
|
64
|
+
# The encoded value, with all encodings applied.
|
65
|
+
def encode(value)
|
66
|
+
@encodings.each do |enc|
|
67
|
+
case enc
|
68
|
+
when :base64
|
69
|
+
value = Base64.encode64(value)
|
70
|
+
when :url
|
71
|
+
value = CGI.escape(value)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
value
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Decode a binary or textual string.
|
80
|
+
#
|
81
|
+
# === Parameters
|
82
|
+
# value(String):: the value to be decoded
|
83
|
+
#
|
84
|
+
# === Return
|
85
|
+
# The decoded string value
|
86
|
+
def decode(value)
|
87
|
+
@encodings.reverse.each do |enc|
|
88
|
+
case enc
|
89
|
+
when :base64
|
90
|
+
value = Base64.decode64(value)
|
91
|
+
when :url
|
92
|
+
value = CGI.unescape(value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/right_support.rb
CHANGED
data/right_support.gemspec
CHANGED
@@ -7,23 +7,17 @@ spec = Gem::Specification.new do |s|
|
|
7
7
|
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
|
8
8
|
|
9
9
|
s.name = 'right_support'
|
10
|
-
s.version = '1.0
|
11
|
-
s.date = '2011-
|
10
|
+
s.version = '1.1.0'
|
11
|
+
s.date = '2011-11-03'
|
12
12
|
|
13
|
-
s.authors = ['Tony Spataro']
|
14
|
-
s.email = '
|
15
|
-
s.homepage= 'https://github.com/
|
13
|
+
s.authors = ['Tony Spataro', 'Sergey Sergyenko', 'Ryan Williamson']
|
14
|
+
s.email = 'support@rightscale.com'
|
15
|
+
s.homepage= 'https://github.com/rightscale/right_support'
|
16
16
|
|
17
17
|
s.summary = %q{Reusable foundation code.}
|
18
|
-
s.description = %q{A toolkit of useful foundation code
|
18
|
+
s.description = %q{A toolkit of useful, reusable foundation code created by RightScale.}
|
19
19
|
|
20
|
-
s.
|
21
|
-
s.add_development_dependency('ruby-debug', [">= 0.10"])
|
22
|
-
s.add_development_dependency('rspec', ["~> 1.3"])
|
23
|
-
s.add_development_dependency('cucumber', ["~> 0.8"])
|
24
|
-
s.add_development_dependency('flexmock', ["~> 0.8"])
|
25
|
-
s.add_development_dependency('net-ssh', ["~> 2.0"])
|
26
|
-
s.add_development_dependency('rest-client', ["~> 1.6"])
|
20
|
+
s.add_dependency('json', ['~> 1.4'])
|
27
21
|
|
28
22
|
basedir = File.dirname(__FILE__)
|
29
23
|
candidates = ['right_support.gemspec', 'LICENSE', 'README.rdoc'] + Dir['lib/**/*']
|
metadata
CHANGED
@@ -1,131 +1,42 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.11
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tony Spataro
|
14
|
+
- Sergey Sergyenko
|
15
|
+
- Ryan Williamson
|
14
16
|
autorequire:
|
15
17
|
bindir: bin
|
16
18
|
cert_chain: []
|
17
19
|
|
18
|
-
date: 2011-
|
20
|
+
date: 2011-11-03 00:00:00 -07:00
|
19
21
|
default_executable:
|
20
22
|
dependencies:
|
21
23
|
- !ruby/object:Gem::Dependency
|
22
|
-
name:
|
24
|
+
name: json
|
23
25
|
prerelease: false
|
24
26
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 49
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
- 8
|
33
|
-
- 7
|
34
|
-
version: 0.8.7
|
35
|
-
type: :development
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: ruby-debug
|
39
|
-
prerelease: false
|
40
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ">="
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
hash: 31
|
46
|
-
segments:
|
47
|
-
- 0
|
48
|
-
- 10
|
49
|
-
version: "0.10"
|
50
|
-
type: :development
|
51
|
-
version_requirements: *id002
|
52
|
-
- !ruby/object:Gem::Dependency
|
53
|
-
name: rspec
|
54
|
-
prerelease: false
|
55
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
|
-
requirements:
|
58
|
-
- - ~>
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
hash: 9
|
61
|
-
segments:
|
62
|
-
- 1
|
63
|
-
- 3
|
64
|
-
version: "1.3"
|
65
|
-
type: :development
|
66
|
-
version_requirements: *id003
|
67
|
-
- !ruby/object:Gem::Dependency
|
68
|
-
name: cucumber
|
69
|
-
prerelease: false
|
70
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
71
27
|
none: false
|
72
28
|
requirements:
|
73
29
|
- - ~>
|
74
30
|
- !ruby/object:Gem::Version
|
75
|
-
hash:
|
76
|
-
segments:
|
77
|
-
- 0
|
78
|
-
- 8
|
79
|
-
version: "0.8"
|
80
|
-
type: :development
|
81
|
-
version_requirements: *id004
|
82
|
-
- !ruby/object:Gem::Dependency
|
83
|
-
name: flexmock
|
84
|
-
prerelease: false
|
85
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
-
none: false
|
87
|
-
requirements:
|
88
|
-
- - ~>
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
hash: 27
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
- 8
|
94
|
-
version: "0.8"
|
95
|
-
type: :development
|
96
|
-
version_requirements: *id005
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: net-ssh
|
99
|
-
prerelease: false
|
100
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
-
none: false
|
102
|
-
requirements:
|
103
|
-
- - ~>
|
104
|
-
- !ruby/object:Gem::Version
|
105
|
-
hash: 3
|
106
|
-
segments:
|
107
|
-
- 2
|
108
|
-
- 0
|
109
|
-
version: "2.0"
|
110
|
-
type: :development
|
111
|
-
version_requirements: *id006
|
112
|
-
- !ruby/object:Gem::Dependency
|
113
|
-
name: rest-client
|
114
|
-
prerelease: false
|
115
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
116
|
-
none: false
|
117
|
-
requirements:
|
118
|
-
- - ~>
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
hash: 3
|
31
|
+
hash: 7
|
121
32
|
segments:
|
122
33
|
- 1
|
123
|
-
-
|
124
|
-
version: "1.
|
125
|
-
type: :
|
126
|
-
version_requirements: *
|
127
|
-
description:
|
128
|
-
email:
|
34
|
+
- 4
|
35
|
+
version: "1.4"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
description: A toolkit of useful, reusable foundation code created by RightScale.
|
39
|
+
email: support@rightscale.com
|
129
40
|
executables: []
|
130
41
|
|
131
42
|
extensions: []
|
@@ -136,6 +47,8 @@ files:
|
|
136
47
|
- LICENSE
|
137
48
|
- README.rdoc
|
138
49
|
- lib/right_support.rb
|
50
|
+
- lib/right_support/crypto.rb
|
51
|
+
- lib/right_support/crypto/signed_hash.rb
|
139
52
|
- lib/right_support/db.rb
|
140
53
|
- lib/right_support/db/cassandra_model.rb
|
141
54
|
- lib/right_support/log.rb
|
@@ -149,6 +62,7 @@ files:
|
|
149
62
|
- lib/right_support/net/balancing/round_robin.rb
|
150
63
|
- lib/right_support/net/http_client.rb
|
151
64
|
- lib/right_support/net/request_balancer.rb
|
65
|
+
- lib/right_support/net/string_encoder.rb
|
152
66
|
- lib/right_support/rack.rb
|
153
67
|
- lib/right_support/rack/custom_logger.rb
|
154
68
|
- lib/right_support/ruby.rb
|
@@ -158,7 +72,7 @@ files:
|
|
158
72
|
- lib/right_support/validation/ssh.rb
|
159
73
|
- right_support.gemspec
|
160
74
|
has_rdoc: true
|
161
|
-
homepage: https://github.com/
|
75
|
+
homepage: https://github.com/rightscale/right_support
|
162
76
|
licenses: []
|
163
77
|
|
164
78
|
post_install_message:
|