vodka 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +28 -9
- data/lib/party.rb +2 -1
- data/lib/vodka/client/exceptions/security_exception.rb +6 -0
- data/lib/vodka/client/middleware/signed_request.rb +1 -4
- data/lib/vodka/client/middleware/signed_response.rb +39 -0
- data/lib/vodka/client.rb +2 -0
- data/lib/vodka/configuration.rb +2 -1
- data/lib/vodka/server/controllers/vodka_controller.rb +5 -1
- data/lib/vodka/server/handlers/response.rb +2 -0
- data/lib/vodka/server/middleware/signed_request.rb +27 -10
- data/lib/vodka/server/response.rb +5 -1
- data/lib/vodka/version.rb +1 -1
- data/spec/dummy/config/initializers/vodka_setup.rb +2 -1
- data/spec/middleware/error_aware_spec.rb +3 -2
- data/spec/middleware/signed_response_spec.rb +19 -0
- data/spec/spec_helper.rb +2 -1
- metadata +4 -1
data/README.md
CHANGED
@@ -3,14 +3,16 @@ Vodka makes communication easier. Always.
|
|
3
3
|
|
4
4
|
Vodka uses [Her](https://github.com/remiprev/her) as a REST client.
|
5
5
|
|
6
|
-
It currently supports
|
6
|
+
It currently supports these ORMs on server:
|
7
7
|
- [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord)
|
8
8
|
- [MongoMapper](https://github.com/jnunemaker/mongomapper)
|
9
9
|
|
10
|
-
|
10
|
+
And the following plugins:
|
11
11
|
- [WillPaginate](https://github.com/mislav/will_paginate)
|
12
12
|
|
13
|
-
|
13
|
+
Vodka supports I18n. If you change locale on client you may expect getting response from server in this locale.
|
14
|
+
|
15
|
+
Vodka signs all requests and responses so you can relax and hope nothing goes wrong. Or your sensitive data will be stolen by some Russian gangsters. Or there will be some man-in-the-middle that will attack you. Anyway, as long as you're not using SSL for all your requests you're screwed.
|
14
16
|
|
15
17
|
## Installation
|
16
18
|
Add this gem to both server and client application Gemfiles:
|
@@ -27,7 +29,8 @@ Add initializer `vodka_setup.rb` to `config/initializers`:
|
|
27
29
|
|
28
30
|
```ruby
|
29
31
|
Vodka::Server.configure do |c|
|
30
|
-
c.
|
32
|
+
c.request_secret = '8089c2189321798bf60df6b8c01bb661fb585080'
|
33
|
+
c.response_secret = '3ecb42ac23cd58994a6518971e7e31d2f4545e3c'
|
31
34
|
end
|
32
35
|
```
|
33
36
|
|
@@ -53,7 +56,7 @@ class ArticlesController < VodkaController
|
|
53
56
|
end
|
54
57
|
|
55
58
|
# comments_controller.rb
|
56
|
-
class CommentsController <
|
59
|
+
class CommentsController < VodkaController
|
57
60
|
def approve
|
58
61
|
vodka_response.success = resource.approve
|
59
62
|
respond_with_resource
|
@@ -98,9 +101,20 @@ Add initializer `vodka_setup.rb` to `config/initializers`:
|
|
98
101
|
```ruby
|
99
102
|
Vodka::Client.configure do |c|
|
100
103
|
c.api_url = 'https://api.myproject.org/vodka'
|
101
|
-
c.
|
104
|
+
c.request_secret = '8089c2189321798bf60df6b8c01bb661fb585080' # Same as server's
|
105
|
+
c.response_secret = '3ecb42ac23cd58994a6518971e7e31d2f4545e3c' # Same as server's
|
102
106
|
end
|
103
107
|
Vodka::Client.configure_her!
|
108
|
+
|
109
|
+
# Instead of calling Vodka::Client.configure_her! you may configure her yourself
|
110
|
+
# Her::API.setup(url: Vodka::Client.config.api_url) do |c|
|
111
|
+
# c.use Vodka::Client::Middleware::ErrorAware
|
112
|
+
# c.use Vodka::Client::Middleware::SignedRequest
|
113
|
+
# c.use Faraday::Request::UrlEncoded
|
114
|
+
# c.use Vodka::Client::Middleware::SignedResponse
|
115
|
+
# c.use Her::Middleware::SecondLevelParseJSON
|
116
|
+
# c.use Faraday::Adapter::NetHttp
|
117
|
+
# end
|
104
118
|
```
|
105
119
|
|
106
120
|
## Usage
|
@@ -108,12 +122,17 @@ After all the configuration is done, you can use your Her-applied models with al
|
|
108
122
|
|
109
123
|
Vodka adds some convinient methods to client and supports them on server:
|
110
124
|
- `.create!` (throws exception on error)
|
111
|
-
- `.paginate` (
|
125
|
+
- `.paginate` (returns WillPaginate-compatible collection)
|
112
126
|
- `.where` (supports chaining the way you expect)
|
127
|
+
- `.first`
|
128
|
+
- `.last`
|
113
129
|
- `#update_attribute`
|
114
130
|
- `#update_attribute!` (throws exception on error)
|
115
131
|
- `#update_attributes`
|
116
132
|
- `#update_attributes!` (throws exception on error)
|
117
133
|
- `#destroy!` (throws exception on error)
|
118
|
-
- `#delete` (acts
|
119
|
-
- `#delete!` (acts
|
134
|
+
- `#delete` (acts like `#destroy`)
|
135
|
+
- `#delete!` (acts like `#destroy!`)
|
136
|
+
|
137
|
+
## Is it secure? Should I use it in production?
|
138
|
+
Hell no. At least not yet.
|
data/lib/party.rb
CHANGED
@@ -4,6 +4,7 @@ require './spec/client/article'
|
|
4
4
|
|
5
5
|
Vodka::Client.configure do |c|
|
6
6
|
c.api_url = 'http://0.0.0.0:3000/vodka'
|
7
|
-
c.
|
7
|
+
c.request_secret = '8089c2189321798bf60df6b8c01bb661fb585080'
|
8
|
+
c.response_secret = '3ecb42ac23cd58994a6518971e7e31d2f4545e3c'
|
8
9
|
end
|
9
10
|
Vodka::Client.configure_her!
|
@@ -23,10 +23,7 @@ module Vodka
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def request_signature
|
26
|
-
Digest::SHA512.hexdigest([
|
27
|
-
env[:url].scheme, env[:url].host, env[:url].port, env[:url].path,
|
28
|
-
request_id, Vodka::Client.config.secret
|
29
|
-
].join)
|
26
|
+
Digest::SHA512.hexdigest([request_id, Vodka::Client.config.request_secret].join)
|
30
27
|
end
|
31
28
|
end
|
32
29
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Vodka
|
2
|
+
module Client
|
3
|
+
module Middleware
|
4
|
+
class SignedResponse < Faraday::Response::Middleware
|
5
|
+
attr_reader :env
|
6
|
+
|
7
|
+
def on_complete(env)
|
8
|
+
@env = env
|
9
|
+
raise SecurityException.new('Response ID mismatch') unless ids_match?
|
10
|
+
raise SecurityException.new('Invalid response signature') unless signature_valid?
|
11
|
+
end
|
12
|
+
|
13
|
+
def ids_match?
|
14
|
+
request_id == response_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def signature_valid?
|
18
|
+
response_signature == expected_response_signature
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_id
|
22
|
+
env[:request_headers]['X-Request-Id']
|
23
|
+
end
|
24
|
+
|
25
|
+
def response_id
|
26
|
+
env[:response_headers]['x-response-id']
|
27
|
+
end
|
28
|
+
|
29
|
+
def response_signature
|
30
|
+
env[:response_headers]['x-response-signature']
|
31
|
+
end
|
32
|
+
|
33
|
+
def expected_response_signature
|
34
|
+
Digest::SHA512.hexdigest([request_id, Vodka::Client.config.response_secret].join)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/vodka/client.rb
CHANGED
@@ -4,7 +4,9 @@ require 'vodka/client/exceptions/failed_action_exception'
|
|
4
4
|
require 'vodka/client/exceptions/forbidden_exception'
|
5
5
|
require 'vodka/client/exceptions/not_found_exception'
|
6
6
|
require 'vodka/client/exceptions/resource_exception'
|
7
|
+
require 'vodka/client/exceptions/security_exception'
|
7
8
|
require 'vodka/client/middleware/signed_request'
|
9
|
+
require 'vodka/client/middleware/signed_response'
|
8
10
|
require 'vodka/client/middleware/error_aware'
|
9
11
|
require 'vodka/her/extensions/extended_orm'
|
10
12
|
require 'vodka/her/extensions/will_paginate'
|
data/lib/vodka/configuration.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Vodka
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :request_secret, :response_secret, :api_url
|
4
4
|
end
|
5
5
|
|
6
6
|
module Configurable
|
@@ -17,6 +17,7 @@ module Vodka
|
|
17
17
|
c.use Vodka::Client::Middleware::ErrorAware
|
18
18
|
c.use Vodka::Client::Middleware::SignedRequest
|
19
19
|
c.use Faraday::Request::UrlEncoded
|
20
|
+
c.use Vodka::Client::Middleware::SignedResponse
|
20
21
|
c.use ::Her::Middleware::SecondLevelParseJSON
|
21
22
|
c.use Faraday::Adapter::NetHttp
|
22
23
|
end
|
@@ -7,7 +7,7 @@ module Vodka
|
|
7
7
|
include Handlers::Resource
|
8
8
|
include Handlers::Response
|
9
9
|
|
10
|
-
before_filter :fix_params_names, :set_locale
|
10
|
+
before_filter :set_response_id, :fix_params_names, :set_locale
|
11
11
|
before_filter :handle_not_found, only: [:show, :update, :destroy]
|
12
12
|
|
13
13
|
private
|
@@ -25,6 +25,10 @@ module Vodka
|
|
25
25
|
def handle_not_found
|
26
26
|
not_found if resource.nil? && vodka_response.success == false
|
27
27
|
end
|
28
|
+
|
29
|
+
def set_response_id
|
30
|
+
vodka_response.id = request.headers['X-Request-Id']
|
31
|
+
end
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
@@ -20,6 +20,8 @@ module Vodka
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def respond!
|
23
|
+
response.headers['X-Response-Id'] = vodka_response.id
|
24
|
+
response.headers['X-Response-Signature'] = vodka_response.signature
|
23
25
|
return render text: vodka_response.json, status: vodka_response.code, content_type: 'application/json'
|
24
26
|
end
|
25
27
|
|
@@ -16,22 +16,39 @@ module Vodka
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def request_signature_valid?
|
19
|
-
|
19
|
+
request_signature == expected_request_signature
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_id
|
23
|
+
env['HTTP_X_REQUEST_ID']
|
20
24
|
end
|
21
25
|
|
22
26
|
def request_signature
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
env['HTTP_X_REQUEST_SIGNATURE']
|
28
|
+
end
|
29
|
+
|
30
|
+
def expected_request_signature
|
31
|
+
Digest::SHA512.hexdigest([request_id, Vodka::Server.config.request_secret].join)
|
32
|
+
end
|
33
|
+
|
34
|
+
def response_signature
|
35
|
+
Digest::SHA512.hexdigest([request_id, Vodka::Server.config.response_secret].join)
|
27
36
|
end
|
28
37
|
|
29
38
|
def forbidden
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
39
|
+
headers = {
|
40
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
41
|
+
'X-Response-Id' => request_id,
|
42
|
+
'X-Response-Signature' => response_signature
|
43
|
+
}
|
44
|
+
data = {
|
45
|
+
data: nil,
|
46
|
+
errors: {
|
47
|
+
vodka_error: '403 Forbidden'
|
48
|
+
},
|
49
|
+
metadata: {}
|
50
|
+
}
|
51
|
+
[403, headers, [MultiJson.dump(data)]]
|
35
52
|
end
|
36
53
|
end
|
37
54
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Vodka
|
2
2
|
module Server
|
3
3
|
class Response
|
4
|
-
attr_accessor :code, :success, :data, :metadata, :errors
|
4
|
+
attr_accessor :id, :code, :success, :data, :metadata, :errors
|
5
5
|
|
6
6
|
def initialize
|
7
7
|
@code = 200
|
@@ -29,6 +29,10 @@ module Vodka
|
|
29
29
|
errors: errors_to_return
|
30
30
|
)
|
31
31
|
end
|
32
|
+
|
33
|
+
def signature
|
34
|
+
Digest::SHA512.hexdigest([id, Vodka::Server.config.response_secret].join)
|
35
|
+
end
|
32
36
|
end
|
33
37
|
end
|
34
38
|
end
|
data/lib/vodka/version.rb
CHANGED
@@ -2,13 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Vodka::Client::Middleware::ErrorAware do
|
4
4
|
it 'should throw exception for a forbidden response' do
|
5
|
-
Vodka::Client.config.
|
5
|
+
@old_secret = Vodka::Client.config.request_secret.dup
|
6
|
+
Vodka::Client.config.request_secret = 'invalid'
|
6
7
|
|
7
8
|
expect{
|
8
9
|
Article.first
|
9
10
|
}.to raise_exception(Vodka::Client::ForbiddenException, Vodka::Client::ForbiddenException::MESSAGE)
|
10
11
|
|
11
|
-
Vodka::Client.config.
|
12
|
+
Vodka::Client.config.request_secret = @old_secret
|
12
13
|
end
|
13
14
|
|
14
15
|
it 'should throw exception for when response was not found' do
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vodka::Client::Middleware::SignedResponse do
|
4
|
+
it 'raise exception if request id and response id dont match' do
|
5
|
+
Vodka::Client::Middleware::SignedResponse.any_instance.stub(:response_id).and_return('bullshit')
|
6
|
+
|
7
|
+
expect {
|
8
|
+
Article.first
|
9
|
+
}.to raise_exception(Vodka::Client::SecurityException, 'Response ID mismatch')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raise exception if response signature is invalid' do
|
13
|
+
Vodka::Client::Middleware::SignedResponse.any_instance.stub(:response_signature).and_return('bullshit')
|
14
|
+
|
15
|
+
expect {
|
16
|
+
Article.first
|
17
|
+
}.to raise_exception(Vodka::Client::SecurityException, 'Invalid response signature')
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,7 +8,8 @@ require 'client/article'
|
|
8
8
|
|
9
9
|
Vodka::Client.configure do |c|
|
10
10
|
c.api_url = 'http://0.0.0.0:3000/vodka'
|
11
|
-
c.
|
11
|
+
c.request_secret = '8089c2189321798bf60df6b8c01bb661fb585080'
|
12
|
+
c.response_secret = '3ecb42ac23cd58994a6518971e7e31d2f4545e3c'
|
12
13
|
end
|
13
14
|
Vodka::Client.configure_her!
|
14
15
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: vodka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0
|
5
|
+
version: 0.1.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Gregory Eremin
|
@@ -95,8 +95,10 @@ files:
|
|
95
95
|
- lib/vodka/client/exceptions/forbidden_exception.rb
|
96
96
|
- lib/vodka/client/exceptions/not_found_exception.rb
|
97
97
|
- lib/vodka/client/exceptions/resource_exception.rb
|
98
|
+
- lib/vodka/client/exceptions/security_exception.rb
|
98
99
|
- lib/vodka/client/middleware/error_aware.rb
|
99
100
|
- lib/vodka/client/middleware/signed_request.rb
|
101
|
+
- lib/vodka/client/middleware/signed_response.rb
|
100
102
|
- lib/vodka/configuration.rb
|
101
103
|
- lib/vodka/her/extensions/extended_orm.rb
|
102
104
|
- lib/vodka/her/extensions/will_paginate.rb
|
@@ -160,6 +162,7 @@ files:
|
|
160
162
|
- spec/middleware/error_aware_spec.rb
|
161
163
|
- spec/middleware/response_locale_spec.rb
|
162
164
|
- spec/middleware/signed_request_spec.rb
|
165
|
+
- spec/middleware/signed_response_spec.rb
|
163
166
|
- spec/spec_helper.rb
|
164
167
|
- vodka.gemspec
|
165
168
|
homepage: https://github.com/magnolia-fan/vodka
|