bixby_common 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +45 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/bixby_common.gemspec +95 -0
- data/lib/bixby_common.rb +21 -0
- data/lib/bixby_common/api/base_module.rb +12 -0
- data/lib/bixby_common/api/bundle_repository.rb +12 -0
- data/lib/bixby_common/api/json_request.rb +25 -0
- data/lib/bixby_common/api/json_response.rb +69 -0
- data/lib/bixby_common/command_response.rb +31 -0
- data/lib/bixby_common/command_spec.rb +149 -0
- data/lib/bixby_common/exception/bundle_not_found.rb +5 -0
- data/lib/bixby_common/exception/command_not_found.rb +5 -0
- data/lib/bixby_common/util/crypto_util.rb +64 -0
- data/lib/bixby_common/util/hashify.rb +16 -0
- data/lib/bixby_common/util/http_client.rb +70 -0
- data/lib/bixby_common/util/jsonify.rb +27 -0
- data/test/helper.rb +74 -0
- data/test/support/test_bundle/bin/cat +2 -0
- data/test/support/test_bundle/bin/echo +2 -0
- data/test/support/test_bundle/digest +17 -0
- data/test/support/test_bundle/manifest.json +0 -0
- data/test/test_bixby_common.rb +18 -0
- data/test/test_command_spec.rb +92 -0
- data/test/test_jsonify.rb +36 -0
- data/test_guard.rb +179 -0
- metadata +267 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
require 'base64'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Bixby
|
6
|
+
module CryptoUtil
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def encrypt(data, key_pem, iv_pem)
|
11
|
+
c = new_cipher()
|
12
|
+
c.encrypt
|
13
|
+
key = c.random_key
|
14
|
+
iv = c.random_iv
|
15
|
+
encrypted = c.update(data) + c.final
|
16
|
+
|
17
|
+
out = []
|
18
|
+
out << w( key_pem.public_encrypt(key) )
|
19
|
+
out << w( iv_pem.private_encrypt(iv) )
|
20
|
+
out << e64(encrypted)
|
21
|
+
|
22
|
+
return out.join("\n")
|
23
|
+
end
|
24
|
+
|
25
|
+
def decrypt(data, key_pem, iv_pem)
|
26
|
+
data = StringIO.new(data, 'rb')
|
27
|
+
key = key_pem.private_decrypt(read_next(data))
|
28
|
+
iv = iv_pem.public_decrypt(read_next(data))
|
29
|
+
|
30
|
+
c = new_cipher()
|
31
|
+
c.decrypt
|
32
|
+
c.key = key
|
33
|
+
c.iv = iv
|
34
|
+
|
35
|
+
ret = c.update(d64(data.read)) + c.final
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def new_cipher
|
42
|
+
OpenSSL::Cipher.new("AES-256-CBC")
|
43
|
+
end
|
44
|
+
|
45
|
+
def w(s)
|
46
|
+
e64(s).gsub(/\n/, "\\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_next(data)
|
50
|
+
d64(data.readline.gsub(/\\n/, "\n"))
|
51
|
+
end
|
52
|
+
|
53
|
+
def e64(s)
|
54
|
+
Base64.encode64(s)
|
55
|
+
end
|
56
|
+
|
57
|
+
def d64(s)
|
58
|
+
Base64.decode64(s)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end # CryptoUtil
|
64
|
+
end # Bixby
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module Bixby
|
3
|
+
|
4
|
+
# Adds to_hash method to an Object
|
5
|
+
module Hashify
|
6
|
+
|
7
|
+
# Creates a Hash representation of self
|
8
|
+
#
|
9
|
+
# @return [Hash]
|
10
|
+
def to_hash
|
11
|
+
self.instance_variables.inject({}) { |m,v| m[v[1,v.length].to_sym] = instance_variable_get(v); m }
|
12
|
+
end
|
13
|
+
|
14
|
+
end # Hashify
|
15
|
+
|
16
|
+
end # Bixby
|
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
require 'curb'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
module Bixby
|
6
|
+
|
7
|
+
# Utilities for creating HTTP Clients. Just a thin wrapper around curb and JSON
|
8
|
+
# for common cases.
|
9
|
+
module HttpClient
|
10
|
+
|
11
|
+
# Execute an HTTP GET request to the given URL
|
12
|
+
#
|
13
|
+
# @param [String] url
|
14
|
+
# @return [String] Contents of the response's body
|
15
|
+
def http_get(url)
|
16
|
+
Curl::Easy.http_get(url).body_str
|
17
|
+
end
|
18
|
+
|
19
|
+
# Execute an HTTP GET request (see #http_get) and parse the JSON response
|
20
|
+
#
|
21
|
+
# @param [String] url
|
22
|
+
# @return [Object] Result of calling JSON.parse() on the response body
|
23
|
+
def http_get_json(url)
|
24
|
+
MultiJson.load(http_get(url))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convert a Hash into a Curl::Postfield Array
|
28
|
+
#
|
29
|
+
# @param [Hash] data
|
30
|
+
def create_post_data(data)
|
31
|
+
if data.kind_of? Hash then
|
32
|
+
data = data.map{ |k,v| Curl::PostField.content(k, v) }
|
33
|
+
end
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
# Execute an HTTP POST request to the given URL
|
38
|
+
#
|
39
|
+
# @param [String] url
|
40
|
+
# @param [Hash] data Key/Value pairs to POST
|
41
|
+
# @return [String] Contents of the response's body
|
42
|
+
def http_post(url, data)
|
43
|
+
return Curl::Easy.http_post(url, create_post_data(data)).body_str
|
44
|
+
end
|
45
|
+
|
46
|
+
# Execute an HTTP POST request (see #http_get) and parse the JSON response
|
47
|
+
#
|
48
|
+
# @param [String] url
|
49
|
+
# @param [Hash] data Key/Value pairs to POST
|
50
|
+
# @return [Object] Result of calling JSON.parse() on the response body
|
51
|
+
def http_post_json(url, data)
|
52
|
+
MultiJson.load(http_post(url, data))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Execute an HTTP post request and save the response body
|
56
|
+
#
|
57
|
+
# @param [String] url
|
58
|
+
# @param [Hash] data Key/Value pairs to POST
|
59
|
+
# @return [void]
|
60
|
+
def http_post_download(url, data, dest)
|
61
|
+
File.open(dest, "w") do |io|
|
62
|
+
c = Curl::Easy.new(url)
|
63
|
+
c.on_body { |d| io << d; d.length }
|
64
|
+
c.http_post(data)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end # HttpClient
|
69
|
+
|
70
|
+
end # Bixby
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Bixby
|
5
|
+
module Jsonify
|
6
|
+
|
7
|
+
include Hashify
|
8
|
+
|
9
|
+
def to_json
|
10
|
+
MultiJson.dump(self.to_hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def from_json(json)
|
15
|
+
json = MultiJson.load(json) if json.kind_of? String
|
16
|
+
obj = self.allocate
|
17
|
+
json.each{ |k,v| obj.send("#{k}=".to_sym, v) }
|
18
|
+
obj
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(receiver)
|
23
|
+
receiver.extend(ClassMethods)
|
24
|
+
end
|
25
|
+
|
26
|
+
end # Jsonify
|
27
|
+
end # Bixby
|
data/test/helper.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'minitest/unit'
|
11
|
+
require 'turn'
|
12
|
+
require 'turn/reporter'
|
13
|
+
require 'turn/reporters/outline_reporter'
|
14
|
+
|
15
|
+
Turn.config.framework = :minitest
|
16
|
+
Turn.config.format = :outline
|
17
|
+
|
18
|
+
module Turn
|
19
|
+
class OutlineReporter < Reporter
|
20
|
+
def start_test(test)
|
21
|
+
@stdout = StringIO.new
|
22
|
+
@stderr = StringIO.new
|
23
|
+
|
24
|
+
name = naturalized_name(test)
|
25
|
+
|
26
|
+
io.print " %-57s" % name
|
27
|
+
|
28
|
+
@stdout.rewind
|
29
|
+
@stderr.rewind
|
30
|
+
|
31
|
+
$stdout = @stdout
|
32
|
+
$stderr = @stderr unless $DEBUG
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
require 'simplecov'
|
39
|
+
SimpleCov.start do
|
40
|
+
add_filter "/test/"
|
41
|
+
end
|
42
|
+
rescue Exception => ex
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
47
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
48
|
+
require 'bixby_common'
|
49
|
+
|
50
|
+
class MiniTest::Unit::TestCase
|
51
|
+
|
52
|
+
# minitest assert_throws doesn't seem to work properly
|
53
|
+
def assert_throws(clazz, msg = nil, &block)
|
54
|
+
begin
|
55
|
+
yield
|
56
|
+
rescue Exception => ex
|
57
|
+
puts "#{ex.class}: #{ex.message}"
|
58
|
+
puts ex.backtrace.join("\n")
|
59
|
+
if clazz.to_s == ex.class.name then
|
60
|
+
if msg.nil?
|
61
|
+
return
|
62
|
+
elsif msg == ex.message then
|
63
|
+
return
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
flunk("Expected #{mu_pp(clazz)} to have been thrown")
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
Dir.glob(File.dirname(__FILE__) + "/../lib/**/*.rb").each{ |f| require f }
|
73
|
+
|
74
|
+
MiniTest::Unit.autorun
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"digest": "2429629015110c29f8fae8ca97e0e494410a28b981653c0e094cfe4a7567f1b7",
|
3
|
+
"files": [
|
4
|
+
{
|
5
|
+
"file": "bin/cat",
|
6
|
+
"digest": "6d14ab63802f3ef51bdc16db1cfeb6df93167fe7a2aa09a6c737293d42c118b2"
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"file": "bin/echo",
|
10
|
+
"digest": "299001868fb8c02fd431c336c6d058f5558c5dff5b5af5e6fe04b870a6a9cbba"
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"file": "manifest.json",
|
14
|
+
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
15
|
+
}
|
16
|
+
]
|
17
|
+
}
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Bixby
|
4
|
+
module Test
|
5
|
+
|
6
|
+
class TestBixbyCommon < MiniTest::Unit::TestCase
|
7
|
+
|
8
|
+
def test_autoloading
|
9
|
+
assert_equal(JsonRequest, JsonRequest.new(nil, nil).class)
|
10
|
+
assert_equal(BundleNotFound, BundleNotFound.new.class)
|
11
|
+
assert_equal(CommandNotFound, CommandNotFound.new.class)
|
12
|
+
assert_equal(CommandSpec, CommandSpec.new.class)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end # Test
|
18
|
+
end # Bixby
|
@@ -0,0 +1,92 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
module Bixby
|
6
|
+
module Test
|
7
|
+
|
8
|
+
class TestCommandSpec < MiniTest::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
BundleRepository.path = File.expand_path(File.dirname(__FILE__))
|
12
|
+
h = { :repo => "support", :bundle => "test_bundle", 'command' => "echo", :foobar => "baz" }
|
13
|
+
@c = CommandSpec.new(h)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def test_init_with_hash
|
18
|
+
|
19
|
+
assert(@c)
|
20
|
+
assert_equal("support", @c.repo)
|
21
|
+
assert_equal("test_bundle", @c.bundle)
|
22
|
+
assert_equal("echo", @c.command)
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_to_hash
|
27
|
+
|
28
|
+
assert_equal("support", @c.to_hash[:repo])
|
29
|
+
assert_equal("test_bundle", @c.to_hash[:bundle])
|
30
|
+
assert_equal("echo", @c.to_hash[:command])
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_validate
|
35
|
+
@c.validate
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_validate_failures
|
39
|
+
assert_throws(BundleNotFound) do
|
40
|
+
CommandSpec.new(:repo => "support", :bundle => "foobar").validate
|
41
|
+
end
|
42
|
+
assert_throws(CommandNotFound) do
|
43
|
+
CommandSpec.new(:repo => "support", :bundle => "test_bundle", :command => "foobar").validate
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_execute
|
48
|
+
(status, stdout, stderr) = @c.execute
|
49
|
+
assert(status.success?)
|
50
|
+
assert_equal("hi\n", stdout)
|
51
|
+
assert_equal("", stderr)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_execute_stdin
|
55
|
+
@c.command = "cat"
|
56
|
+
@c.stdin = "hi"
|
57
|
+
(status, stdout, stderr) = @c.execute
|
58
|
+
assert(status.success?)
|
59
|
+
assert_equal("hi", stdout)
|
60
|
+
assert_equal("", stderr)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_digest
|
64
|
+
assert_equal "2429629015110c29f8fae8ca97e0e494410a28b981653c0e094cfe4a7567f1b7", @c.digest
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_digest_no_err
|
68
|
+
c = CommandSpec.new({ :repo => "support", :bundle => "test_bundle", 'command' => "echofoo" })
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_update_digest
|
72
|
+
expected = MultiJson.load(File.read(BundleRepository.path + "/support/test_bundle/digest"))
|
73
|
+
|
74
|
+
t = "/tmp/foobar_test_repo"
|
75
|
+
d = "#{t}/support/test_bundle/digest"
|
76
|
+
`mkdir -p #{t}`
|
77
|
+
`cp -a #{BundleRepository.path}/support #{t}/`
|
78
|
+
`rm #{d}`
|
79
|
+
BundleRepository.path = t
|
80
|
+
|
81
|
+
refute File.exist? d
|
82
|
+
@c.update_digest
|
83
|
+
assert File.exist? d
|
84
|
+
|
85
|
+
assert_equal MultiJson.dump(expected), MultiJson.dump(MultiJson.load(File.read(d)))
|
86
|
+
`rm -rf #{t}`
|
87
|
+
end
|
88
|
+
|
89
|
+
end # TestCommandSpec
|
90
|
+
|
91
|
+
end # Test
|
92
|
+
end # Bixby
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Bixby
|
5
|
+
module Test
|
6
|
+
|
7
|
+
class TestJsonify < MiniTest::Unit::TestCase
|
8
|
+
|
9
|
+
def test_to_json
|
10
|
+
j = JFoo.new
|
11
|
+
j.bar = "baz"
|
12
|
+
json = j.to_json
|
13
|
+
assert json
|
14
|
+
assert_equal '{"bar":"baz"}', json
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_from_json
|
18
|
+
j1 = JFoo.new
|
19
|
+
j1.bar = "baz"
|
20
|
+
|
21
|
+
j2 = JFoo.from_json(j1.to_json)
|
22
|
+
assert_equal "baz", j2.bar
|
23
|
+
|
24
|
+
j3 = JFoo.from_json('{"bar":"baz"}')
|
25
|
+
assert_equal "baz", j3.bar
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Jsonify
|
29
|
+
|
30
|
+
class JFoo
|
31
|
+
include Jsonify
|
32
|
+
attr_accessor :bar
|
33
|
+
end
|
34
|
+
|
35
|
+
end # Test
|
36
|
+
end # Bixby
|