right_support 2.4.1 → 2.5.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.
@@ -5,6 +5,8 @@ module RightSupport::Crypto
5
5
 
6
6
  if require_succeeds?('yajl')
7
7
  DefaultEncoding = ::Yajl
8
+ elsif require_succeeds?('oj')
9
+ DefaultEncoding = ::Oj
8
10
  elsif require_succeeds?('json')
9
11
  DefaultEncoding = ::JSON
10
12
  else
@@ -0,0 +1,223 @@
1
+ require 'time'
2
+ require 'date'
3
+
4
+ module RightSupport::Data
5
+ # Utility class that implements true, lossless Ruby-to-JSON serialization.
6
+ # With a few small exceptions, this module can #dump any Ruby object in your
7
+ # VM, and later #load the exact same object from its serialized representation.
8
+ # It can also be used with encoding schemes other than JSON, e.g. msgpack.
9
+ #
10
+ # This class works by transforming Ruby object graphs into an intermediate
11
+ # representation that consists solely of JSON-clean Ruby objects (String,
12
+ # Integer, ...). This intermediate format is dubbed "JSONish", and it can
13
+ # be transformed to JSON and back without losing data or creating ambiguity.
14
+ #
15
+ # If you use the class-level (::load and ::dump) interface to this class,
16
+ # it always uses JSON serialization by default. If you construct an instance,
17
+ # you can control which encoding scheme to use, as well as which Hash key to use
18
+ # to mark a serialized object.
19
+ #
20
+ # === JSONish Object Representation
21
+ # Most Ruby simple types (String, Integer, Float, true/false/nil) are
22
+ # represented by their corresponding JSON type. However, some JSONish
23
+ # values have special meaning:
24
+ # * Strings beginning with a ':' represent a Ruby Symbol
25
+ # * Strings in the ISO 8601 timestamp format represent a Ruby Time
26
+ #
27
+ # To avoid ambiguity due to Ruby Strings that happen to look like a
28
+ # JSONish Time or Symbol, some Strings are "object-escaped," meaning they
29
+ # are represented as a JSON object with _ruby_class:String.
30
+ #
31
+ # Arbitrary Ruby objects are represented as a Hash that contains a special
32
+ # key '_ruby_class'. The other key/value pairs of the hash consist of the
33
+ # object's instance variables. Any object whose state is solely contained
34
+ # in its instance variables is therefore eligible for serialization.
35
+ #
36
+ # JSONish also has special-purpose logic for some Ruby built-ins,
37
+ # allowing them to be serialized even though their state is not contained in
38
+ # instance variables. The following are all serializable built-ins:
39
+ # * Class
40
+ # * Module
41
+ # * String (see below).
42
+ #
43
+ # === Capabilities
44
+ # The serializer can handle any Ruby object that uses instance variables to
45
+ # record state. It cleanly round-trips the following Ruby object types:
46
+ # * Collections (Hash, Array)
47
+ # * JSON-clean data (Numeric, String, true, false, nil)
48
+ # * Symbol
49
+ # * Time
50
+ # * Class
51
+ # * Module
52
+ #
53
+ # === Known Limitations
54
+ # Cannot record the following kinds of state:
55
+ # * Class variables of objects
56
+ # * State that lives outside of instance variables, e.g. IO#fileno
57
+ #
58
+ # Cannot cleanly round-trip the following:
59
+ # * Objects that represent callable code: Proc, Lambda, etc.
60
+ # * High-precision floating point numbers (due to truncation)
61
+ # * Times with timezone other than UTC or precision greater than 1 sec
62
+ # * Hash keys that are anything other than a String (depends on JSON parser)
63
+ # * Hashes that contain a String key whose value is '_ruby_class'
64
+ class Serializer
65
+ if require_succeeds?('yajl')
66
+ Encoder = ::Yajl
67
+ elsif require_succeeds?('oj')
68
+ Encoder = ::Oj
69
+ elsif require_succeeds?('json')
70
+ Encoder = ::JSON
71
+ else
72
+ Encoder = nil
73
+ end unless defined?(Encoder)
74
+
75
+ # Format string to use when sprintf'ing a JSONish Time
76
+ TIME_FORMAT = '%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2dZ'
77
+ # Pattern to match Strings against for object-escaping
78
+ TIME_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/
79
+ # Special key used as a pseudo-instance-variable for Class and Module
80
+ CLASS_ESCAPE_KEY = 'name'
81
+ # Special key used as a pseudo-instance-variable for String
82
+ STRING_ESCAPE_KEY = 'value'
83
+
84
+ DEFAULT_OPTIONS = {
85
+ :marker => '_ruby_class',
86
+ :encoder => Encoder
87
+ }
88
+
89
+ def self.load(data)
90
+ self.new.load(data)
91
+ end
92
+
93
+ def self.dump(object)
94
+ self.new.dump(object)
95
+ end
96
+
97
+ # Instantiate a Serializer instance.
98
+ # @option options :encoder [#load, #dump] underlying data-encoder to use. Defaults to any available JSON gem.
99
+ # @option options :marker special object key to use when representing a serialized Ruby object. Defaults to '_ruby_class'.
100
+ def initialize(options={})
101
+ options = DEFAULT_OPTIONS.merge(options)
102
+ @marker = options[:marker]
103
+ @encoder = options[:encoder]
104
+ end
105
+
106
+ # @param [String] data valid JSON document representing a serialized object
107
+ # @return [Object] unserialized Ruby object
108
+ def load(data)
109
+ jsonish = @encoder.load(data)
110
+ jsonish_to_object(jsonish)
111
+ end
112
+
113
+ # @param [Object] object any Ruby object
114
+ # @return [String] JSON document representing the serialized object
115
+ def dump(object)
116
+ jsonish = object_to_jsonish(object)
117
+ @encoder.dump(jsonish)
118
+ end
119
+
120
+ protected
121
+
122
+ # Given an Object, transform it into a JSONish Ruby structure.
123
+ # @param [Object] object any Ruby object
124
+ # @return [Object] JSONish representation of input object
125
+ def object_to_jsonish(object)
126
+ case object
127
+ when String
128
+ if (object =~ /^:/ ) || (object =~ TIME_PATTERN)
129
+ # Strings that look like a Symbol or Time must be object-escaped.
130
+ {@marker => String.name,
131
+ STRING_ESCAPE_KEY => object}
132
+ else
133
+ object
134
+ end
135
+ when Fixnum, Float, TrueClass, FalseClass, NilClass
136
+ object
137
+ when Hash
138
+ object.inject({}) do |result, (k, v)|
139
+ result[object_to_jsonish(k)] = object_to_jsonish(v)
140
+ result
141
+ end
142
+ when Array
143
+ object.map { |e| object_to_jsonish(e) }
144
+ when Symbol
145
+ # Ruby Symbol is represented as a string beginning with ':'
146
+ ":#{object}"
147
+ when Time
148
+ # Ruby Time is represented as an ISO 8601 timestamp in UTC timeztone
149
+ utc = object.utc
150
+ TIME_FORMAT % [utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec]
151
+ when Class, Module
152
+ # Ruby Class/Module needs special handling - no instance vars
153
+ { @marker => object.class.name,
154
+ CLASS_ESCAPE_KEY => object.name }
155
+ else
156
+ # Generic serialized object; convert to Hash.
157
+ hash = {}
158
+ hash[@marker] = object.class.name
159
+
160
+ object.instance_variables.each do |var|
161
+ hash[ var[1..-1] ] = object_to_jsonish(object.instance_variable_get(var))
162
+ end
163
+
164
+ hash
165
+ end
166
+ end
167
+
168
+ # Given a JSONish structure, transform it back to a Ruby object.
169
+ # @param [Object] jsonish JSONish Ruby structure
170
+ # @return [Object] unserialized Ruby object
171
+ def jsonish_to_object(jsonish)
172
+ case jsonish
173
+ when String
174
+ if jsonish =~ /^:/
175
+ # JSONish Symbol
176
+ jsonish[1..-1].to_sym
177
+ elsif TIME_PATTERN.match(jsonish)
178
+ # JSONish Time
179
+ Time.parse(jsonish)
180
+ else
181
+ # Normal String
182
+ jsonish
183
+ end
184
+ when Fixnum, Float, TrueClass, FalseClass, NilClass
185
+ jsonish
186
+ when Hash
187
+ if jsonish.key?(@marker) # We have a serialized Ruby object!
188
+ hash = jsonish
189
+ klass = hash.delete(@marker)
190
+ case klass
191
+ when Class.name, Module.name
192
+ # Serialized instance of Ruby Class or Module
193
+ jsonish = hash.delete(CLASS_ESCAPE_KEY).to_const
194
+ when String.name
195
+ # Object-escaped Ruby String
196
+ jsonish = String.new(hash.delete(STRING_ESCAPE_KEY))
197
+ when Time.name
198
+ # Object-escaped Ruby String
199
+ jsonish = Time.at(hash.delete(TIME_ESCAPE_KEY))
200
+ else
201
+ # Generic serialized object
202
+ klass = klass.to_const
203
+ jsonish = klass.allocate
204
+ hash.each_pair do |k, v|
205
+ jsonish.instance_variable_set("@#{k}", jsonish_to_object(v))
206
+ end
207
+ end
208
+
209
+ jsonish
210
+ else # We have a plain old Ruby Hash
211
+ jsonish.inject({}) do |result, (k, v)|
212
+ result[jsonish_to_object(k)] = jsonish_to_object(v)
213
+ result
214
+ end
215
+ end
216
+ when Array
217
+ jsonish.map { |e| jsonish_to_object(e) }
218
+ else
219
+ raise ArgumentError, "Non-JSONish object of type '#{jsonish.class}'"
220
+ end
221
+ end
222
+ end
223
+ end
@@ -29,5 +29,6 @@ module RightSupport
29
29
  end
