forward 0.0.1 → 0.0.11

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/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
@@ -1,3 +1,8 @@
1
1
  #!/usr/bin/env rake
2
2
  require 'rake/testtask'
3
- require 'bundler/gem_tasks'
3
+ require 'bundler/gem_tasks'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
3
+
4
+ require 'forward'
5
+
6
+ Forward::CLI.run(ARGV)
@@ -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.description = 'Something'
8
- gem.summary = 'Something'
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|spec|features)/})
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
@@ -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-----
@@ -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
@@ -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,16 @@
1
+ module Forward
2
+ module Api
3
+ class PublicKey < Resource
4
+
5
+ def self.create
6
+ resource = PublicKey.new(:create)
7
+ resource.uri = '/api/public_keys'
8
+
9
+ response = resource.post
10
+
11
+ response[:private_key]
12
+ end
13
+
14
+ end
15
+ end
16
+ 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