stormmq-client 0.0.4

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.
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ #--
4
+ # Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
5
+ # All rights reserved.
6
+ #
7
+ # Please refer to the LICENSE file that accompanies this source
8
+ # for terms of use and redistribution.
9
+ #++
10
+
11
+ $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
12
+
13
+ require 'stormmq/secret_keys'
14
+ require 'stormmq/application'
15
+
16
+ class StormMQ::Application::URLSigner < StormMQ::Application
17
+
18
+ def initialize
19
+ synopsis "--help | --user <userName> --method <HttpMethod> --url <URL>"
20
+ options :help
21
+
22
+ option :names => %w(--user -u),
23
+ :opt_description => "a valid user name, i.e. the user name that you log into the site with",
24
+ :arity => [1,1],
25
+ :opt_found => get_args
26
+
27
+ option :names => %w(--method -m),
28
+ :opt_description => "the HTTP method to use - defaults to 'GET'",
29
+ :arity => [1,1],
30
+ :opt_found => get_args,
31
+ :opt_not_found => 'GET'
32
+
33
+ option :names => %w(--url -a),
34
+ :opt_description => "the URL to be signed",
35
+ :arity => [1,1],
36
+ :opt_found => get_args,
37
+ :opt_not_found => CommandLine::OptionParser::OPT_NOT_FOUND_BUT_REQUIRED
38
+ end
39
+
40
+ def main
41
+ puts self.url.canonicalise_and_sign(opt.user, self.secret_key(opt.user), opt['--method'])
42
+ end
43
+
44
+ def man
45
+ <<-EOM
46
+ NAME
47
+
48
+ #{File.basename(__FILE__)}
49
+
50
+ PURPOSE
51
+
52
+ Use this to experiment with the how signing URLs works if you need
53
+ a reference implementation.
54
+
55
+ USAGE
56
+
57
+ #{File.basename(__FILE__)} --help
58
+ #{File.basename(__FILE__)} --user <userName> --method <HttpMethod> --url <URL>
59
+
60
+ PARAMETERS
61
+
62
+ --user
63
+
64
+ The user name you log in to this site with.
65
+
66
+ --method
67
+
68
+ One of:
69
+
70
+ * GET
71
+ * HEAD
72
+ * OPTIONS
73
+ * PUT
74
+ * DELETE
75
+
76
+ POST is not yet supported.
77
+
78
+ --url
79
+
80
+ The unsigned URL to be signed.
81
+
82
+ --help, -h
83
+
84
+ Displays this help page then quits.
85
+
86
+ RESULT
87
+
88
+ Outputs a signed URL to STDOUT.
89
+
90
+ NOTES
91
+
92
+ Advanced use only - commandline subject to change.
93
+
94
+ COPYRIGHT
95
+
96
+ Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
97
+ All rights reserved.
98
+
99
+ EOM
100
+ end
101
+
102
+ def url
103
+ StormMQ::URL.new(opt.url)
104
+ end
105
+
106
+ def secret_key(user) # :nodoc:
107
+ StormMQ::SecretKeys.key_for(user)
108
+ end
109
+ end
110
+
111
+ StormMQ::Application::URLSigner.run
@@ -0,0 +1,92 @@
1
+ #--
2
+ # Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
3
+ # All rights reserved.
4
+ #
5
+ # Please refer to the LICENSE file that accompanies this source
6
+ # for terms of use and redistribution.
7
+ #++
8
+
9
+ require 'mq'
10
+ require 'pp'
11
+
12
+ module StormMQ
13
+
14
+ module AMQPClientImplementation
15
+
16
+ VERSION = '0.0.4'
17
+
18
+ def process_frame frame
19
+ if mq = channels[frame.channel]
20
+ mq.process_frame(frame)
21
+ return
22
+ end
23
+
24
+ case frame
25
+ when Frame::Method
26
+ case method = frame.payload
27
+ when Protocol::Connection::Start
28
+ send Protocol::Connection::StartOk.new(
29
+ {
30
+ :platform => 'Ruby/EventMachine',
31
+ :product => 'StormMQ AMQP',
32
+ :information => 'http://github.com/tonybyrne/StormMQ-Ruby-Client/',
33
+ :version => VERSION
34
+ },
35
+ 'AMQPLAIN',
36
+ {
37
+ :LOGIN => @settings[:user],
38
+ :PASSWORD => @settings[:password]
39
+ },
40
+ 'en_US'
41
+ )
42
+
43
+ when Protocol::Connection::Tune
44
+ send Protocol::Connection::TuneOk.new(
45
+ :channel_max => 0,
46
+ :frame_max => 131072,
47
+ :heartbeat => 0
48
+ )
49
+
50
+ send Protocol::Connection::Open.new(
51
+ :virtual_host => @settings[:vhost],
52
+ :capabilities => '',
53
+ :insist => @settings[:insist]
54
+ )
55
+
56
+ when Protocol::Connection::OpenOk
57
+ succeed(self)
58
+
59
+ when Protocol::Connection::Close
60
+ STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
61
+
62
+ when Protocol::Connection::CloseOk
63
+ @on_disconnect.call if @on_disconnect
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ module AMQPClient
70
+
71
+ def self.connect(options={})
72
+ AMQP.client = AMQPClientImplementation
73
+ AMQP.connect(
74
+ {
75
+ :vhost => self.vhost_from_options(options),
76
+ :host => 'amqp.stormmq.com',
77
+ :port => 443,
78
+ :ssl => true
79
+ }.merge(options)
80
+ )
81
+ end
82
+
83
+ def self.vhost_from_options(options)
84
+ vhost = "/#{options[:company]}/#{options[:system]}/#{options[:environment]}"
85
+ [:company, :system, :environment].each {|option| options.delete(option)}
86
+ vhost
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
@@ -0,0 +1,36 @@
1
+ #--
2
+ # Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
3
+ # All rights reserved.
4
+ #
5
+ # Please refer to the LICENSE file that accompanies this source
6
+ # for terms of use and redistribution.
7
+ #++
8
+
9
+ require 'stormmq/errors'
10
+ require 'stormmq/rest'
11
+ require 'commandline'
12
+ require 'commandline/optionparser'
13
+
14
+ module StormMQ
15
+ class Application < CommandLine::Application_wo_AutoRun
16
+
17
+ def initialize(*args)
18
+ author "Tony Byrne"
19
+ copyright "2010, Tony Byrne & StormMQ. All rights reserved."
20
+ super(*args)
21
+ end
22
+
23
+ def rest_client(user)
24
+ begin
25
+ Rest.new(:user => user)
26
+ rescue Error::SecretKeyNotProvidedError
27
+ raise "Could not find the secret key for user '#{user}' - please ensure it is present in the secret key file"
28
+ end
29
+ end
30
+
31
+ def self_test
32
+ 'Self test ouput goes here'
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ #--
2
+ # Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
3
+ # All rights reserved.
4
+ #
5
+ # Please refer to the LICENSE file that accompanies this source
6
+ # for terms of use and redistribution.
7
+ #++
8
+
9
+ module Base64
10
+
11
+ def urlsafe_decode64(str)
12
+ decode64(str.tr("-_", "+/"))
13
+ end
14
+
15
+ def urlsafe_encode64(bin)
16
+ encode64(bin).tr("+/", "-_")
17
+ end
18
+
19
+ end
@@ -0,0 +1,33 @@
1
+ #--
2
+ # Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
3
+ # All rights reserved.
4
+ #
5
+ # Please refer to the LICENSE file that accompanies this source
6
+ # for terms of use and redistribution.
7
+ #++
8
+
9
+ module StormMQ
10
+
11
+ module Error
12
+
13
+ class Base < StandardError
14
+ end
15
+
16
+ class LoadSecretKeysError < StormMQ::Error::Base
17
+ end
18
+
19
+ class SecretKeyNotFoundError < StormMQ::Error::Base
20
+ end
21
+
22
+ class SecretKeyNotProvidedError < StormMQ::Error::Base
23
+ end
24
+
25
+ class UserNotProvidedError < StormMQ::Error::Base
26
+ end
27
+
28
+ class InvalidURLError < StormMQ::Error::Base
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,160 @@
1
+ #--
2
+ # Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
3
+ # All rights reserved.
4
+ #
5
+ # Please refer to the LICENSE file that accompanies this source
6
+ # for terms of use and redistribution.
7
+ #++
8
+
9
+ require 'stormmq/url'
10
+ require 'stormmq/errors'
11
+ require 'stormmq/secret_keys'
12
+ require 'json'
13
+ require 'rest_client'
14
+
15
+ module StormMQ
16
+
17
+ API_HOST = ENV['STORMMQ_HOST'] || "api.stormmq.com"
18
+ API = ENV['STORMMQ_API'] || "api"
19
+ API_VERSION = ENV['STORMMQ_API_VERSION'] || "2009-01-01"
20
+
21
+ COMPANIES_PATH = '/companies/'
22
+ CLUSTERS_PATH = '/clusters'
23
+ APIS_PATH = '/'
24
+
25
+ # The Rest class implements the client for StormMQ's RESTful API. The API allows clients to access
26
+ # company, system and environment information.
27
+
28
+ class Rest
29
+
30
+ RestClient.proxy = ENV['http_proxy'] unless ENV['http_proxy'].nil?
31
+
32
+ def initialize(options={})
33
+ unless @user = options.delete(:user)
34
+ raise Error::UserNotProvidedError,
35
+ "could not determine the user name - either provide it via the :user param",
36
+ caller
37
+ end
38
+
39
+ unless @secret_key = options.delete(:secret_key) || self.class.secret_key_from_key_store(@user)
40
+ raise Error::SecretKeyNotProvidedError, "could not determine the secret key for user '#{@user}' - either provide it via the :secret_key param or ensure it is available in the secret key file",
41
+ caller
42
+ end
43
+
44
+ @host = options.delete(:host) || API_HOST
45
+ @api = options.delete(:api) || API
46
+ @version = options.delete(:version) || API_VERSION
47
+
48
+ @url_options = options
49
+ end
50
+
51
+ # Returns an Array of company indentifiers associated with the user.
52
+ def companies
53
+ signed_get(COMPANIES_PATH)
54
+ end
55
+
56
+ # Takes a String containing a valid company identifier and returns a
57
+ # Hash representation of the detailed information stored on the StormMQ
58
+ # system about the company.
59
+ def describe_company(company)
60
+ signed_get(COMPANIES_PATH, escape(company))
61
+ end
62
+
63
+ def systems(company)
64
+ signed_get(COMPANIES_PATH, escape(company), '/')
65
+ end
66
+
67
+ def create_system(company, system, json)
68
+ signed_put(json, COMPANIES_PATH, escape(company), escape(system))
69
+ end
70
+
71
+ def describe_system(company, system)
72
+ signed_get(COMPANIES_PATH, escape(company), escape(system))
73
+ end
74
+
75
+ def delete_system(company, system)
76
+ signed_delete(COMPANIES_PATH, escape(company), escape(system))
77
+ end
78
+
79
+ def clusters(company)
80
+ signed_get(CLUSTERS_PATH, escape(company))
81
+ end
82
+
83
+ def queues(company, system, environment)
84
+ signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'queues/')
85
+ end
86
+
87
+ def bindings(company, system, environment)
88
+ signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'bindings/')
89
+ end
90
+
91
+ def exchanges(company, system, environment)
92
+ signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'exchanges/')
93
+ end
94
+
95
+ def apis
96
+ signed_get(APIS_PATH)
97
+ end
98
+
99
+ def amqp_password(company, system, environment, amqp_user)
100
+ signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'users', escape(amqp_user))
101
+ end
102
+
103
+ def amqp_users(company, system, environment)
104
+ signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'users/')
105
+ end
106
+
107
+ private
108
+
109
+ def signed_get(*path_args)
110
+ get(build_signed_resource_url(make_normalised_path(*path_args)))
111
+ end
112
+
113
+ def signed_delete(*path_args)
114
+ delete(build_signed_resource_url(make_normalised_path(*path_args), 'DELETE'))
115
+ end
116
+
117
+ def signed_put(payload, *path_args)
118
+ put(build_signed_resource_url(make_normalised_path(*path_args), 'PUT'), payload)
119
+ end
120
+
121
+ def escape(string) # :nodoc:
122
+ StormMQ::URL.escape(string)
123
+ end
124
+
125
+ def get(signed_url) # :nodoc:
126
+ JSON.parse(RestClient.get(signed_url.to_s, {:accept => '*/*'}).to_s)
127
+ end
128
+
129
+ def delete(signed_url) # :nodoc:
130
+ JSON.parse(RestClient.delete(signed_url.to_s).to_s)
131
+ end
132
+
133
+ def put(signed_url, payload) # :nodoc:
134
+ JSON.parse(RestClient.put(signed_url.to_s, payload, {:accept => '*/*', :content_type => 'application/json'}).to_s)
135
+ end
136
+
137
+ def build_signed_resource_url(resource_path='/', method='GET') # :nodoc:
138
+ StormMQ::URL.new(
139
+ {
140
+ :host => @host,
141
+ :scheme => 'https',
142
+ :path => make_normalised_path(@api, @version, resource_path)
143
+ }.merge(@url_options)
144
+ ).canonicalise_and_sign(@user, @secret_key, method)
145
+ end
146
+
147
+ def make_normalised_path(*args) # :nodoc:
148
+ ('/' + args.join('/')).gsub(/\/+/,'/')
149
+ end
150
+
151
+ def self.secret_key_from_key_store(user) # :nodoc:
152
+ begin
153
+ SecretKeys.key_for(user)
154
+ rescue
155
+ end
156
+ end
157
+
158
+ end
159
+
160
+ end