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 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