forward 0.0.14 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/forward.rb +8 -0
- data/lib/forward/api.rb +1 -1
- data/lib/forward/api/client_log.rb +2 -10
- data/lib/forward/api/tunnel.rb +2 -2
- data/lib/forward/api/{public_key.rb → tunnel_key.rb} +3 -3
- data/lib/forward/cli.rb +6 -10
- data/lib/forward/client.rb +4 -3
- data/lib/forward/config.rb +87 -59
- data/lib/forward/version.rb +1 -1
- data/test/api/{public_key_test.rb → tunnel_key_test.rb} +5 -5
- data/test/api/tunnel_test.rb +3 -6
- data/test/config_test.rb +33 -40
- data/test/test_helper.rb +1 -0
- metadata +5 -5
data/lib/forward.rb
CHANGED
data/lib/forward/api.rb
CHANGED
@@ -7,22 +7,14 @@ module Forward
|
|
7
7
|
resource.uri = '/api/client_logs'
|
8
8
|
params = {
|
9
9
|
:client => Forward.client_string,
|
10
|
-
:log => log
|
10
|
+
:log => log
|
11
11
|
}
|
12
12
|
|
13
|
-
params[:
|
13
|
+
params[:api_token] = Forward.config.api_token unless Forward.config.nil?
|
14
14
|
|
15
15
|
resource.post(params)
|
16
16
|
end
|
17
17
|
|
18
|
-
private
|
19
|
-
|
20
|
-
def self.user_id
|
21
|
-
if !Forward.client.nil? && !Forward.client.config.nil?
|
22
|
-
Forward.client.config.id
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
18
|
end
|
27
19
|
end
|
28
20
|
end
|
data/lib/forward/api/tunnel.rb
CHANGED
@@ -62,8 +62,8 @@ module Forward
|
|
62
62
|
def self.destroy_and_create(id)
|
63
63
|
Forward.log(:debug, "Destroying tunnel: #{id}")
|
64
64
|
destroy(id)
|
65
|
-
puts "tunnel removed, creating
|
66
|
-
create
|
65
|
+
puts "tunnel removed, now we're creating a new one"
|
66
|
+
create(Forward.client.options)
|
67
67
|
end
|
68
68
|
|
69
69
|
def self.create_error(errors)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Forward
|
2
2
|
module Api
|
3
|
-
class
|
3
|
+
class TunnelKey < Resource
|
4
4
|
|
5
5
|
def self.create
|
6
|
-
resource =
|
7
|
-
resource.uri = '/api/
|
6
|
+
resource = TunnelKey.new(:create)
|
7
|
+
resource.uri = '/api/tunnel_keys'
|
8
8
|
|
9
9
|
response = resource.post
|
10
10
|
|
data/lib/forward/cli.rb
CHANGED
@@ -193,16 +193,12 @@ module Forward
|
|
193
193
|
#
|
194
194
|
# Returns a Hash with the email and password.
|
195
195
|
def self.authenticate
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
Forward.log(:debug, "Authenticating User: `#{email}:#{password.gsub(/./, 'x')}'")
|
203
|
-
|
204
|
-
{ :email => email, :password => password }
|
205
|
-
end
|
196
|
+
puts 'Enter your email and password'
|
197
|
+
email = ask('email: ').chomp
|
198
|
+
password = ask('password: ') { |q| q.echo = false }.chomp
|
199
|
+
Forward.log(:debug, "Authenticating User: `#{email}:#{password.gsub(/./, 'x')}'")
|
200
|
+
|
201
|
+
{ :email => email, :password => password }
|
206
202
|
end
|
207
203
|
|
208
204
|
# Parses various options and arguments, validates everything to ensure
|
data/lib/forward/client.rb
CHANGED
@@ -53,17 +53,18 @@ module Forward
|
|
53
53
|
trap(:INT) { cleanup_and_exit!('closing tunnel and exiting...') }
|
54
54
|
|
55
55
|
Forward.client = @client = Client.new(options)
|
56
|
-
@tunnel
|
56
|
+
@tunnel = @client.setup_tunnel
|
57
|
+
@session = Net::SSH.start(@tunnel.tunneler, Forward.ssh_user, @client.ssh_options)
|
57
58
|
|
58
59
|
Forward.log(:debug, "Starting remote forward at #{@tunnel.subdomain}.fwd.wf on port #{@tunnel.port}")
|
59
60
|
puts "Forwarding port #{@tunnel.hostport} at \033[04mhttps://#{@tunnel.subdomain}.fwd.wf\033[m\nCtrl-C to stop forwarding"
|
60
|
-
|
61
|
+
|
61
62
|
@session.forward.remote(@tunnel.hostport, @tunnel.host, @tunnel.port)
|
62
63
|
@session.loop { watch_session(@session) }
|
63
64
|
|
64
65
|
rescue Net::SSH::AuthenticationFailed => e
|
65
66
|
Forward.log(:fatal, "SSH Auth failed `#{e}'")
|
66
|
-
cleanup_and_exit!("Authentication failed, try deleting `#{Forward::Config
|
67
|
+
cleanup_and_exit!("Authentication failed, try deleting `#{Forward::Config.config_path}' and giving it another go. If the problem continues, contact support@forwardhq.com")
|
67
68
|
rescue => e
|
68
69
|
Forward.log(:fatal, "#{e.message}\n#{e.backtrace.join("\n")}")
|
69
70
|
cleanup_and_exit!("You've been disconnected...")
|
data/lib/forward/config.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'yaml'
|
4
|
+
|
1
5
|
module Forward
|
2
6
|
class Config
|
3
|
-
|
4
|
-
CIPHER_LENGTH = 128
|
5
|
-
CIPHER_MODE = :CBC
|
6
|
-
DELIMETER = '-------M-------'
|
7
|
+
CONFIG_FILE_VALUES = [ :api_token ]
|
7
8
|
|
8
9
|
attr_accessor :id
|
9
10
|
attr_accessor :api_token
|
@@ -39,12 +40,11 @@ module Forward
|
|
39
40
|
Hash[instance_variables.map { |var| [var[1..-1].to_sym, instance_variable_get(var)] }]
|
40
41
|
end
|
41
42
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
43
|
+
# Validate that the required values are in the Config.
|
44
|
+
# Raises a config error if values are missing.
|
45
45
|
def validate
|
46
46
|
Forward.log(:debug, 'Validating Config')
|
47
|
-
attributes = [:
|
47
|
+
attributes = [:api_token, :private_key]
|
48
48
|
errors = []
|
49
49
|
|
50
50
|
attributes.each do |attribute|
|
@@ -58,62 +58,76 @@ module Forward
|
|
58
58
|
elsif errors.length >= 2
|
59
59
|
raise ConfigError, "#{errors.join(', ')} are required fields"
|
60
60
|
end
|
61
|
-
|
62
61
|
end
|
63
62
|
|
63
|
+
# Write the current config data to `config_path', and the current
|
64
|
+
# private_key to `key_path'.
|
64
65
|
#
|
65
|
-
#
|
66
|
-
#
|
66
|
+
# Returns the Config object.
|
67
67
|
def write
|
68
68
|
Forward.log(:debug, 'Writing Config')
|
69
|
-
|
70
|
-
|
71
|
-
data = JSON.dump(self.to_hash)
|
72
|
-
cipher = OpenSSL::Cipher::AES.new(CIPHER_LENGTH, CIPHER_MODE)
|
73
|
-
cipher.encrypt
|
69
|
+
key_folder = File.dirname(Config.key_path)
|
70
|
+
config_data = to_hash.delete_if { |k,v| !CONFIG_FILE_VALUES.include?(k) }
|
74
71
|
|
75
|
-
|
76
|
-
iv = cipher.random_iv
|
77
|
-
|
78
|
-
encrypted_data = cipher.update(data) + cipher.final
|
79
|
-
config_data = Base64.encode64(encrypted_data)
|
80
|
-
config_data << DELIMETER
|
81
|
-
config_data << Base64.encode64("#{key}::#{iv}")
|
72
|
+
self.validate
|
82
73
|
|
83
|
-
|
74
|
+
FileUtils.mkdir(key_folder) unless File.exist?(key_folder)
|
75
|
+
File.open(Config.config_path, 'w') { |f| f.write(YAML.dump(config_data)) }
|
76
|
+
File.open(Config.key_path, 'w') { |f| f.write(private_key) }
|
84
77
|
|
85
78
|
self
|
79
|
+
rescue
|
80
|
+
raise ConfigError, 'Unable to write config file'
|
86
81
|
end
|
87
82
|
|
83
|
+
# Shortcut for checking if host os is windows.
|
88
84
|
#
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
config_data = File.read(CONFIG_PATH)
|
94
|
-
encrypted_data, keys = config_data.split(DELIMETER).map { |d| Base64.decode64(d) }
|
95
|
-
key, iv = keys.split('::')
|
96
|
-
decipher = OpenSSL::Cipher::AES.new(CIPHER_LENGTH, CIPHER_MODE)
|
97
|
-
|
98
|
-
decipher.decrypt
|
99
|
-
|
100
|
-
decipher.key = key
|
101
|
-
decipher.iv = iv
|
85
|
+
# Returns true or false if windows or not.
|
86
|
+
def self.windows?
|
87
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
88
|
+
end
|
102
89
|
|
103
|
-
|
90
|
+
# Returns the location of the forward ssh private key
|
91
|
+
# based on the host os.
|
92
|
+
#
|
93
|
+
# Returns the String path.
|
94
|
+
def self.key_path
|
95
|
+
if windows?
|
96
|
+
File.join(ENV['HOME'], 'forward', 'key')
|
97
|
+
else
|
98
|
+
File.join(ENV['HOME'], '.ssh', 'forwardhq.com')
|
99
|
+
end
|
100
|
+
end
|
104
101
|
|
105
|
-
|
106
|
-
|
107
|
-
|
102
|
+
# Returns the location of the forward config file
|
103
|
+
# based on the host os.
|
104
|
+
#
|
105
|
+
# Returns the String path.
|
106
|
+
def self.config_path
|
107
|
+
if windows?
|
108
|
+
File.join(ENV['HOME'], 'forward', 'config')
|
109
|
+
else
|
110
|
+
File.join(ENV['HOME'], '.forward')
|
111
|
+
end
|
108
112
|
end
|
109
113
|
|
110
114
|
# Checks to see if a .forward config file exists.
|
111
115
|
#
|
112
116
|
# Returns true or false based on the existence of the config file.
|
113
117
|
def self.present?
|
114
|
-
File.exist?
|
118
|
+
File.exist? config_path
|
119
|
+
end
|
120
|
+
|
121
|
+
# Checks to see if a `private_key' exist.
|
122
|
+
#
|
123
|
+
# Returns true or false based on the existence of the key file.
|
124
|
+
def self.key_file_present?
|
125
|
+
File.exist? key_path
|
115
126
|
end
|
116
127
|
|
128
|
+
# Create a config file if it doesn't exist, load one if it does.
|
129
|
+
#
|
130
|
+
# Returns the resulting Config object.
|
117
131
|
def self.create_or_load
|
118
132
|
if Config.present?
|
119
133
|
Config.load
|
@@ -122,42 +136,56 @@ module Forward
|
|
122
136
|
end
|
123
137
|
end
|
124
138
|
|
139
|
+
# Create a config by authenticating the user via the api,
|
140
|
+
# and saving the users api_token/id/private_key.
|
141
|
+
#
|
142
|
+
# Returns the new Config object.
|
125
143
|
def self.create
|
126
144
|
Forward.log(:debug, 'Creating Config')
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
145
|
+
if @updating_config || ask('Already have an account with Forward? ').chomp =~ /\Ay/i
|
146
|
+
config = Config.new
|
147
|
+
email, password = CLI.authenticate.values_at(:email, :password)
|
148
|
+
config.update(Forward::Api::User.api_token(email, password))
|
149
|
+
Forward::Api.token = config.api_token
|
150
|
+
config.private_key = Forward::Api::TunnelKey.create
|
151
|
+
|
152
|
+
config.write
|
153
|
+
else
|
154
|
+
Client.cleanup_and_exit!("You'll need a Forward account first. You can create one at \033[04mhttps://forwardhq.com\033[04m")
|
155
|
+
end
|
134
156
|
end
|
135
157
|
|
136
158
|
# It initializes a new Config instance, updates it with the config values
|
137
159
|
# from the config file, and raises an error if there's not a config or if
|
138
160
|
# the config options aren't valid.
|
139
161
|
#
|
140
|
-
# Returns the Config
|
162
|
+
# Returns the Config object.
|
141
163
|
def self.load
|
142
164
|
Forward.log(:debug, 'Loading Config')
|
143
165
|
config = Config.new
|
144
166
|
|
145
|
-
|
146
|
-
|
167
|
+
raise ConfigError, "Unable to find a forward config file at `#{config_path}'" unless Config.present?
|
168
|
+
|
169
|
+
if File.read(config_path).include? '-----M-----'
|
170
|
+
puts "Forward needs to update your config file, please re-authenticate"
|
171
|
+
File.delete(config_path)
|
172
|
+
@updating_config = true
|
173
|
+
create_or_load
|
147
174
|
end
|
148
175
|
|
176
|
+
raise ConfigError, "Unable to find a forward key file at `#{key_path}'" unless Config.key_file_present?
|
177
|
+
|
178
|
+
|
179
|
+
config.update(YAML.load_file(config_path).symbolize_keys)
|
180
|
+
config.private_key = File.read(key_path)
|
181
|
+
Forward::Api.token = config.api_token
|
182
|
+
|
149
183
|
config.validate
|
150
184
|
|
151
|
-
Forward
|
185
|
+
Forward.config = config
|
152
186
|
|
153
187
|
config
|
154
188
|
end
|
155
189
|
|
156
|
-
# Deletes the .forward config file if it exists.
|
157
|
-
def self.clear
|
158
|
-
Forward.log(:debug, 'Clearning Config')
|
159
|
-
File.delete(CONFIG_PATH) if Config.present?
|
160
|
-
end
|
161
|
-
|
162
190
|
end
|
163
191
|
end
|
data/lib/forward/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
describe Forward::Api::
|
3
|
+
describe Forward::Api::TunnelKey do
|
4
4
|
|
5
5
|
before :each do
|
6
6
|
FakeWeb.allow_net_connect = false
|
@@ -9,19 +9,19 @@ describe Forward::Api::PublicKey do
|
|
9
9
|
it 'retrieves the public_key and returns it' do
|
10
10
|
fake_body = { :private_key => 'ssh-key 1234567890' }
|
11
11
|
|
12
|
-
stub_api_request(:post, '/api/
|
12
|
+
stub_api_request(:post, '/api/tunnel_keys', :body => fake_body.to_json)
|
13
13
|
|
14
|
-
response = Api::
|
14
|
+
response = Api::TunnelKey.create
|
15
15
|
response.must_equal fake_body[:private_key]
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'exits with message if response has errors' do
|
19
19
|
fake_body = { :errors => { :base => 'Unable to retrieve a private key' } }
|
20
20
|
|
21
|
-
stub_api_request(:post, '/api/
|
21
|
+
stub_api_request(:post, '/api/tunnel_keys', :body => fake_body.to_json)
|
22
22
|
|
23
23
|
lambda {
|
24
|
-
dev_null { Api::
|
24
|
+
dev_null { Api::TunnelKey.create }
|
25
25
|
}.must_raise SystemExit
|
26
26
|
end
|
27
27
|
|
data/test/api/tunnel_test.rb
CHANGED
@@ -11,10 +11,9 @@ describe Forward::Api::Tunnel do
|
|
11
11
|
Forward.client = mock
|
12
12
|
fake_body = { :_id => '1', :subdomain => 'foo', :port => 56789 }
|
13
13
|
|
14
|
-
Forward.client.expects(:options).returns(:port => 3000)
|
15
14
|
stub_api_request(:post, '/api/tunnels', :body => fake_body.to_json)
|
16
15
|
|
17
|
-
response = Forward::Api::Tunnel.create
|
16
|
+
response = Forward::Api::Tunnel.create(:port => 3000)
|
18
17
|
|
19
18
|
fake_body.each do |key, value|
|
20
19
|
response[key].must_equal fake_body[key]
|
@@ -25,11 +24,10 @@ describe Forward::Api::Tunnel do
|
|
25
24
|
Forward.client = mock
|
26
25
|
fake_body = { :errors => { :base => [ 'unable to create tunnel' ] } }
|
27
26
|
|
28
|
-
Forward.client.expects(:options).returns(:port => 3000)
|
29
27
|
stub_api_request(:post, '/api/tunnels', :body => fake_body.to_json)
|
30
28
|
|
31
29
|
lambda {
|
32
|
-
dev_null { Forward::Api::Tunnel.create }
|
30
|
+
dev_null { Forward::Api::Tunnel.create(:port => 3000) }
|
33
31
|
}.must_raise SystemExit
|
34
32
|
end
|
35
33
|
|
@@ -41,13 +39,12 @@ describe Forward::Api::Tunnel do
|
|
41
39
|
]
|
42
40
|
index_body = [ { :_id => 'abc123' }, { :_id => 'def456' } ]
|
43
41
|
|
44
|
-
Forward.client.expects(:options).returns(:port => 3000).twice
|
45
42
|
stub_api_request(:post, '/api/tunnels', post_options)
|
46
43
|
stub_api_request(:get, '/api/tunnels', :body => index_body.to_json)
|
47
44
|
STDIN.expects(:gets).returns('1')
|
48
45
|
Forward::Api::Tunnel.expects(:destroy).with(index_body.first[:_id])
|
49
46
|
|
50
|
-
dev_null { Forward::Api::Tunnel.create }
|
47
|
+
dev_null { Forward::Api::Tunnel.create(:port => 3000) }
|
51
48
|
end
|
52
49
|
|
53
50
|
it 'destroys a tunnel and returns the attributes' do
|
data/test/config_test.rb
CHANGED
@@ -3,8 +3,7 @@ require 'test_helper'
|
|
3
3
|
describe Forward::Config do
|
4
4
|
|
5
5
|
before :each do
|
6
|
-
@
|
7
|
-
:id => '12345',
|
6
|
+
@config_attributes = {
|
8
7
|
:api_token => 'abcdefg',
|
9
8
|
:private_key => 'secret'
|
10
9
|
}
|
@@ -12,56 +11,58 @@ describe Forward::Config do
|
|
12
11
|
end
|
13
12
|
|
14
13
|
after :each do
|
15
|
-
if Forward::Config.present?
|
16
|
-
|
17
|
-
end
|
14
|
+
FileUtils.rm(Forward::Config.config_path) if Forward::Config.present?
|
15
|
+
FileUtils.rm(Forward::Config.key_path) if Forward::Config.key_file_present?
|
18
16
|
end
|
19
17
|
|
20
18
|
it "initializes a config with a hash" do
|
21
|
-
config = Forward::Config.new(@
|
19
|
+
config = Forward::Config.new(@config_attributes)
|
22
20
|
|
23
|
-
@
|
21
|
+
@config_attributes.each do |key, value|
|
24
22
|
config.send(key).must_equal value
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
26
|
it "updates a config with a hash" do
|
29
27
|
config = Forward::Config.new
|
30
|
-
config.update(@
|
28
|
+
config.update(@config_attributes)
|
31
29
|
|
32
|
-
@
|
30
|
+
@config_attributes.each do |key, value|
|
33
31
|
config.send(key).must_equal value
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
35
|
it "writes and reads a config from disk" do
|
38
|
-
config = Forward::Config.new(@
|
36
|
+
config = Forward::Config.new(@config_attributes)
|
39
37
|
config.write
|
40
38
|
|
41
39
|
saved_config = Forward::Config.load
|
42
40
|
|
43
|
-
|
41
|
+
File.exist?(Forward::Config.config_path).must_equal true
|
42
|
+
File.exist?(Forward::Config.key_path).must_equal true
|
43
|
+
|
44
|
+
@config_attributes.each do |key, value|
|
44
45
|
saved_config.send(key).must_equal value
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
it "only writes config values to the config file" do
|
50
|
+
config = Forward::Config.new(@config_attributes)
|
51
|
+
config.write
|
52
|
+
|
53
|
+
config_file = File.read(Forward::Config.config_path)
|
54
|
+
config_file.include?('private_key').must_equal false
|
55
|
+
end
|
56
|
+
|
48
57
|
it "converts a config to a hash" do
|
49
|
-
config = Forward::Config.new(@
|
58
|
+
config = Forward::Config.new(@config_attributes)
|
50
59
|
config_hash = config.to_hash
|
51
60
|
|
52
|
-
@
|
61
|
+
@config_attributes.each do |key, value|
|
53
62
|
config_hash[key].must_equal value
|
54
63
|
end
|
55
64
|
end
|
56
65
|
|
57
|
-
it "deletes config file" do
|
58
|
-
config = Forward::Config.new(@user_attributes)
|
59
|
-
config.write
|
60
|
-
Forward::Config.clear
|
61
|
-
|
62
|
-
Forward::Config.wont_be :present?
|
63
|
-
end
|
64
|
-
|
65
66
|
it "raises exception with an empty config" do
|
66
67
|
config = Forward::Config.new
|
67
68
|
|
@@ -69,34 +70,26 @@ describe Forward::Config do
|
|
69
70
|
end
|
70
71
|
|
71
72
|
it "raises exception with an invalid config" do
|
72
|
-
@
|
73
|
-
config = Forward::Config.new(@
|
73
|
+
@config_attributes.delete(:api_token)
|
74
|
+
config = Forward::Config.new(@config_attributes)
|
74
75
|
|
75
76
|
lambda { config.validate }.must_raise Forward::ConfigError
|
76
77
|
end
|
77
78
|
|
78
|
-
it "raises exception with bad config on write" do
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
79
|
+
# it "raises exception with bad config on write" do
|
80
|
+
# @config_attributes.delete(:private_key)
|
81
|
+
# config = Forward::Config.new(@config_attributes)
|
82
|
+
#
|
83
|
+
# lambda { config.write }.must_raise Forward::ConfigError
|
84
|
+
# end
|
84
85
|
|
85
86
|
it "raises exception when a config file is not found" do
|
86
87
|
lambda { Forward::Config.load }.must_raise Forward::ConfigError
|
87
88
|
end
|
88
89
|
|
89
|
-
it "raises exception when
|
90
|
-
config_path
|
91
|
-
|
92
|
-
|
93
|
-
File.open(config_path, 'w') { |f| f.write(config_data) }
|
94
|
-
|
95
|
-
lambda { Forward::Config.read }.must_raise Forward::ConfigError
|
96
|
-
end
|
97
|
-
|
98
|
-
it "raises exception when an invalid config file is loaded" do
|
99
|
-
skip
|
90
|
+
it "raises exception when a key file is not found" do
|
91
|
+
File.open(Forward::Config.config_path, 'w') { |f| f.write YAML.dump(@config_attributes) }
|
92
|
+
lambda { Forward::Config.load }.must_raise Forward::ConfigError
|
100
93
|
end
|
101
94
|
|
102
95
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forward
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -142,9 +142,9 @@ files:
|
|
142
142
|
- lib/forward.rb
|
143
143
|
- lib/forward/api.rb
|
144
144
|
- lib/forward/api/client_log.rb
|
145
|
-
- lib/forward/api/public_key.rb
|
146
145
|
- lib/forward/api/resource.rb
|
147
146
|
- lib/forward/api/tunnel.rb
|
147
|
+
- lib/forward/api/tunnel_key.rb
|
148
148
|
- lib/forward/api/user.rb
|
149
149
|
- lib/forward/cli.rb
|
150
150
|
- lib/forward/client.rb
|
@@ -153,8 +153,8 @@ files:
|
|
153
153
|
- lib/forward/error.rb
|
154
154
|
- lib/forward/tunnel.rb
|
155
155
|
- lib/forward/version.rb
|
156
|
-
- test/api/public_key_test.rb
|
157
156
|
- test/api/resource_test.rb
|
157
|
+
- test/api/tunnel_key_test.rb
|
158
158
|
- test/api/tunnel_test.rb
|
159
159
|
- test/api/user_test.rb
|
160
160
|
- test/api_test.rb
|
@@ -188,8 +188,8 @@ signing_key:
|
|
188
188
|
specification_version: 3
|
189
189
|
summary: Forward Lets You Share localhost over the Web. Demo a Website Without Hosting.
|
190
190
|
test_files:
|
191
|
-
- test/api/public_key_test.rb
|
192
191
|
- test/api/resource_test.rb
|
192
|
+
- test/api/tunnel_key_test.rb
|
193
193
|
- test/api/tunnel_test.rb
|
194
194
|
- test/api/user_test.rb
|
195
195
|
- test/api_test.rb
|