30
30
  end
31
31
 
32
+ require 'right_support/data/serializer'
32
33
  require 'right_support/data/hash_tools'
33
34
  require 'right_support/data/uuid'
@@ -0,0 +1,144 @@
1
+ # Copyright (c) 2009-2011 RightScale, Inc, All Rights Reserved Worldwide.
2
+ #
3
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
4
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
5
+ # reproduction, modification, or disclosure of this program is
6
+ # strictly prohibited. Any use of this program by an authorized
7
+ # licensee is strictly subject to the terms and conditions,
8
+ # including confidentiality obligations, set forth in the applicable
9
+ # License Agreement between RightScale.com, Inc. and the licensee.
10
+
11
+ module RightSupport::Net
12
+ # Class provides S3 functionality.
13
+ # As part of RightSupport S3Helper does not include Rightscale::S3 and Encryptor modules.
14
+ # This modules must be included in application.
15
+ #
16
+ # Example:
17
+ #
18
+ # require 'right_support'
19
+ # require 'right_aws'
20
+ # require 'encryptor'
21
+ #
22
+ # s3_config = YAML.load_file(File.expand_path("../../config/s3.yml",__FILE__))[ENV['RACK_ENV']]
23
+ # RightSupport::Net::S3Helper.init(s3_config, Rightscale::S3, Encryptor)
24
+ # s3_health = RightSupport::Net::S3Helper.health_check
25
+ class S3Helper
26
+ # Init() is the first method which must be called.
27
+ # This is configuration and integration with S3 and Encryptor
28
+ #
29
+ # @param [Hash] config config for current environment (from YAML config file)
30
+ # creds:
31
+ # aws_access_key_id: String
32
+ # aws_secret_access_key: String
33
+ # bucket_name: String
34
+ # master_secret: String
35
+ # @param [Class] s3 Rightscale::S3 class is used to connect to S3
36
+ # @param [Class] encryptor Class which will be used to encrypt data
37
+ # encrypt() and decrypt() methods must be implemented
38
+ #
39
+ # Init() does not return anything
40
+ def self.init(config, s3, encryptor)
41
+ @config = config
42
+ @s3class = s3
43
+ @encryptor = encryptor
44
+ end
45
+ # Checks S3 credentials syntax in config parameter
46
+ #
47
+ # @return [Boolean] true if s3 creadentials are ok or false if not
48
+ def self.s3_enabled?
49
+ return @s3_enabled if @s3_enabled
50
+ @s3_enabled ||= !config.empty? &&
51
+ !config["creds"].empty? &&
52
+ !config["creds"]["aws_access_key_id"].nil? &&
53
+ config["creds"]["aws_access_key_id"] != "@@AWS_ACCESS_KEY_ID@@"
54
+ end
55
+ # Returns @config parameter
56
+ def self.config
57
+ @config
58
+ end
59
+ # Creates (if does not exist) and return S3 object
60
+ def self.s3
61
+ @s3 ||= @s3class.new config["creds"]["aws_access_key_id"], config["creds"]["aws_secret_access_key"]
62
+ end
63
+ # Creates S3 Bucket object using created in s3() object
64
+ def self.bucket
65
+ #TO DISCUSS: second param 'true' means 'create new bucket'
66
+ #@bucket ||= s3.bucket(config["bucket_name"], true)
67
+ @bucket ||= s3.bucket(config["bucket_name"])
68
+ end
69
+ # Returns Master secret from config
70
+ def self.master_secret
71
+ config["master_secret"]
72
+ end
73
+ # Downloads data from S3 Bucket
74
+ #
75
+ # @param [String] key Name of the bucket key
76
+ # @param [Block] blck Ruby code wich will be done by Bucket object
77
+ #
78
+ # @return [String] S3 bucket's key content in plain/text format
79
+ def self.get(key, &blck)
80
+ return nil unless s3_enabled?
81
+
82
+ object = bucket.key(key, true, &blck)
83
+ return nil if object.nil?
84
+
85
+ # don't decrypt/verify unencrypted values
86
+ return object.data if object.meta_headers["digest"].nil?
87
+
88
+ ciphertext = object.data
89
+ passphrase = "#{key}:#{master_secret}"
90
+ plaintext = @encryptor.decrypt(:key=>passphrase, :value=>ciphertext)
91
+ digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, passphrase, plaintext)
92
+
93
+ if digest == object.meta_headers["digest"]
94
+ return plaintext
95
+ else
96
+ raise "digest for key:#{key} in s3 does not match calculated digest."
97
+ end
98
+ end
99
+ # Upload data to S3 Bucket
100
+ #
101
+ # @param [String] key Name of the bucket key
102
+ # @param [String] plaintext Data which should be saved in the Bucket
103
+ # @param [Block] blck Ruby code wich will be done by Bucket object
104
+ #
105
+ def self.post(key, plaintext, &blck)
106
+ passphrase = "#{key}:#{master_secret}"
107
+ plaintext = "-- no detail --" unless plaintext && plaintext.size > 0
108
+ ciphertext = @encryptor.encrypt(:key=>passphrase, :value=>plaintext)
109
+ digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, passphrase, plaintext)
110
+ bucket.put(key, ciphertext, {"digest" => digest}, &blck)
111
+ end
112
+ # Checks if it is possible to connect to the S3 Bucket.
113
+ #
114
+ # @return [String or True] If test was successful then return true
115
+ # Else return error message
116
+ def self.health_check
117
+ config
118
+ s3
119
+ # test config file
120
+ return 'S3 Config file: Credentials: Syntax error.' unless s3_enabled?
121
+ ["bucket_name", "master_secret"].each do |conf|
122
+ return "S3 Config file: #{conf.upcase}: Syntax error." if config[conf].nil? || config[conf] == '' || config[conf] == "@@#{conf.upcase}@@"
123
+ end
124
+ # test connection
125
+ original_text = 'heath check'
126
+ test_key = 'ping'
127
+ begin
128
+ post(test_key, original_text)
129
+ rescue Exception => e
130
+ return e.message
131
+ end
132
+ begin
133
+ result_text = get(test_key)
134
+ rescue Exception => e
135
+ return e.message
136
+ end
137
+ return 'Sended text and returned one are not equal. Possible master_secret problem' if result_text != original_text
138
+ # retrurn true if there were not errors
139
+ true
140
+ end
141
+
142
+ end
143
+
144
+ end
@@ -36,5 +36,6 @@ require 'right_support/net/lb'
36
36
  require 'right_support/net/request_balancer'
