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 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 ORM's on server:
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
- Plugins:
10
+ And the following plugins:
11
11
  - [WillPaginate](https://github.com/mislav/will_paginate)
12
12
 
13
- It is strongly recommended *NOT* to use this gem in production (yet).
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.secret = 'whatever'
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 < Vodka::Server::VodkaController
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.secret = 'whatever' # Same as server's
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` (same as `.all`, for WillPaginate compatibility)
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 as `#destroy`)
119
- - `#delete!` (acts as `#destroy!`)
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.secret = 'whatever'
7
+ c.request_secret = '8089c2189321798bf60df6b8c01bb661fb585080'
8
+ c.response_secret = '3ecb42ac23cd58994a6518971e7e31d2f4545e3c'
8
9
  end
9
10
  Vodka::Client.configure_her!
@@ -0,0 +1,6 @@
1
+ module Vodka
2
+ module Client
3
+ class SecurityException < ::Exception
4
+ end
5
+ end
6
+ end
@@ -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'
@@ -1,6 +1,6 @@
1
1
  module Vodka
2
2
  class Configuration
3
- attr_accessor :secret, :api_url
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
- env['HTTP_X_REQUEST_SIGNATURE'] == request_signature
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
- Digest::SHA512.hexdigest([
24
- request.scheme, request.host, request.port, request.path,
25
- env['HTTP_X_REQUEST_ID'], Vodka::Server.config.secret
26
- ].join)
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
- 403,
32
- { 'Content-Type' => 'application/json; charset=utf-8' },
33
- ['{"data":null,"errors":{"vodka_error":"403 Forbidden"},"metadata":{}}']
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
@@ -1,3 +1,3 @@
1
1
  module Vodka
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -1,3 +1,4 @@
1
1
  Vodka::Server.configure do |c|
2
- c.secret = 'whatever'
2
+ c.request_secret = '8089c2189321798bf60df6b8c01bb661fb585080'
3
+ c.response_secret = '3ecb42ac23cd58994a6518971e7e31d2f4545e3c'
3
4
  end
@@ -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.secret = 'invalid'
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.secret = 'whatever'
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.secret = 'whatever'
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.1
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