forward 0.0.1 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -0
- data/Rakefile +6 -1
- data/bin/forward +6 -0
- data/forward.gemspec +12 -4
- data/forwardhq.crt +112 -0
- data/lib/forward.rb +86 -0
- data/lib/forward/api.rb +45 -0
- data/lib/forward/api/client_log.rb +28 -0
- data/lib/forward/api/public_key.rb +16 -0
- data/lib/forward/api/resource.rb +121 -0
- data/lib/forward/api/tunnel.rb +91 -0
- data/lib/forward/api/user.rb +19 -0
- data/lib/forward/cli.rb +238 -0
- data/lib/forward/client.rb +92 -0
- data/lib/forward/config.rb +163 -0
- data/lib/forward/core_extensions.rb +12 -0
- data/lib/forward/error.rb +10 -0
- data/lib/forward/tunnel.rb +58 -0
- data/lib/forward/version.rb +1 -1
- data/test/api/public_key_test.rb +28 -0
- data/test/api/resource_test.rb +82 -0
- data/test/api/tunnel_test.rb +75 -0
- data/test/api/user_test.rb +28 -0
- data/test/api_test.rb +0 -0
- data/test/cli_test.rb +84 -0
- data/test/client_test.rb +8 -0
- data/test/config_test.rb +102 -0
- data/test/test_helper.rb +40 -0
- data/test/tunnel_test.rb +8 -0
- metadata +156 -9
data/README.md
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
1. parse options
|
2
|
+
2. pass to client
|
3
|
+
3. client gets a session for the user based on stored token/authentication request or asks the user to signup
|
4
|
+
4. if account is in good standing client requests a tunnel to be setup
|
5
|
+
5. if tunnel is setup, client opens the ssh channel
|
6
|
+
6. client starts forwarding traffic
|
7
|
+
7. the end
|
data/Rakefile
CHANGED
data/bin/forward
ADDED
data/forward.gemspec
CHANGED
@@ -4,14 +4,22 @@ require File.expand_path('../lib/forward/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ['blahed']
|
6
6
|
gem.email = ['travis@50east.co']
|
7
|
-
gem.
|
8
|
-
gem.
|
9
|
-
gem.homepage = ''
|
7
|
+
gem.summary = 'Forward Lets You Share localhost over the Web. Demo a Website Without Hosting.'
|
8
|
+
gem.homepage = 'https://forwardhq.com'
|
10
9
|
|
11
10
|
gem.files = `git ls-files`.split($\)
|
12
11
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
-
gem.test_files = gem.files.grep(%r{^(test|
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|features)/})
|
14
13
|
gem.name = 'forward'
|
15
14
|
gem.require_paths = ['lib']
|
16
15
|
gem.version = Forward::VERSION
|
16
|
+
|
17
|
+
gem.add_dependency 'json', '~> 1.7.3'
|
18
|
+
gem.add_dependency 'highline', '~> 1.6.13'
|
19
|
+
gem.add_dependency 'net-ssh', '~> 2.4.0'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'minitest', '~> 3.0.0'
|
22
|
+
gem.add_development_dependency 'mocha', '~> 0.11.4'
|
23
|
+
gem.add_development_dependency 'fakeweb', '~> 1.3.0'
|
24
|
+
gem.add_development_dependency 'fakefs', '~> 0.4.0'
|
17
25
|
end
|
data/forwardhq.crt
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIFCTCCA/GgAwIBAgIQQN4DiY5aF21RqglaWWIJnDANBgkqhkiG9w0BAQUFADBy
|
3
|
+
MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
4
|
+
VQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEYMBYGA1UE
|
5
|
+
AxMPRXNzZW50aWFsU1NMIENBMB4XDTEyMDgwOTAwMDAwMFoXDTEzMDgwOTIzNTk1
|
6
|
+
OVowUjEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMRUwEwYDVQQL
|
7
|
+
EwxFc3NlbnRpYWxTU0wxFjAUBgNVBAMTDWZvcndhcmRocS5jb20wggEiMA0GCSqG
|
8
|
+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcyzuKOE3k44yBYLKXp3AXlf3zbXnZQoHg
|
9
|
+
qzSupKtxHXIVPv6H6IK5HMKT0fgkJaBldMACrIwh6qBCwNm/2OlToTlPY4Ho/9tU
|
10
|
+
f4ud6Y8zSGMOssvR2/jNAfViRtn6AsDZu+0+NM3ZbF4lYorO7KrSTZHhmVDtk4KP
|
11
|
+
HpGRLXKBOa3H/8TQ7RrG8779Ad+RiobwpZc0C7RV/xX2rM5HwWDGNc03eC2GiRSd
|
12
|
+
u8vHp36PeXeW+EWOiHdOthBS49FiTlb6VIQe9eyfOKXMAWEsBum55woGbOutULwQ
|
13
|
+
TQ9HfGfcqve0Zjo24chLvb0hmbOXk+VRJG7J83+QZzHn3JjHEF5dAgMBAAGjggG5
|
14
|
+
MIIBtTAfBgNVHSMEGDAWgBTay+qtWwhdzP/8JlTOSeVVxjj0+DAdBgNVHQ4EFgQU
|
15
|
+
fvmPP+mzOr0ed77Yfsdle6lKLc8wDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQC
|
16
|
+
MAAwNAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDBglg
|
17
|
+
hkgBhvhCBAEwRQYDVR0gBD4wPDA6BgsrBgEEAbIxAQICBzArMCkGCCsGAQUFBwIB
|
18
|
+
Fh1odHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzA7BgNVHR8ENDAyMDCgLqAs
|
19
|
+
hipodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9Fc3NlbnRpYWxTU0xDQS5jcmwwbgYI
|
20
|
+
KwYBBQUHAQEEYjBgMDgGCCsGAQUFBzAChixodHRwOi8vY3J0LmNvbW9kb2NhLmNv
|
21
|
+
bS9Fc3NlbnRpYWxTU0xDQV8yLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
|
22
|
+
Y29tb2RvY2EuY29tMCsGA1UdEQQkMCKCDWZvcndhcmRocS5jb22CEXd3dy5mb3J3
|
23
|
+
YXJkaHEuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQCJEBvC7c/IrrA0YQPNfgeI2UHN
|
24
|
+
OE5wSK8OveVL++iP4lTxMXew59YOjFccovwot6Jp7RLtlDNlRePDavKzcG/IXiY7
|
25
|
+
oW39bjjLwosrjljq2tZCMm0aTGeNIjMjcN76m9TidlU2DmhhbAT2JdSe+FqEgJnM
|
26
|
+
rJxOgDyxNFGA4UERYambkm+CEFWhEZ6CUneW69IcENglZN9DRcBrX4tivUA/i24W
|
27
|
+
xXz7OFhwfNbJRoLCGH6YVSKzyE8hV1EXDRmTsmHQ1Tw7xk+FduxR5Q2TiB8cdix5
|
28
|
+
ByS6bHA8C5DnS2xJ4mXSxcrCIcjDtbyMEFdDMS7yzfv3LHDetyYyt4rK9E2D
|
29
|
+
-----END CERTIFICATE-----
|
30
|
+
-----BEGIN CERTIFICATE-----
|
31
|
+
MIIFAzCCA+ugAwIBAgIQGLLLuqME8aAPwfLzJkYqSjANBgkqhkiG9w0BAQUFADCB
|
32
|
+
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
33
|
+
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
|
34
|
+
BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
|
35
|
+
MDBaFw0xOTEyMzEyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh
|
36
|
+
dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
|
37
|
+
TyBDQSBMaW1pdGVkMRgwFgYDVQQDEw9Fc3NlbnRpYWxTU0wgQ0EwggEiMA0GCSqG
|
38
|
+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt8AiwcsargxIxF3CJhakgEtSYau2A1NHf
|
39
|
+
5I5ZLdOWIY120j8YC0YZYwvHIPPlC92AGvFaoL0dds23Izp0XmEbdaqb1IX04XiR
|
40
|
+
0y3hr/yYLgbSeT1awB8hLRyuIVPGOqchfr7tZ291HRqfalsGs2rjsQuqag7nbWzD
|
41
|
+
ypWMN84hHzWQfdvaGlyoiBSyD8gSIF/F03/o4Tjg27z5H6Gq1huQByH6RSRQXScq
|
42
|
+
oChBRVt9vKCiL6qbfltTxfEFFld+Edc7tNkBdtzffRDPUanlOPJ7FAB1WfnwWdsX
|
43
|
+
Pvev5gItpHnBXaIcw5rIp6gLSApqLn8tl2X2xQScRMiZln5+pN0vAgMBAAGjggGD
|
44
|
+
MIIBfzAfBgNVHSMEGDAWgBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQU
|
45
|
+
2svqrVsIXcz//CZUzknlVcY49PgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
|
46
|
+
MAYBAf8CAQAwIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMD4GA1Ud
|
47
|
+
IAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21v
|
48
|
+
ZG8uY29tL0NQUzBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2Nh
|
49
|
+
LmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBsBggrBgEFBQcB
|
50
|
+
AQRgMF4wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NvbW9k
|
51
|
+
b1VUTlNHQ0NBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2Eu
|
52
|
+
Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAtlzR6QDLqcJcvgTtLeRJ3rvuq1xqo2l/z
|
53
|
+
odueTZbLN3qo6u6bldudu+Ennv1F7Q5Slqz0J790qpL0pcRDAB8OtXj5isWMcL2a
|
54
|
+
ejGjKdBZa0wztSz4iw+SY1dWrCRnilsvKcKxudokxeRiDn55w/65g+onO7wdQ7Vu
|
55
|
+
F6r7yJiIatnyfKH2cboZT7g440LX8NqxwCPf3dfxp+0Jj1agq8MLy6SSgIGSH6lv
|
56
|
+
+Wwz3D5XxqfyH8wqfOQsTEZf6/Nh9yvENZ+NWPU6g0QO2JOsTGvMd/QDzczc4BxL
|
57
|
+
XSXaPV7Od4rhPsbXlM1wSTz/Dr0ISKvlUhQVnQ6cGodWaK2cCQBk
|
58
|
+
-----END CERTIFICATE-----
|
59
|
+
-----BEGIN CERTIFICATE-----
|
60
|
+
MIIE8TCCA9mgAwIBAgIQS3VXgmk5DJvjLxLsX22UXjANBgkqhkiG9w0BAQUFADBv
|
61
|
+
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
|
62
|
+
ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
|
63
|
+
eHRlcm5hbCBDQSBSb290MB4XDTEwMDIxMTAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
|
64
|
+
gYExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
65
|
+
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMScwJQYD
|
66
|
+
VQQDEx5DT01PRE8gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3
|
67
|
+
DQEBAQUAA4IBDwAwggEKAoIBAQDQQIuLcuORG/dRwRtUBJjTqb/B5opdO4f7u4jO
|
68
|
+
DeMvPwaW8KIpUJmu2zuhV7B0UXHN7UKRTUH+qcjYaoZ3RLtZZpdQXrTULHBEz9o3
|
69
|
+
lUJpPDDEcbNS8CFNodi6OXwcnqMknfKDFpiqFnxDmxVbt640kf7UYiYYRpo/68H5
|
70
|
+
8ZBX66x6DYvbcjBqZtXgRqNw3GjZ/wRIiXfeten7Z21B6bw5vTLZYgLxsag9bjec
|
71
|
+
4i/i06Imi8a4VUOI4SM+pdIkOWpHqwDUobOpJf4NP6cdutNRwQuk2qw471VQJAVl
|
72
|
+
RpM0Ty2NrcbUIRnSjsoFYXEHc0flihkSvQRNzk6cpUisuyb3AgMBAAGjggF0MIIB
|
73
|
+
cDAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUC1jl
|
74
|
+
i8ZMFTekQKkwqSG+RzZaVv8wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB
|
75
|
+
Af8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9j
|
76
|
+
cmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYI
|
77
|
+
KwYBBQUHAQEEgaYwgaMwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0
|
78
|
+
LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0
|
79
|
+
cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsG
|
80
|
+
AQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUA
|
81
|
+
A4IBAQBNhw1QMPOCXcQ/1O/ujUjj572Qa8QyOMZeKKtcpa1h+Y67hRQ5IVFbjozc
|
82
|
+
F5KAL4OUaYjBvieOT5+pg9i+14eScaO2/RF0uJWBKCB3DUN3dXY4HU0bLpeJjAob
|
83
|
+
ZhZS1BSab4BIFt4wwEJo6r+iuipETayJ4vPMU5vj5h1uT5if2Q5RUIbgGjQyJIB9
|
84
|
+
OofzPOVaTbeLvQokDa7b9I9c0mYMghxyN7bRudCYNBsnbYteHkBzGPqo5MbokMOr
|
85
|
+
GeTBoc1M1Dq2iMjz0GVhOr8Y9K8cVqnrlzjZICkfPyopR52KD2oSgUQCIdQ7Ohor
|
86
|
+
HkBDfZSgaQ78LvtS9v0uMtjLa73r
|
87
|
+
-----END CERTIFICATE-----
|
88
|
+
-----BEGIN CERTIFICATE-----
|
89
|
+
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
|
90
|
+
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
|
91
|
+
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
|
92
|
+
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
|
93
|
+
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
|
94
|
+
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
|
95
|
+
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
|
96
|
+
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
|
97
|
+
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
|
98
|
+
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
|
99
|
+
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
|
100
|
+
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
|
101
|
+
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
|
102
|
+
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
|
103
|
+
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
|
104
|
+
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
|
105
|
+
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
|
106
|
+
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
|
107
|
+
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
|
108
|
+
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
|
109
|
+
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
|
110
|
+
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
|
111
|
+
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
|
112
|
+
-----END CERTIFICATE-----
|
data/lib/forward.rb
CHANGED
@@ -1,4 +1,90 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
3
|
+
require 'logger'
|
4
|
+
require 'openssl'
|
5
|
+
require 'optparse'
|
6
|
+
require 'rbconfig'
|
7
|
+
require 'stringio'
|
8
|
+
require 'uri'
|
9
|
+
|
10
|
+
require 'highline/import'
|
11
|
+
require 'net/ssh'
|
12
|
+
|
13
|
+
require 'forward/core_extensions'
|
14
|
+
|
15
|
+
require 'forward/error'
|
16
|
+
require 'forward/api'
|
17
|
+
require 'forward/config'
|
18
|
+
require 'forward/tunnel'
|
19
|
+
require 'forward/client'
|
20
|
+
require 'forward/cli'
|
1
21
|
require 'forward/version'
|
2
22
|
|
3
23
|
module Forward
|
24
|
+
DEFAULT_SSL = true
|
25
|
+
DEFAULT_SSH_PORT = 22
|
26
|
+
DEFAULT_SSH_USER = 'tunnel'
|
27
|
+
|
28
|
+
# Returns either a ssh user set in the environment or a set default.
|
29
|
+
#
|
30
|
+
# Returns a String containing the ssh user.
|
31
|
+
def self.ssh_user
|
32
|
+
ENV['FORWARD_SSH_USER'] || DEFAULT_SSH_USER
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns either a ssh port set in the environment or a set default.
|
36
|
+
#
|
37
|
+
# Returns a String containing the ssh port.
|
38
|
+
def self.ssh_port
|
39
|
+
ENV['FORWARD_SSH_PORT'] || DEFAULT_SSH_PORT
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.client=(client)
|
43
|
+
@@client = client
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.client
|
47
|
+
defined?(@@client) && @@client
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.debug!
|
51
|
+
@@debug = true
|
52
|
+
logger.level = Logger::DEBUG
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.debug?
|
56
|
+
defined?(@@debug) && @@debug
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.stringio_log
|
60
|
+
@@stringio_log ||= StringIO.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.debug_remotely!
|
64
|
+
logger(stringio_log)
|
65
|
+
debug!
|
66
|
+
@@debug_remotely = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.debug_remotely?
|
70
|
+
defined?(@@debug_remotely) && @@debug_remotely
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.log(level, message)
|
74
|
+
logger.send(level, message) if debug?
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.logger(logdev = STDOUT)
|
78
|
+
@@logger ||= Logger.new(logdev)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns a string representing a detailed client version.
|
82
|
+
#
|
83
|
+
# Returns a String representing the client.
|
84
|
+
def self.client_string
|
85
|
+
os = RbConfig::CONFIG['host_os']
|
86
|
+
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
87
|
+
|
88
|
+
"[#{os}]::[#{engine}-#{RUBY_VERSION}]::[ruby-client-#{Forward::VERSION}]"
|
89
|
+
end
|
4
90
|
end
|
data/lib/forward/api.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'forward/api/resource'
|
2
|
+
require 'forward/api/client_log'
|
3
|
+
require 'forward/api/public_key'
|
4
|
+
require 'forward/api/tunnel'
|
5
|
+
require 'forward/api/user'
|
6
|
+
|
7
|
+
module Forward
|
8
|
+
module Api
|
9
|
+
class BadResponse < StandardError; end
|
10
|
+
class ResourceNotFound < StandardError; end
|
11
|
+
class ResourceError < StandardError
|
12
|
+
attr_reader :action, :errors
|
13
|
+
|
14
|
+
def initialize(action, json)
|
15
|
+
@action = action
|
16
|
+
@json = json
|
17
|
+
@errors = json[:errors]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
DEFAULT_API_HOST = 'https://forwardhq.com'
|
22
|
+
|
23
|
+
# Returns either an api host set in the environment or a set default.
|
24
|
+
#
|
25
|
+
# Returns a String containing the api host.
|
26
|
+
def self.uri
|
27
|
+
URI.parse(ENV['FORWARD_API_HOST'] || DEFAULT_API_HOST)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns True or False if we should be using ssl
|
31
|
+
#
|
32
|
+
# Returns a Boolean.
|
33
|
+
def self.ssl?
|
34
|
+
uri.scheme == 'https'
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.token=(token)
|
38
|
+
@@api_token = token
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.token
|
42
|
+
defined?(@@api_token) ? @@api_token : nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Forward
|
2
|
+
module Api
|
3
|
+
class ClientLog < Resource
|
4
|
+
|
5
|
+
def self.create(log)
|
6
|
+
resource = ClientLog.new(:create)
|
7
|
+
resource.uri = '/api/client_logs'
|
8
|
+
params = {
|
9
|
+
:client => Forward.client_string,
|
10
|
+
:log => log,
|
11
|
+
}
|
12
|
+
|
13
|
+
params[:user_id] = user_id unless user_id.nil?
|
14
|
+
|
15
|
+
resource.post(params)
|
16
|
+
end
|
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
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Forward
|
8
|
+
module Api
|
9
|
+
class Resource
|
10
|
+
|
11
|
+
attr_accessor :http
|
12
|
+
attr_accessor :uri
|
13
|
+
|
14
|
+
def initialize(action = nil)
|
15
|
+
@action = action
|
16
|
+
@http = Net::HTTP.new(Forward::Api.uri.host, Forward::Api.uri.port)
|
17
|
+
@http.use_ssl = Forward::Api.ssl?
|
18
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
19
|
+
@http.ca_file = File.expand_path('../../../../forwardhq.crt', __FILE__)
|
20
|
+
end
|
21
|
+
|
22
|
+
def request(method = :get, params = {})
|
23
|
+
log(:debug, "Request: [#{method.to_s.upcase}] for `#{http.address}:#{http.port}#{uri}'")
|
24
|
+
log(:debug, "Request: params `#{params.reject { |k,v| k == :password }.inspect }'")
|
25
|
+
build_request(method, params)
|
26
|
+
add_headers!
|
27
|
+
|
28
|
+
response = @http.request(@request)
|
29
|
+
|
30
|
+
parse_response(response)
|
31
|
+
rescue ResourceError => e
|
32
|
+
self.class.dispatch_error(e)
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_request(method, params = {})
|
36
|
+
@method = method
|
37
|
+
|
38
|
+
case @method
|
39
|
+
when :get
|
40
|
+
@request = Net::HTTP::Get.new(uri)
|
41
|
+
when :post
|
42
|
+
@request = Net::HTTP::Post.new(uri)
|
43
|
+
@request.body = params.to_json unless params.empty?
|
44
|
+
when :put
|
45
|
+
@request = Net::HTTP::Put.new(uri)
|
46
|
+
@request.body = params.to_json unless params.empty?
|
47
|
+
when :delete
|
48
|
+
@request = Net::HTTP::Delete.new(uri)
|
49
|
+
@request.body = params.to_json unless params.empty?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_headers!
|
54
|
+
if Forward::Api.token
|
55
|
+
@request['Authorization'] = "Token token=#{Forward::Api.token}"
|
56
|
+
end
|
57
|
+
|
58
|
+
@request['Content-Type'] = 'application/json'
|
59
|
+
@request['Accept'] = 'application/json'
|
60
|
+
end
|
61
|
+
|
62
|
+
def get(params = {})
|
63
|
+
@response = request(:get, params)
|
64
|
+
end
|
65
|
+
|
66
|
+
def post(params = {})
|
67
|
+
@response = request(:post, params)
|
68
|
+
end
|
69
|
+
|
70
|
+
def put(params = {})
|
71
|
+
@response = request(:put, params)
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete(params = {})
|
75
|
+
@response = request(:delete, params)
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_response(response)
|
79
|
+
log(:debug, "Response: [#{response.code}] `#{response.body}'")
|
80
|
+
|
81
|
+
if response.code.to_i == 404
|
82
|
+
raise ResourceNotFound
|
83
|
+
elsif response.code.to_i != 200
|
84
|
+
raise BadResponse, "response code was: #{response.code}"
|
85
|
+
elsif response['content-type'] !~ /^application\/json/
|
86
|
+
raise BadResponse, "response was not JSON, unable to parse"
|
87
|
+
end
|
88
|
+
|
89
|
+
json = JSON.parse(response.body)
|
90
|
+
|
91
|
+
if json.is_a? Hash
|
92
|
+
json.symbolize_keys!
|
93
|
+
raise ResourceError.new(@action, json) if json.has_key?(:errors)
|
94
|
+
end
|
95
|
+
|
96
|
+
json
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.dispatch_error(error)
|
100
|
+
Forward.log(:debug, "Dispatching ResourceError: action: #{error.action} errors: #{error.errors.inspect}")
|
101
|
+
method = :"#{error.action}_error"
|
102
|
+
|
103
|
+
if respond_to? method
|
104
|
+
send(method, error.errors)
|
105
|
+
else
|
106
|
+
Forward::Client.cleanup_and_exit!('An error occured, please contact support@forwardhq.com')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def log(level, message)
|
113
|
+
unless self.class.to_s == 'Forward::Api::ClientLog'
|
114
|
+
Forward.log(level, message)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|