37
37
  require 'right_support/net/ssl'
38
38
  require 'right_support/net/dns'
39
+ require 'right_support/net/s3_helper'
39
40
 
40
41
  RightSupport::Net.extend(RightSupport::Net::AddressHelper)
@@ -7,8 +7,8 @@ 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 = '2.4.1'
11
- s.date = '2012-09-18'
10
+ s.version = '2.5.0'
11
+ s.date = '2012-09-19'
12
12
 
13
13
  s.authors = ['Tony Spataro', 'Sergey Sergyenko', 'Ryan Williamson', 'Lee Kirchhoff', 'Sergey Enin']
14
14
  s.email = 'support@rightscale.com'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_support
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
- - 4
9
- - 1
10
- version: 2.4.1
8
+ - 5
9
+ - 0
10
+ version: 2.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tony Spataro
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2012-09-18 00:00:00 -07:00
22
+ date: 2012-09-19 00:00:00 -07:00
23
23
  default_executable:
24
24
  dependencies: []
25
25
 
@@ -43,6 +43,7 @@ files:
43
43
  - lib/right_support/crypto/signed_hash.rb
44
44
  - lib/right_support/data.rb
45
45
  - lib/right_support/data/hash_tools.rb
46
+ - lib/right_support/data/serializer.rb
46
47
  - lib/right_support/data/uuid.rb
47
48
  - lib/right_support/db.rb
48
49
  - lib/right_support/db/cassandra_model.rb
@@ -62,6 +63,7 @@ files:
62
63
  - lib/right_support/net/lb/round_robin.rb
63
64
  - lib/right_support/net/lb/sticky.rb
64
65
  - lib/right_support/net/request_balancer.rb
66
+ - lib/right_support/net/s3_helper.rb
65
67
  - lib/right_support/net/ssl.rb
66
68
  - lib/right_support/net/ssl/open_ssl_patch.rb
67
69
  - lib/right_support/net/string_encoder.rb