rabbitcage 0.1.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ rabbitcage.gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Dominik Sander
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,61 @@
1
+ # RabbitCage
2
+
3
+ **WARNING: This project is at a very early stage of development. The command line options and the config file format will most likely change in future versions.**
4
+
5
+ RabbitCage is a AMQP application firewall build on EventMachine. The code has been heavily inspired by mojombo's awesome [ProxyMachine](http://github.com/mojombo/proxymachine/).
6
+
7
+ RabbitCage was written because RabbitMQ's access control capabilities are rather limited.
8
+
9
+ RabbitCage works as a transparent, content aware proxy between the connecting client and a AMQP broker (currently only tested with RabbitMQ). Based on configured ACL-like rules RabbitCage will either forward or reject the message. Messages sent from the broker are forwarded directly to the client using EventMachine's [proxy incoming to](http://eventmachine.rubyforge.org/EventMachine/Connection.html#M000275), though it will just affect the client -> server performance.
10
+
11
+ ## Installation
12
+
13
+ sudo gem install rabbitcage
14
+
15
+ ## Running
16
+
17
+ Usage:
18
+ rabbitcage -c <config file> [-h <host>] [-p <port>]
19
+
20
+ Options:
21
+ -c, --config CONFIG Configuration file
22
+ -h, --host HOST Hostname to bind. Default 0.0.0.0
23
+ -p, --port PORT Port to listen on. Default 5672
24
+ -r, --remote-host HOST Hostname of the RabbitMQ server to connect to. Default 'localhost'
25
+ -x, --remote-port PORT Port of the RabbitMQ server to connect to. Default 5673
26
+ -v Verbose output (denied requests).
27
+ -V Very verbose output (denied requests/allowed requests).
28
+ -D Debug output (denied requests/allowed requests/debug info).
29
+
30
+ ## Example config file
31
+ # Basic syntax:
32
+ # allow|deny 'username'|:all, AMQP method|:all, AMQP class|:all, Hash of AMQP method properties
33
+ #
34
+ # This example will allow the admin user to perform any action on the broker.
35
+ # A guest is allowed to consume every exchange which name does not start with 'private_' and
36
+ # register every queue which name does not start with 'reserved_'
37
+ include RabbitCageACL
38
+ config do
39
+ allow 'admin', :all, :all
40
+ allow 'guest', :all, :queue, :name => /^(?!reserved_)/
41
+ allow 'guest', :all, :exchange, :name => /^(?!private_)/
42
+ allow 'guest', [:consume, :get], :basic
43
+ allow 'guest', :all, :connection
44
+ allow 'guest', :all, :channel
45
+ allow 'guest', :all, :access
46
+ default :deny
47
+ end
48
+
49
+ ## Note on Patches/Pull Requests
50
+
51
+ * Fork the project.
52
+ * Make your feature addition or bug fix.
53
+ * Add tests for it. This is important so I don't break it in a
54
+ future version unintentionally.
55
+ * Commit, do not mess with rakefile, version, or history.
56
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
57
+ * Send me a pull request. Bonus points for topic branches.
58
+
59
+ ## Copyright
60
+
61
+ Copyright (c) 2009 Dominik Sander. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rabbitcage"
8
+ gem.summary = %Q{A AMQP firewall which allows to restrict user access to RabbitMQ using ACLs.}
9
+ gem.description = %Q{RabbitMQ's access control capabilities are rather limited. RabbitCage enables fine-grated permission setups, you define which user can perform which AMQP method on which class.}
10
+ gem.email = "git@dsander.de"
11
+ gem.homepage = "http://github.com/dsander/rabbitcage"
12
+ gem.authors = ["Dominik Sander"]
13
+ gem.add_development_dependency "bacon", ">= 0"
14
+ gem.add_dependency "amqp", ">= 0.6.5"
15
+ gem.add_dependency "eventmachine", ">= 0.12.10"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.pattern = 'spec/**/*_spec.rb'
27
+ spec.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |spec|
33
+ spec.libs << 'spec'
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :spec => :check_dependencies
44
+
45
+ task :default => :spec
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "rabbitcage #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/rabbitcage ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
3
+ require 'rabbitcage'
4
+ require 'optparse'
5
+
6
+
7
+
8
+ begin
9
+ options = {:host => "0.0.0.0", :port => 5672, :remote_host => 'localhost', :remote_port => 5673, :log_level => Logger::ERROR}
10
+
11
+ opts = OptionParser.new do |opts|
12
+ opts.banner = <<-EOF
13
+ Usage:
14
+ rabbitcage -c <config file> [-h <host>] [-p <port>]
15
+
16
+ Options:
17
+ EOF
18
+ opts.on("-cCONFIG", "--config CONFIG", "Configuration file") do |x|
19
+ options[:config] = x
20
+ end
21
+
22
+ opts.on("-hHOST", "--host HOST", "Hostname to bind. Default 0.0.0.0") do |x|
23
+ options[:host] = x
24
+ end
25
+
26
+ opts.on("-pPORT", "--port PORT", "Port to listen on. Default 5672") do |x|
27
+ options[:port] = x
28
+ end
29
+
30
+ opts.on("-rHOST", "--remote-host HOST", "Hostname of the RabbitMQ server to connect to. Default 'localhost'") do |x|
31
+ options[:host] = x
32
+ end
33
+
34
+
35
+ opts.on("-xPORT", "--remote-port PORT", "Port of the RabbitMQ server to connect to. Default 5673") do |x|
36
+ options[:remote_port] = x
37
+ end
38
+
39
+ opts.on("-v", "Verbose output (denied requests).") do |x|
40
+ options[:log_level] = Logger::WARN
41
+ end
42
+
43
+ opts.on("-V", "Very verbose output (denied requests/allowed requests).") do |x|
44
+ options[:log_level] = Logger::INFO
45
+ end
46
+
47
+ opts.on("-D", "Debug output (denied requests/allowed requests/debug info).") do |x|
48
+ options[:log_level] = Logger::DEBUG
49
+ end
50
+
51
+ end
52
+
53
+ opts.parse!
54
+
55
+ load(options[:config])
56
+ name = options[:config].split('/').last.chomp('.rb')
57
+ RabbitCage.run('config', options[:host], options[:port], options[:remote_host], options[:remote_port], options[:log_level])
58
+ rescue Exception => e
59
+ if e.instance_of?(SystemExit)
60
+ raise
61
+ else
62
+ LOGGER.fatal 'Uncaught exception'
63
+ LOGGER.fatal e.message
64
+ LOGGER.fatal "\n"+e.backtrace.join("\n")
65
+ end
66
+ end
@@ -0,0 +1,95 @@
1
+ class RabbitCage
2
+ class ClientConnection < EventMachine::Connection
3
+ def self.start(host, port)
4
+ $server = EM.start_server(host, port, self)
5
+ LOGGER.info "Listening on #{host}:#{port}"
6
+ LOGGER.info "Send QUIT to quit after waiting for all connections to finish."
7
+ LOGGER.info "Send TERM or INT to quit after waiting for up to 10 seconds for connections to finish."
8
+ end
9
+
10
+ def post_init
11
+ LOGGER.info "Accepted #{peer}"
12
+ @buffer = []
13
+ @tries = 0
14
+ RabbitCage.incr
15
+ end
16
+
17
+ def peer
18
+ @peer ||=
19
+ begin
20
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
21
+ "#{ip}:#{port}"
22
+ end
23
+ end
24
+
25
+ def receive_data(data)
26
+ handle_data data
27
+ rescue => e
28
+ close_connection
29
+ LOGGER.error "#{e.class} - #{e.message}"
30
+ LOGGER.debug "\n#{e.backtrace.join("\n")}"
31
+ end
32
+
33
+ def handle_data(data)
34
+ @timer.cancel if @timer
35
+ data2 = data.dup
36
+
37
+ while frame = AMQP::Frame.parse(data2)
38
+ LOGGER.debug "Got frame: " + frame.payload.inspect
39
+ case frame.payload
40
+ when AMQP::Protocol::Connection::Open
41
+ @vhost = frame.payload.virtual_host
42
+ when AMQP::Protocol::Connection::StartOk
43
+ length = frame.payload.response[10].unpack('c').first
44
+ @user = frame.payload.response[11..10+length]
45
+ end
46
+
47
+ command = self.filter(frame.payload)
48
+ if command == :deny
49
+ LOGGER.warn generate_log_line(frame.payload, command) if frame.payload.class != AMQP::Protocol::Basic::Get
50
+ resp = AMQP::Protocol::Channel::Close.new(:reply_code => 403,
51
+ :reply_text => "ACCESS_REFUSED - access to '#{frame.payload.queue || frame.payload.exchange rescue 'the server'}' in vhost '#{@vhost}' refused for user '#{@user}' by rabbitcage",
52
+ :method_id => 10,
53
+ :class_id => 50,
54
+ ).to_frame
55
+ resp.channel = frame.channel
56
+ self.send_data resp.to_s
57
+ return
58
+ else
59
+ LOGGER.info generate_log_line(frame.payload, command) if frame.payload.class != AMQP::Protocol::Basic::Get
60
+ end
61
+ end
62
+ if @server_side || try_server_connect(RabbitCage.rabbit_host, RabbitCage.rabbit_port)
63
+ @server_side.send_data data
64
+ end
65
+ end
66
+
67
+ def try_server_connect(host, port)
68
+ @server_side = ServerConnection.request(host, port, self)
69
+ LOGGER.info "Successful connection to #{host}:#{port}."
70
+ true
71
+ rescue => e
72
+ @server_side = nil
73
+ if @tries < 10
74
+ @tries += 1
75
+ LOGGER.error "Failed on server connect attempt #{@tries}. Trying again..."
76
+ @timer.cancel if @timer
77
+ @timer = EventMachine::Timer.new(0.1) do
78
+ self.handle_data
79
+ end
80
+ else
81
+ LOGGER.error "Failed after ten connection attempts."
82
+ end
83
+ false
84
+ end
85
+
86
+ def unbind
87
+ @server_side.close_connection_after_writing if @server_side
88
+ RabbitCage.decr
89
+ end
90
+
91
+ def generate_log_line(payload, command)
92
+ "#{peer} #{command} #{payload.class.to_s[16..-1]}\tuser:#{@user} vh:#{@vhost} q:#{payload.respond_to?(:queue) ? payload.queue : 'nil'} ex:#{payload.respond_to?(:exchange) ? payload.exchange : 'nil'}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,20 @@
1
+
2
+ # ripped from active_record
3
+ class String
4
+ def camelize(first_letter_in_uppercase = true)
5
+ if first_letter_in_uppercase
6
+ self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
7
+ else
8
+ self.first + self.camelize[1..-1]
9
+ end
10
+ end
11
+ end
12
+ class Symbol
13
+ def camelize(first_letter_in_uppercase = true)
14
+ if first_letter_in_uppercase
15
+ self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
16
+ else
17
+ self.first + self.camelize[1..-1]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ module RabbitCageACL
4
+ def allow user, action, object, properties = {}
5
+ RabbitCage::Filter.register :allow, user, action, object, properties
6
+ end
7
+ def deny user, action, object, properties = {}
8
+ RabbitCage::Filter.register :deny, user, action, object, properties
9
+ end
10
+
11
+ def default action
12
+ RabbitCage::Filter.set_default(action)
13
+ end
14
+ end
15
+
16
+ class RabbitCage
17
+ class Filter
18
+ @rules = {}
19
+
20
+ def self.register permission, user, action, object, properties
21
+ available_actions = {:queue => ['Declare', 'Bind', 'Unbind', 'Delete', 'Purge'],
22
+ :connection => ['Start', 'StartOk', 'Open', 'Tune', 'TuneOk', 'Close', 'Redirect', 'Secure'],
23
+ :channel => ['Open', 'Flow', 'Alert', 'Close', 'CloseOk'],
24
+ :exchange => ['Declare', 'Delete'],
25
+ :access => ['Request'],
26
+ :basic => ['Qos', 'Consume', 'Cancel', 'Publish', 'Deliver', 'Get', 'Ack', 'Reject']
27
+ }
28
+ klass = []
29
+ if action.is_a? Array
30
+ klass = action.collect { |a| "AMQP::Protocol::#{object.camelize}::#{a.camelize}" }.join(',')
31
+ else
32
+ if action == :all && object != :all
33
+ klass = available_actions[object].collect { |x| "AMQP::Protocol::#{object.camelize}::#{x}" }
34
+ elsif action == :all && object == :all
35
+ klass = :any
36
+ elsif action != :all && object == :all
37
+ raise "This acl format is currently not supported"
38
+ exit
39
+ else
40
+ klass = "AMQP::Protocol::#{object.camelize}::#{action.camelize}"
41
+ end
42
+ end
43
+ klass = klass.join(', ') if klass.class == Array
44
+ @rules[klass] = [] unless @rules[klass].class == Array
45
+
46
+ name = properties.delete(:name)
47
+ properties[object] = name if name
48
+ cond = []
49
+ cond << ('@user =' << (user.is_a?(Regexp) ? "~ #{user.inspect}" : "= '#{user}'")) if user != :all
50
+
51
+ properties.each_pair do |p, value|
52
+ cond << ("frame.#{p} =" << (value.is_a?(Regexp) ? "~ #{value.inspect}" : "= '#{value}'"))
53
+ end
54
+
55
+ @rules[klass] << {:permission => permission, :user => user, :properties => cond.join(' and ') }
56
+ end
57
+
58
+ def self.generate_properties(properties, object)
59
+ properties
60
+ end
61
+
62
+ def self.set_default action
63
+ @default = action
64
+ end
65
+
66
+ def self.build
67
+ require 'erb'
68
+ method = ERB.new(%q[
69
+ def filter(frame)
70
+ <%- if @rules.any? -%>
71
+ <%- @rules[:any].each do |rule| -%>
72
+ return :<%= rule[:permission] %> if <%= rule[:properties] %>
73
+ <%- end -%>
74
+ case frame # 4
75
+ <%- @rules.each_pair do |klass, r|-%>
76
+ <%- next if klass == :any -%>
77
+ when <%= klass %>
78
+ <%- r.each do |rule| -%>
79
+ return :<%= rule[:permission] %> if <%= rule[:properties] %>
80
+ <%- end -%>
81
+ <%- end -%>
82
+ end
83
+ <%- end -%>
84
+ :<%= @default %>
85
+ end
86
+ ].gsub!(/^ /,''), nil, '>-%').result(binding)
87
+ LOGGER.debug "Generated filter method:\n#{method}"
88
+ RabbitCage::ClientConnection.class_eval method
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,19 @@
1
+ class RabbitCage
2
+ class ServerConnection < EventMachine::Connection
3
+ def self.request(host, port, client_side)
4
+ EventMachine.connect(host, port, self, client_side)
5
+ end
6
+
7
+ def initialize(conn)
8
+ @client_side = conn
9
+ end
10
+
11
+ def post_init
12
+ proxy_incoming_to(@client_side, 10240)
13
+ end
14
+
15
+ def unbind
16
+ @client_side.close_connection_after_writing
17
+ end
18
+ end
19
+ end
data/lib/rabbitcage.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'rubygems'
2
+ require 'eventmachine'
3
+ require 'amqp'
4
+ require 'logger'
5
+ require 'socket'
6
+
7
+ require 'rabbitcage/client_connection'
8
+ require 'rabbitcage/server_connection'
9
+ require 'rabbitcage/filter'
10
+ require 'rabbitcage/core_extensions'
11
+ LOGGER = Logger.new(STDOUT)
12
+ LOGGER.datetime_format = "%m-%d %H:%M:%S "
13
+
14
+ class RabbitCage
15
+ MAX_FAST_SHUTDOWN_SECONDS = 10
16
+
17
+ class << self
18
+ def update_procline
19
+ $0 = "rabbitcage #{VERSION} - #{@@name} #{@@listen} - #{self.stats} cur/max/tot conns"
20
+ end
21
+
22
+ def stats
23
+ "#{@@counter}/#{@@maxcounter}/#{@@totalcounter}"
24
+ end
25
+
26
+ def count
27
+ @@counter
28
+ end
29
+
30
+ def incr
31
+ @@totalcounter += 1
32
+ @@counter += 1
33
+ @@maxcounter = @@counter if @@counter > @@maxcounter
34
+ self.update_procline
35
+ @@counter
36
+ end
37
+
38
+ def decr
39
+ @@counter -= 1
40
+ if $server.nil?
41
+ LOGGER.info "Waiting for #{@@counter} connections to finish."
42
+ end
43
+ self.update_procline
44
+ EM.stop if $server.nil? and @@counter == 0
45
+ @@counter
46
+ end
47
+
48
+ def set_config(block)
49
+ @@config = block
50
+ end
51
+
52
+ def config
53
+ @@config
54
+ end
55
+
56
+ def rabbit_host
57
+ @@remote_host
58
+ end
59
+
60
+ def rabbit_port
61
+ @@remote_port
62
+ end
63
+ def build_filter
64
+ RabbitCage.config.call
65
+ @@filter = Filter.build
66
+ end
67
+
68
+ def filter frame
69
+ Filter.filter frame
70
+ end
71
+
72
+ def graceful_shutdown(signal)
73
+ EM.stop_server($server) if $server
74
+ LOGGER.info "Received #{signal} signal. No longer accepting new connections."
75
+ LOGGER.info "Waiting for #{RabbitCage.count} connections to finish."
76
+ $server = nil
77
+ EM.stop if RabbitCage.count == 0
78
+ end
79
+
80
+ def fast_shutdown(signal)
81
+ EM.stop_server($server) if $server
82
+ LOGGER.info "Received #{signal} signal. No longer accepting new connections."
83
+ LOGGER.info "Maximum time to wait for connections is #{MAX_FAST_SHUTDOWN_SECONDS} seconds."
84
+ LOGGER.info "Waiting for #{RabbitCage.count} connections to finish."
85
+ $server = nil
86
+ EM.stop if RabbitCage.count == 0
87
+ Thread.new do
88
+ sleep MAX_FAST_SHUTDOWN_SECONDS
89
+ exit!
90
+ end
91
+ end
92
+
93
+ def run(name, host, port, rhost, rport, log_level)
94
+ @@totalcounter = 0
95
+ @@maxcounter = 0
96
+ @@counter = 0
97
+ @@name = name
98
+ @@listen = "#{host}:#{port}"
99
+ @@remote_host = rhost
100
+ @@remote_port = rport
101
+ LOGGER.level = log_level
102
+
103
+ self.update_procline
104
+ EM.epoll
105
+
106
+ RabbitCage.build_filter
107
+
108
+ EM.run do
109
+ RabbitCage::ClientConnection.start(host, port)
110
+ trap('QUIT') do
111
+ self.graceful_shutdown('QUIT')
112
+ end
113
+ trap('TERM') do
114
+ self.fast_shutdown('TERM')
115
+ end
116
+ trap('INT') do
117
+ self.fast_shutdown('INT')
118
+ end
119
+ end
120
+ end
121
+
122
+ def version
123
+ yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
124
+ "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
125
+ rescue
126
+ 'unknown'
127
+ end
128
+ end
129
+
130
+ VERSION = self.version
131
+ end
132
+
133
+ module Kernel
134
+ def config(&block)
135
+ RabbitCage.set_config(block)
136
+ end
137
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Rabbitcage" do
4
+ it "fails" do
5
+ should.flunk "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'rabbitcage'
7
+
8
+ Bacon.summary_on_exit
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rabbitcage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dominik Sander
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-20 00:00:00 +01:00
13
+ default_executable: rabbitcage
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bacon
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: amqp
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.6.5
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: eventmachine
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.12.10
44
+ version:
45
+ description: RabbitMQ's access control capabilities are rather limited. RabbitCage enables fine-grated permission setups, you define which user can perform which AMQP method on which class.
46
+ email: git@dsander.de
47
+ executables:
48
+ - rabbitcage
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.markdown
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.markdown
59
+ - Rakefile
60
+ - VERSION
61
+ - bin/rabbitcage
62
+ - lib/rabbitcage.rb
63
+ - lib/rabbitcage/client_connection.rb
64
+ - lib/rabbitcage/core_extensions.rb
65
+ - lib/rabbitcage/filter.rb
66
+ - lib/rabbitcage/server_connection.rb
67
+ - spec/rabbitcage_spec.rb
68
+ - spec/spec_helper.rb
69
+ has_rdoc: true
70
+ homepage: http://github.com/dsander/rabbitcage
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options:
75
+ - --charset=UTF-8
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ version:
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.5
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: A AMQP firewall which allows to restrict user access to RabbitMQ using ACLs.
97
+ test_files:
98
+ - spec/rabbitcage_spec.rb
99
+ - spec/spec_helper.rb