rabbitmq_http_auth_backend 1.0.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +19 -0
- data/README.md +342 -0
- data/lib/rabbitmq_http_auth_backend.rb +25 -0
- data/lib/rabbitmq_http_auth_backend/app.rb +39 -0
- data/lib/rabbitmq_http_auth_backend/app/response_formatter.rb +24 -0
- data/lib/rabbitmq_http_auth_backend/config.rb +117 -0
- data/lib/rabbitmq_http_auth_backend/config/runtime.rb +47 -0
- data/lib/rabbitmq_http_auth_backend/resolver.rb +53 -0
- data/lib/rabbitmq_http_auth_backend/resolver/runtime.rb +83 -0
- data/lib/rabbitmq_http_auth_backend/service.rb +13 -0
- data/lib/rabbitmq_http_auth_backend/version.rb +17 -0
- data/rabbitmq_http_auth_backend.gemspec +60 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 96a75df203d47f917752b3cef73ec78a6e3f6a570db3c1e92f778c380276ef1b
|
4
|
+
data.tar.gz: aee167ba2a0804d262a564b15a53bce8df4ccb17a243999750eec2e8cc8d59e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1946c3abd0c748a6cf11c16a71e52d0d16ad85ac338cd813a6f7625320511a89bda3549abe8a3827a137c9839c8492203071b319c07717217cf9859e0ad4ee8e
|
7
|
+
data.tar.gz: 273f0c29de8a6f6c31d8c2593e7d86bb21426e5da84c7079626766984eff535651079e3dac58202bec98b3989d7519a29cb01ef036548fa28d18acf52f38b489
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rabbitmq_http_auth_backend (1.0.0)
|
5
|
+
roda (~> 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.1.2)
|
11
|
+
diff-lcs (1.3)
|
12
|
+
method_source (0.9.2)
|
13
|
+
pry (0.12.2)
|
14
|
+
coderay (~> 1.1.0)
|
15
|
+
method_source (~> 0.9.0)
|
16
|
+
rack (2.0.7)
|
17
|
+
rack-test (1.1.0)
|
18
|
+
rack (>= 1.0, < 3)
|
19
|
+
rake (10.5.0)
|
20
|
+
roda (3.19.0)
|
21
|
+
rack
|
22
|
+
rspec (3.8.0)
|
23
|
+
rspec-core (~> 3.8.0)
|
24
|
+
rspec-expectations (~> 3.8.0)
|
25
|
+
rspec-mocks (~> 3.8.0)
|
26
|
+
rspec-core (3.8.0)
|
27
|
+
rspec-support (~> 3.8.0)
|
28
|
+
rspec-expectations (3.8.3)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.8.0)
|
31
|
+
rspec-mocks (3.8.0)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.8.0)
|
34
|
+
rspec-support (3.8.0)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 2.0)
|
41
|
+
pry (~> 0.12.2)
|
42
|
+
rabbitmq_http_auth_backend!
|
43
|
+
rack-test (~> 1.1.0)
|
44
|
+
rake (~> 10.0)
|
45
|
+
rspec (~> 3.0)
|
46
|
+
|
47
|
+
BUNDLED WITH
|
48
|
+
2.0.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright 2019 Stanko K.R. <hey@stanko.io>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,342 @@
|
|
1
|
+
# RabbitMQHttpAuthBackend
|
2
|
+
|
3
|
+
Mountable Rack application that implements a configurable API for RabbitMQ's
|
4
|
+
[rabbitmq-auth-backend-http](https://github.com/rabbitmq/rabbitmq-auth-backend-http).
|
5
|
+
|
6
|
+
## Purpose
|
7
|
+
|
8
|
+
RabbitMQ comes bundled with the [rabbitmq-auth-backend-http](https://github.com/rabbitmq/rabbitmq-auth-backend-http)
|
9
|
+
plug-in. The purpose of this plug-in is to authorize each action of an user
|
10
|
+
connected to RabbitMQ by asking a server over HTTP if the user is allowed to
|
11
|
+
do that action.
|
12
|
+
|
13
|
+
The plugin expects the server to implement four endpoints - one for
|
14
|
+
login (`/user`), one for vhost access, one for general resources (exchanges,
|
15
|
+
queues, topics) and one for topics.
|
16
|
+
|
17
|
+
Each endpoint has to respond with a custom format to either allow or deny the
|
18
|
+
action.
|
19
|
+
|
20
|
+
This library implements all of this as a mountable Rack application. Meaning,
|
21
|
+
after minimal configuration your application can implement the four required
|
22
|
+
endpoints and respond with correctly formated responses.
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
1. [Mounting the endpoint](#mounting-the-endpoint)
|
27
|
+
2. [Configuration](#configuration)
|
28
|
+
3. [Resolvers](#resolvers)
|
29
|
+
4. [Versioning](#versioning)
|
30
|
+
5. [Default configuration](#default-configuration)
|
31
|
+
|
32
|
+
### Mounting the endpoint
|
33
|
+
|
34
|
+
To use `RabbitMQHttpAuthBackend`, you will have to mount it within your
|
35
|
+
Rack application. This will expose it's endpoints from your application,
|
36
|
+
name spaced with by any prefix of your choosing.
|
37
|
+
|
38
|
+
The following are examples for some popular Rack based frameworks. Note that
|
39
|
+
`/rabbitmq/auth` is only a prefix and can be changed to whatever you desire.
|
40
|
+
|
41
|
+
For **Rails** applications, add the following line to your `routes.rb` file:
|
42
|
+
```ruby
|
43
|
+
# /config/routes.rb
|
44
|
+
Rails.application.routes.draw do
|
45
|
+
mount RabbitMQHttpAuthBackend.app => '/rabbitmq/auth'
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
For **Sinatra** applications, add the following to `config.ru`:
|
50
|
+
```ruby
|
51
|
+
# config.ru
|
52
|
+
map '/rabbitmq/auth' do
|
53
|
+
run RabbitMQHttpAuthBackend.app
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
For **Hanami** applications, add the following to `config/environment.rb`:
|
58
|
+
```ruby
|
59
|
+
Hanami.configure do
|
60
|
+
mount RabbitMQHttpAuthBackend.app, at: '/rabbitmq/auth'
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
For **Roda** applications, you have to call `run` from within your routing tree:
|
65
|
+
```ruby
|
66
|
+
class MyApp < Roda
|
67
|
+
route do |r|
|
68
|
+
r.on '/rabbitmq/auth' do
|
69
|
+
r.run RabbitMQHttpAuthBackend.app
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### Configuration
|
76
|
+
|
77
|
+
`RabbitMQHttpAuthBackend` can be configured to suite your needs. Both the
|
78
|
+
HTTP method as well as the names of all the endpoints are configurable in the
|
79
|
+
following manner.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
# /config/initializers/rabbitmq_http_auth_backend.rb
|
83
|
+
RabbitMQHttpAuthBackend.configure! do
|
84
|
+
http_method :post
|
85
|
+
|
86
|
+
user do
|
87
|
+
path '/anvandare'
|
88
|
+
end
|
89
|
+
|
90
|
+
vhost do
|
91
|
+
path '/vhost'
|
92
|
+
end
|
93
|
+
|
94
|
+
resource do
|
95
|
+
path '/resurs'
|
96
|
+
end
|
97
|
+
|
98
|
+
topic do
|
99
|
+
path '/amne'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
### Resolvers
|
105
|
+
|
106
|
+
Resolvers are used to determine whether or not a user is allowed access to a
|
107
|
+
particular resource. Resolvers are passed as part of the configuration. They
|
108
|
+
can be any callable object - any object that responds to a `call` method that
|
109
|
+
takes one argument (the params, a hash containing RabbitMQ query information).
|
110
|
+
|
111
|
+
The return value of the resolver can be either `:allow` or `:deny`. If
|
112
|
+
additional tags need to be returned alongside `:allow` return an `Array`
|
113
|
+
containing `:allow` and an `Array` of tags - e.g. `[:allow, ['admin']]`.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# /config/initializers/rabbitmq_http_auth_backend.rb
|
117
|
+
RabbitMQHttpAuthBackend.configure! do
|
118
|
+
http_method :post
|
119
|
+
|
120
|
+
user do
|
121
|
+
path '/anvandare'
|
122
|
+
resolver(lambda do |params|
|
123
|
+
if params['username'] == 'admin'
|
124
|
+
return :allow
|
125
|
+
end
|
126
|
+
|
127
|
+
:deny
|
128
|
+
end)
|
129
|
+
end
|
130
|
+
|
131
|
+
topic do
|
132
|
+
resolver TopicsResolver
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class TopicsResolver
|
137
|
+
def self.call(params)
|
138
|
+
if params['username'] == 'admin'
|
139
|
+
return :allow, ['admin', 'manager']
|
140
|
+
end
|
141
|
+
|
142
|
+
:deny
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
A "native" configuration DSL is also provided. The DSL provides utility methods
|
148
|
+
such as `username`, `password`, `vhost`, `resource`, `name`, `permission`, `ip`
|
149
|
+
and `routing_key`. Any utility methods `allow!` and `deny!` can be used to set
|
150
|
+
the return value (they don't have to be the return value).
|
151
|
+
Just note that they don't stop execution!
|
152
|
+
|
153
|
+
Not all methods return usable values for all resources. Here's a list:
|
154
|
+
* user
|
155
|
+
- `username`
|
156
|
+
- `password`
|
157
|
+
* vhost
|
158
|
+
- `username`
|
159
|
+
- `vhost`
|
160
|
+
- `ip`
|
161
|
+
* resource
|
162
|
+
- `username`
|
163
|
+
- `vhost`
|
164
|
+
- `resource` (can return `:exchange`, `:queue` or `:topic`)
|
165
|
+
- `name`
|
166
|
+
- `permission` (can return `:configure`, `:read` or `:write`)
|
167
|
+
* topic
|
168
|
+
- `username`
|
169
|
+
- `vhost`
|
170
|
+
- `resource` (can return `:topic`)
|
171
|
+
- `name`
|
172
|
+
- `permission` (can return `:configure`, `:read` or `:write`)
|
173
|
+
- `routing_key` (of the published message if the permission is `:write`, else of the queue binding)
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
# /config/initializers/rabbitmq_http_auth_backend.rb
|
177
|
+
RabbitMQHttpAuthBackend.configure! do
|
178
|
+
http_method :post
|
179
|
+
|
180
|
+
topic do
|
181
|
+
resolver do
|
182
|
+
if username == 'admin'
|
183
|
+
allow! ['admin', 'manager']
|
184
|
+
else
|
185
|
+
deny!
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
### Versioning
|
193
|
+
|
194
|
+
Everybody makes mistakes and changes their minds. Therefore this library
|
195
|
+
enables you to create multiple versions of itself and mount them.
|
196
|
+
|
197
|
+
There is little difference to the regular usage.
|
198
|
+
|
199
|
+
Mounting:
|
200
|
+
```ruby
|
201
|
+
# /config/routes.rb
|
202
|
+
Rails.application.routes.draw do
|
203
|
+
mount RabbitMQHttpAuthBackend.app(:v1) => '/rabbitmq/auth'
|
204
|
+
# ^^^^^
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
Configuration:
|
209
|
+
```ruby
|
210
|
+
# /config/initializers/rabbitmq_http_auth_backend.rb
|
211
|
+
RabbitMQHttpAuthBackend.configure!(:v1) do
|
212
|
+
# ^^^^^
|
213
|
+
# ...
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
If no version is given the `:default` or global configuration is edited.
|
218
|
+
|
219
|
+
### Default configuration
|
220
|
+
|
221
|
+
The global default configuration can be changed by altering the configuration
|
222
|
+
for the `:default` version.
|
223
|
+
|
224
|
+
Here is the full default configuration
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
# /config/initializers/rabbitmq_http_auth_backend.rb
|
228
|
+
RabbitMQHttpAuthBackend.configure!(:default) do
|
229
|
+
http_method :get
|
230
|
+
|
231
|
+
user do
|
232
|
+
path '/user'
|
233
|
+
resolver do
|
234
|
+
deny!
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
vhost do
|
239
|
+
path '/vhost'
|
240
|
+
resolver do
|
241
|
+
deny!
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
resource do
|
246
|
+
path '/resource'
|
247
|
+
resolver do
|
248
|
+
deny!
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
topic do
|
253
|
+
path '/topic'
|
254
|
+
resolver do
|
255
|
+
deny!
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
## Installation
|
262
|
+
|
263
|
+
Add this line to your application's Gemfile:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
# Gemfile
|
267
|
+
gem 'rabbitmq_http_auth_backend'
|
268
|
+
```
|
269
|
+
|
270
|
+
Configure the library:
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
# /config/initializers/rabbitmq_http_auth_backend.rb
|
274
|
+
RabbitMQHttpAuthBackend.configure!(:v1) do
|
275
|
+
http_method :get
|
276
|
+
|
277
|
+
user do
|
278
|
+
path '/user'
|
279
|
+
resolver do
|
280
|
+
deny!
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
vhost do
|
285
|
+
path '/vhost'
|
286
|
+
resolver do
|
287
|
+
deny!
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
resource do
|
292
|
+
path '/resource'
|
293
|
+
resolver do
|
294
|
+
deny!
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
topic do
|
299
|
+
path '/topic'
|
300
|
+
resolver do
|
301
|
+
deny!
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
```
|
306
|
+
|
307
|
+
Mount the application:
|
308
|
+
|
309
|
+
```ruby
|
310
|
+
# /config/routes.rb
|
311
|
+
Rails.application.routes.draw do
|
312
|
+
mount RabbitMQHttpAuthBackend.app => '/rabbitmq/auth'
|
313
|
+
end
|
314
|
+
```
|
315
|
+
|
316
|
+
## Development
|
317
|
+
|
318
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
319
|
+
Then, run `rake spec` to run the tests.
|
320
|
+
You can also run `bin/console` for an interactive prompt that will allow you
|
321
|
+
to experiment.
|
322
|
+
|
323
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
324
|
+
To release a new version, update the version number in `version.rb`, and then
|
325
|
+
run `bundle exec rake release`, which will create a git tag for the version,
|
326
|
+
push git commits and tags, and push the `.gem` file
|
327
|
+
to [rubygems.org](https://rubygems.org).
|
328
|
+
|
329
|
+
## Contributing
|
330
|
+
|
331
|
+
Bug reports and pull requests are welcome on GitHub at
|
332
|
+
https://github.com/monorkin/rabbitmq_http_auth_backend/.
|
333
|
+
|
334
|
+
## License
|
335
|
+
|
336
|
+
This software is licensed under the MIT license. A copy of the license
|
337
|
+
can be found in the [LICENSE.txt file](/LICENSE.txt) included with this
|
338
|
+
software.
|
339
|
+
|
340
|
+
**TL;DR** this software comes with absolutely no warranty of any kind.
|
341
|
+
You are free to redistribute and modify the software as long as the original
|
342
|
+
copyright notice is present in your derivative work.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
def self.configure!(version = nil, &block)
|
7
|
+
version ||= RabbitMQHttpAuthBackend::Config.default_configuration_key
|
8
|
+
RabbitMQHttpAuthBackend::Config.configuration[version] ||= {}
|
9
|
+
cfg = RabbitMQHttpAuthBackend::Config.configuration[version]
|
10
|
+
RabbitMQHttpAuthBackend::Config::Runtime.new(cfg).instance_eval(&block)
|
11
|
+
RabbitMQHttpAuthBackend::Config.version(version)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.app(version = nil)
|
15
|
+
version ||= RabbitMQHttpAuthBackend::Config.default_configuration_key
|
16
|
+
config = RabbitMQHttpAuthBackend::Config.new(version)
|
17
|
+
RabbitMQHttpAuthBackend::App.new(config).generate
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rabbitmq_http_auth_backend/version'
|
22
|
+
require 'rabbitmq_http_auth_backend/service'
|
23
|
+
require 'rabbitmq_http_auth_backend/config'
|
24
|
+
require 'rabbitmq_http_auth_backend/resolver'
|
25
|
+
require 'rabbitmq_http_auth_backend/app'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'roda'
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class App
|
5
|
+
RESOURCES = %i[user vhost resource topic].freeze
|
6
|
+
private_constant :RESOURCES
|
7
|
+
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate
|
15
|
+
# NOTE: config has to be bound to the local scope as a variable to be
|
16
|
+
# accessible from within the class that is being built
|
17
|
+
config = self.config
|
18
|
+
|
19
|
+
Class.new(Roda) do
|
20
|
+
route do |r|
|
21
|
+
RESOURCES.map do |resource|
|
22
|
+
r.on(config.fetch(resource, :path)) do
|
23
|
+
r.is do
|
24
|
+
r.public_send(config.http_method) do
|
25
|
+
result =
|
26
|
+
RabbitMQHttpAuthBackend::Resolver
|
27
|
+
.call(r.params, config.fetch(resource, :resolver))
|
28
|
+
ResponseFormatter.call(result)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end.last
|
33
|
+
end
|
34
|
+
end.freeze.app
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'rabbitmq_http_auth_backend/app/response_formatter'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class App
|
5
|
+
class ResponseFormatter < RabbitMQHttpAuthBackend::Service
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
action = response[0]
|
14
|
+
tags = response[1]
|
15
|
+
|
16
|
+
if action == :allow && tags
|
17
|
+
"#{action} #{tags.join(' ')}"
|
18
|
+
else
|
19
|
+
action.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class Config
|
5
|
+
DEFAULT_CFG_KEY = :default
|
6
|
+
private_constant :DEFAULT_CFG_KEY
|
7
|
+
|
8
|
+
DENY_PROC = (proc { deny! }).freeze
|
9
|
+
private_constant :DENY_PROC
|
10
|
+
|
11
|
+
DEFAULT_VALUES = {
|
12
|
+
# HTTP
|
13
|
+
http_method: :get,
|
14
|
+
# Paths
|
15
|
+
user_path: '/user',
|
16
|
+
vhost_path: '/vhost',
|
17
|
+
resource_path: '/resource',
|
18
|
+
topic_path: '/topic',
|
19
|
+
# Resolvers
|
20
|
+
user_resolver: DENY_PROC,
|
21
|
+
vhost_resolver: DENY_PROC,
|
22
|
+
resource_resolver: DENY_PROC,
|
23
|
+
topic_resolver: DENY_PROC
|
24
|
+
}.freeze
|
25
|
+
private_constant :DEFAULT_VALUES
|
26
|
+
|
27
|
+
def self.configuration
|
28
|
+
@configuration ||= { default_configuration_key => default_configuration }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.reset!
|
32
|
+
@configuration = { default_configuration_key => default_configuration }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.versions
|
36
|
+
configuration.keys
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.default_configuration_key
|
40
|
+
DEFAULT_CFG_KEY
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.default_configuration
|
44
|
+
{}.merge(DEFAULT_VALUES)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.version(version)
|
48
|
+
return unless versions.include?(version)
|
49
|
+
|
50
|
+
new(version)
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :version
|
54
|
+
|
55
|
+
def initialize(version = DEFAULT_CFG_KEY)
|
56
|
+
@version = version.to_sym
|
57
|
+
end
|
58
|
+
|
59
|
+
def http_method
|
60
|
+
data[:http_method]
|
61
|
+
end
|
62
|
+
|
63
|
+
def user_path
|
64
|
+
sanitize_path(data[:user_path])
|
65
|
+
end
|
66
|
+
|
67
|
+
def user_resolver
|
68
|
+
data[:user_resolver]
|
69
|
+
end
|
70
|
+
|
71
|
+
def vhost_path
|
72
|
+
sanitize_path(data[:vhost_path])
|
73
|
+
end
|
74
|
+
|
75
|
+
def vhost_resolver
|
76
|
+
data[:vhost_resolver]
|
77
|
+
end
|
78
|
+
|
79
|
+
def resource_path
|
80
|
+
sanitize_path(data[:resource_path])
|
81
|
+
end
|
82
|
+
|
83
|
+
def resource_resolver
|
84
|
+
data[:resource_resolver]
|
85
|
+
end
|
86
|
+
|
87
|
+
def topic_path
|
88
|
+
sanitize_path(data[:topic_path])
|
89
|
+
end
|
90
|
+
|
91
|
+
def topic_resolver
|
92
|
+
data[:topic_resolver]
|
93
|
+
end
|
94
|
+
|
95
|
+
def fetch(resource, element)
|
96
|
+
method = "#{resource}_#{element}".to_sym
|
97
|
+
return nil unless respond_to?(method)
|
98
|
+
public_send(method)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def data
|
104
|
+
@data ||= begin
|
105
|
+
defaults = self.class.configuration[DEFAULT_CFG_KEY]
|
106
|
+
cfg = self.class.configuration[version] || {}
|
107
|
+
defaults.merge(cfg).freeze
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def sanitize_path(path)
|
112
|
+
path.gsub(%r{^/}, '')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
require 'rabbitmq_http_auth_backend/config/runtime'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class Config
|
5
|
+
class Runtime
|
6
|
+
attr_reader :configuration
|
7
|
+
|
8
|
+
def initialize(config = nil, key = nil)
|
9
|
+
@configuration =
|
10
|
+
config || RabbitMQHttpAuthBackend::Config.default_configuration
|
11
|
+
@key = key
|
12
|
+
end
|
13
|
+
|
14
|
+
def http_method(method)
|
15
|
+
configuration[:http_method] = method.to_s.downcase.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def user(&block)
|
19
|
+
self.class.new(configuration, :user).instance_eval(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def vhost(&block)
|
23
|
+
self.class.new(configuration, :vhost).instance_eval(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def resource(&block)
|
27
|
+
self.class.new(configuration, :resource).instance_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def topic(&block)
|
31
|
+
self.class.new(configuration, :topic).instance_eval(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def path(path)
|
35
|
+
configuration["#{key}_path".to_sym] = path
|
36
|
+
end
|
37
|
+
|
38
|
+
def resolver(resolver = nil, &block)
|
39
|
+
configuration["#{key}_resolver".to_sym] = resolver || block
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
attr_reader :key
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class Resolver < Service
|
5
|
+
class Error < RabbitMQHttpAuthBackend::Error; end
|
6
|
+
class NoResolverError < Error; end
|
7
|
+
class NonCallableResolverError < Error; end
|
8
|
+
class InvalidResponseError < Error; end
|
9
|
+
|
10
|
+
attr_reader :params
|
11
|
+
attr_reader :resolver
|
12
|
+
|
13
|
+
def initialize(params, resolver)
|
14
|
+
@params = params
|
15
|
+
@resolver = resolver || raise(NoResolverError)
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
response = generate_response!
|
20
|
+
validate_response!(response)
|
21
|
+
response
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def generate_response!
|
27
|
+
if resolver.is_a?(Proc) && resolver.arity.zero?
|
28
|
+
runtime = Runtime.new(params)
|
29
|
+
runtime.instance_eval(&resolver)
|
30
|
+
build_response(runtime)
|
31
|
+
elsif resolver.respond_to?(:call)
|
32
|
+
Array(resolver.call(params))
|
33
|
+
else
|
34
|
+
raise(NonCallableResolverError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_response!(response)
|
39
|
+
raise(InvalidResponseError) unless response.is_a?(Array)
|
40
|
+
raise(InvalidResponseError) unless %I[allow deny].include?(response.first)
|
41
|
+
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_response(runtime)
|
46
|
+
symbol = runtime.allowed? ? :allow : :deny
|
47
|
+
tags = runtime.tags
|
48
|
+
[symbol, tags].compact
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
require 'rabbitmq_http_auth_backend/resolver/runtime'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RabbitMQHttpAuthBackend
|
4
|
+
class Resolver
|
5
|
+
class Runtime
|
6
|
+
class Error < RabbitMQHttpAuthBackend::Error; end
|
7
|
+
class InvalidResourceError < Error; end
|
8
|
+
class InvalidPermissionError < Error; end
|
9
|
+
|
10
|
+
attr_reader :params
|
11
|
+
attr_accessor :tags
|
12
|
+
attr_accessor :_allowed
|
13
|
+
|
14
|
+
def initialize(params)
|
15
|
+
@params = params
|
16
|
+
self.tags = nil
|
17
|
+
self._allowed = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def allow!(tags = nil)
|
21
|
+
self._allowed = true
|
22
|
+
self.tags = tags
|
23
|
+
end
|
24
|
+
|
25
|
+
def deny!
|
26
|
+
self._allowed = false
|
27
|
+
self.tags = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def allowed?
|
31
|
+
_allowed == true
|
32
|
+
end
|
33
|
+
|
34
|
+
def denied?
|
35
|
+
!allowed?
|
36
|
+
end
|
37
|
+
|
38
|
+
def username
|
39
|
+
params['username']
|
40
|
+
end
|
41
|
+
|
42
|
+
def password
|
43
|
+
params['password']
|
44
|
+
end
|
45
|
+
|
46
|
+
def vhost
|
47
|
+
params['vhost']
|
48
|
+
end
|
49
|
+
|
50
|
+
def resource
|
51
|
+
@resource ||=
|
52
|
+
case params['resource']
|
53
|
+
when 'exchange' then :exchange
|
54
|
+
when 'queue' then :queue
|
55
|
+
when 'topic' then :topic
|
56
|
+
else raise(InvalidResourceError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def name
|
61
|
+
params['name']
|
62
|
+
end
|
63
|
+
|
64
|
+
def permission
|
65
|
+
@permission ||=
|
66
|
+
case params['permission']
|
67
|
+
when 'configure' then :configure
|
68
|
+
when 'write' then :write
|
69
|
+
when 'read' then :read
|
70
|
+
else raise(InvalidPermissionError)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def ip
|
75
|
+
params['ip']
|
76
|
+
end
|
77
|
+
|
78
|
+
def routing_key
|
79
|
+
params['routing_key']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'rabbitmq_http_auth_backend/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'rabbitmq_http_auth_backend'
|
9
|
+
spec.version = RabbitMQHttpAuthBackend::Version.to_s
|
10
|
+
spec.authors = ['Stanko K.R.']
|
11
|
+
spec.email = ['me@stanko.io']
|
12
|
+
|
13
|
+
spec.summary = 'Mountable Rack application that implements a configurable '\
|
14
|
+
"API for RabbitMQ's rabbitmq-auth-backend-http"
|
15
|
+
spec.description = spec.summary
|
16
|
+
spec.homepage = 'https://github.com/monorkin/rabbitmq_http_auth_backend'
|
17
|
+
spec.licenses = %w[MIT]
|
18
|
+
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the
|
20
|
+
# 'allowed_push_host' to allow pushing to a single host or delete this
|
21
|
+
# section to allow pushing to any host.
|
22
|
+
if spec.respond_to?(:metadata)
|
23
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
24
|
+
|
25
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
26
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
27
|
+
spec.metadata['changelog_uri'] = 'https://github.com/'\
|
28
|
+
'monorkin/rabbitmq_http_auth_backend'\
|
29
|
+
'/blob/master/CHANGELOG.md'
|
30
|
+
else
|
31
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
32
|
+
'public gem pushes.'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Specify which files should be added to the gem when it is released.
|
36
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
37
|
+
# into git.
|
38
|
+
IGNORED_FILES = [
|
39
|
+
'.gitignore', '.rspec', '.travis.yml', 'Makefile', 'Rakefile',
|
40
|
+
'CHANGELOG.md', /^bin\/.*/
|
41
|
+
].map { |p| Regexp.new(p) }
|
42
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
43
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
44
|
+
f.match(%r{^(test|spec|features)/})
|
45
|
+
end
|
46
|
+
end.reject { |f| IGNORED_FILES.any? { |r| r.match?(f) } }
|
47
|
+
|
48
|
+
|
49
|
+
spec.bindir = 'exe'
|
50
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
51
|
+
spec.require_paths = ['lib']
|
52
|
+
|
53
|
+
spec.add_dependency 'roda', '~> 3.0'
|
54
|
+
|
55
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
56
|
+
spec.add_development_dependency 'pry', '~> 0.12.2'
|
57
|
+
spec.add_development_dependency 'rack-test', '~> 1.1.0'
|
58
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
59
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rabbitmq_http_auth_backend
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stanko K.R.
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-05-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: roda
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.12.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.12.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.1.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.1.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description: Mountable Rack application that implements a configurable API for RabbitMQ's
|
98
|
+
rabbitmq-auth-backend-http
|
99
|
+
email:
|
100
|
+
- me@stanko.io
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- lib/rabbitmq_http_auth_backend.rb
|
110
|
+
- lib/rabbitmq_http_auth_backend/app.rb
|
111
|
+
- lib/rabbitmq_http_auth_backend/app/response_formatter.rb
|
112
|
+
- lib/rabbitmq_http_auth_backend/config.rb
|
113
|
+
- lib/rabbitmq_http_auth_backend/config/runtime.rb
|
114
|
+
- lib/rabbitmq_http_auth_backend/resolver.rb
|
115
|
+
- lib/rabbitmq_http_auth_backend/resolver/runtime.rb
|
116
|
+
- lib/rabbitmq_http_auth_backend/service.rb
|
117
|
+
- lib/rabbitmq_http_auth_backend/version.rb
|
118
|
+
- rabbitmq_http_auth_backend.gemspec
|
119
|
+
homepage: https://github.com/monorkin/rabbitmq_http_auth_backend
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata:
|
123
|
+
allowed_push_host: https://rubygems.org
|
124
|
+
homepage_uri: https://github.com/monorkin/rabbitmq_http_auth_backend
|
125
|
+
source_code_uri: https://github.com/monorkin/rabbitmq_http_auth_backend
|
126
|
+
changelog_uri: https://github.com/monorkin/rabbitmq_http_auth_backend/blob/master/CHANGELOG.md
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubygems_version: 3.0.1
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: Mountable Rack application that implements a configurable API for RabbitMQ's
|
146
|
+
rabbitmq-auth-backend-http
|
147
|
+
test_files: []
|