castle-rb 1.1.1 → 1.2.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/castle-rb/client.rb +12 -120
  3. data/lib/castle-rb/errors.rb +0 -1
  4. data/lib/castle-rb/ext/her.rb +1 -1
  5. data/lib/castle-rb/models/event.rb +0 -2
  6. data/lib/castle-rb/models/model.rb +2 -11
  7. data/lib/castle-rb/models/user.rb +0 -16
  8. data/lib/castle-rb/request.rb +0 -25
  9. data/lib/castle-rb/utils.rb +0 -1
  10. data/lib/castle-rb/version.rb +1 -1
  11. data/lib/castle-rb.rb +0 -9
  12. metadata +3 -37
  13. data/lib/castle-rb/models/backup_codes.rb +0 -4
  14. data/lib/castle-rb/models/challenge.rb +0 -7
  15. data/lib/castle-rb/models/monitoring.rb +0 -6
  16. data/lib/castle-rb/models/pairing.rb +0 -11
  17. data/lib/castle-rb/models/recommendation.rb +0 -3
  18. data/lib/castle-rb/models/session.rb +0 -6
  19. data/lib/castle-rb/models/trusted_device.rb +0 -5
  20. data/lib/castle-rb/session_store.rb +0 -35
  21. data/lib/castle-rb/session_token.rb +0 -39
  22. data/lib/castle-rb/token_store.rb +0 -30
  23. data/spec/fixtures/vcr_cassettes/challenge_create.yml +0 -48
  24. data/spec/fixtures/vcr_cassettes/challenge_verify.yml +0 -42
  25. data/spec/fixtures/vcr_cassettes/session_create.yml +0 -47
  26. data/spec/fixtures/vcr_cassettes/session_refresh.yml +0 -47
  27. data/spec/fixtures/vcr_cassettes/session_verify.yml +0 -47
  28. data/spec/fixtures/vcr_cassettes/user_find.yml +0 -44
  29. data/spec/fixtures/vcr_cassettes/user_find_non_existing.yml +0 -42
  30. data/spec/fixtures/vcr_cassettes/user_import.yml +0 -46
  31. data/spec/fixtures/vcr_cassettes/user_update.yml +0 -47
  32. data/spec/helpers_spec.rb +0 -38
  33. data/spec/jwt_spec.rb +0 -67
  34. data/spec/utils_spec.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 746bd18a03497a62f707772ea478816011e590f0
4
- data.tar.gz: bce6362eb4c5db47f08b5294af0fbf472b9b20d2
3
+ metadata.gz: 640bc0f13f0393b62ee5de5d1f47e43b9932f513
4
+ data.tar.gz: 47c64d6658e5b6f9674c5efde4a071391b6d3116
5
5
  SHA512:
6
- metadata.gz: 008277ce9a2c0d07c19c1920b2e97013c17fd40b4af40f1d4c325d46d39bccc4975b8c1b1a11a895084ec9a45ff9df9d23d0b9c17f59457db3fc32e342f6bf1c
7
- data.tar.gz: b68d88ec6d2fdeb8f44db528c9b56b509c1eac4707790932c993ba255d8018c0fd090d5d1335d2fe6d897c197e0b00cd9acb3fafa48e019c7dbc176ef5f9f8fa
6
+ metadata.gz: 947c9f2e92959262be33039e4de4c91f5a14b9da7c0f7488d090dadbd4fadfedf235ca96cd1954724d3f1ef0800a324a704ad0310bef95acc036598010f0eee2
7
+ data.tar.gz: 5cbea1e70be0616b133011aa5a77acc2b44962074eda994edf56457c62430c165ff452fd3dc3120e57cef637e87389df9542c1074fc3e0260497fd64b138fb2a
@@ -3,144 +3,36 @@ module Castle
3
3
 
4
4
  attr_accessor :request_context
5
5
 
6
- def self.install_proxy_methods(*names)
7
- names.each do |name|
8
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
9
- def #{name}(*args)
10
- Castle::User.new('$current').#{name}(*args)
11
- end
12
- RUBY
13
- end
14
- end
15
-
16
- install_proxy_methods :challenges, :events, :sessions, :pairings,
17
- :backup_codes, :generate_backup_codes, :trusted_devices,
18
- :enable_mfa!, :disable_mfa!
19
-
20
6
  def initialize(request, response, opts = {})
21
7
  # Save a reference in the per-request store so that the request
22
8
  # middleware in request.rb can access it
23
9
  RequestStore.store[:castle] = self
24
10
 
25
- if response.class.name == 'ActionDispatch::Cookies::CookieJar'
26
- cookies = Castle::CookieStore::Rack.new(response)
27
- else
28
- cookies = Castle::CookieStore::Base.new(request, response)
29
- end
30
-
31
- @store = Castle::TokenStore.new(cookies)
32
-
33
11
  @request_context = {
34
12
  ip: request.ip,
35
13
  user_agent: request.user_agent,
36
- cookie_id: cookies['__cid'] || '',
14
+ cookie_id: extract_cookies(request, response)['__cid'] || '',
37
15
  headers: header_string(request)
38
16
  }
39
17
  end
40
18
 
41
- def session_token
42
- @store.session_token
43
- end
44
-
45
- def session_token=(session_token)
46
- @store.session_token = session_token
47
- end
48
-
49
- def authorize!
50
- unless @store.session_token
51
- raise Castle::UserUnauthorizedError,
52
- 'Need to call login before authorize'
53
- end
54
-
55
- if @store.session_token.expired?
56
- Castle::Monitoring.heartbeat
57
- end
58
-
59
- if mfa_in_progress?
60
- logout
61
- raise Castle::UserUnauthorizedError,
62
- 'Logged out due to being unverified'
63
- end
64
-
65
- if mfa_required? && !device_trusted?
66
- raise Castle::ChallengeRequiredError
67
- end
68
- end
69
-
70
- def authorized?
71
- !!@store.session_token
72
- end
73
-
74
- def login(user_id, user_attrs = {})
75
- # Clear the session token if any
76
- @store.session_token = nil
77
-
78
- user = Castle::User.new(user_id.to_s)
79
- session = user.sessions.create(
80
- user: user_attrs, trusted_device_token: @store.trusted_device_token)
81
-
82
- # Set the session token for use in all subsequent requests
83
- @store.session_token = session.token
84
-
85
- session
19
+ def track(opts = {})
20
+ # Castle::Event.post('/v1/events', opts)
21
+ Castle::Event.create(opts)
86
22
  end
87
23
 
88
- def logout
89
- return unless @store.session_token
90
-
91
- # Destroy the current session specified in the session token
92
- begin
93
- sessions.destroy('$current')
94
- rescue Castle::ApiError # ignored
95
- end
96
-
97
- # Clear the session token
98
- @store.session_token = nil
99
- end
24
+ private
100
25
 
101
- def trust_device(attrs = {})
102
- unless @store.session_token
103
- raise Castle::UserUnauthorizedError,
104
- 'Need to call login before trusting device'
26
+ def extract_cookies(request, response)
27
+ # Extract the cookie set by the Castle Javascript
28
+ if response.class.name == 'ActionDispatch::Cookies::CookieJar'
29
+ Castle::CookieStore::Rack.new(response)
30
+ else
31
+ Castle::CookieStore::Base.new(request, response)
105
32
  end
106
- trusted_device = trusted_devices.create(attrs)
107
-
108
- # Set the session token for use in all subsequent requests
109
- @store.trusted_device_token = trusted_device.token
110
- end
111
-
112
- def mfa_enabled?
113
- @store.session_token ? @store.session_token.mfa_enabled? : false
114
33
  end
115
34
 
116
- def device_trusted?
117
- @store.session_token ? @store.session_token.device_trusted? : false
118
- end
119
-
120
- def mfa_in_progress?
121
- @store.session_token ? @store.session_token.mfa_in_progress? : false
122
- end
123
-
124
- def mfa_required?
125
- @store.session_token ? @store.session_token.mfa_required? : false
126
- end
127
-
128
- def has_default_pairing?
129
- @store.session_token ? @store.session_token.has_default_pairing? : false
130
- end
131
-
132
- def track(opts = {})
133
- Castle::Event.post('/v1/events', opts)
134
- end
135
-
136
- def recommendation(opts = {})
137
- Castle::Recommendation.get('/v1/recommendation', opts)
138
- end
139
-
140
- alias_method :recommend, :recommendation
141
-
142
- private
143
-
35
+ # Serialize HTTP headers
144
36
  def header_string(request)
145
37
  scrub_headers = ['Cookie']
146
38
 
@@ -13,4 +13,3 @@ class Castle::UserUnauthorizedError < Castle::ApiError; end
13
13
  class Castle::InvalidParametersError < Castle::ApiError; end
14
14
 
15
15
  class Castle::UnauthorizedError < Castle::ApiError; end
16
- class Castle::ChallengeRequiredError < Castle::ApiError; end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Add destroy to association: user.challenges.destroy(id)
2
+ # Add destroy to association: user.events.destroy(id)
3
3
  #
4
4
  module Her::Model::Associations
5
5
  class AssociationProxy
@@ -1,7 +1,5 @@
1
1
  module Castle
2
2
  class Event < Model
3
- collection_path "users/:user_id/events"
4
- belongs_to :user
5
3
  has_one :context
6
4
  end
7
5
  end
@@ -28,15 +28,6 @@ module Castle
28
28
  attrs
29
29
  end
30
30
 
31
- # Remove the auto-generated embedded User model to prevent recursion
32
- def to_json
33
- attrs = attributes
34
- if attrs['user'] && attrs['user']['id'] == '$current'
35
- attrs.delete 'user'
36
- end
37
- attrs.to_json
38
- end
39
-
40
31
  METHODS.each do |method|
41
32
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
42
33
  def self.instance_#{method}(action)
@@ -47,7 +38,7 @@ module Castle
47
38
 
48
39
  def self.instance_custom(method, action)
49
40
  #
50
- # Add method calls to association: user.challenges.verify(id, attributes)
41
+ # Add method calls to association: user.events.some_method(id, attributes)
51
42
  #
52
43
  AssociationProxy.class_eval <<-RUBY, __FILE__, __LINE__ + 1
53
44
  install_proxy_methods :association, :#{action}
@@ -59,7 +50,7 @@ module Castle
59
50
  RUBY
60
51
 
61
52
  #
62
- # Add method call to instance: user.enable_mfa
53
+ # Add method call to instance: user.enable_something
63
54
  #
64
55
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
65
56
  def #{action}(params={})
@@ -1,20 +1,4 @@
1
1
  module Castle
2
2
  class User < Model
3
- instance_post :enable_mfa!
4
- instance_post :disable_mfa!
5
-
6
- has_many :challenges
7
- has_many :events
8
- has_many :pairings
9
- has_many :sessions
10
- has_many :trusted_devices
11
-
12
- def backup_codes(params={})
13
- Castle::BackupCodes.get("/v1/users/#{id}/backup_codes", params)
14
- end
15
-
16
- def generate_backup_codes(params={})
17
- Castle::BackupCodes.post("/v1/users/#{id}/backup_codes", params)
18
- end
19
3
  end
20
4
  end
@@ -73,31 +73,6 @@ module Castle
73
73
  end
74
74
  end
75
75
 
76
- # Sends the active session token in a header, and extracts the returned
77
- # session token and sets it locally.
78
- #
79
- class SessionToken < Faraday::Middleware
80
- def call(env)
81
- castle = RequestStore.store[:castle]
82
- return @app.call(env) unless castle
83
-
84
- # get the session token from our local store
85
- if castle.session_token
86
- env[:request_headers]['X-Castle-Session-Token'] =
87
- castle.session_token.to_s
88
- end
89
-
90
- # call the API
91
- response = @app.call(env)
92
-
93
- # update the local store with the updated session token
94
- token = response.env.response_headers['X-Castle-set-session-token']
95
- castle.session_token = token if token
96
-
97
- response
98
- end
99
- end
100
-
101
76
  # Adds request context like IP address and user agent to any request.
102
77
  #
103
78
  class ContextHeaders < Faraday::Middleware
@@ -12,7 +12,6 @@ module Castle
12
12
  c.use Castle::Request::Middleware::RequestErrorHandler
13
13
  c.use Castle::Request::Middleware::EnvironmentHeaders
14
14
  c.use Castle::Request::Middleware::ContextHeaders
15
- c.use Castle::Request::Middleware::SessionToken
16
15
  c.use FaradayMiddleware::EncodeJson
17
16
  c.use Castle::Request::Middleware::JSONParser
18
17
  c.use Faraday::Adapter::NetHttp
@@ -1,3 +1,3 @@
1
1
  module Castle
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/castle-rb.rb CHANGED
@@ -12,11 +12,9 @@ require 'castle-rb/version'
12
12
  require 'castle-rb/configuration'
13
13
  require 'castle-rb/client'
14
14
  require 'castle-rb/errors'
15
- require 'castle-rb/token_store'
16
15
  require 'castle-rb/jwt'
17
16
  require 'castle-rb/utils'
18
17
  require 'castle-rb/request'
19
- require 'castle-rb/session_token'
20
18
 
21
19
  require 'castle-rb/support/cookie_store'
22
20
  require 'castle-rb/support/rails' if defined?(Rails::Railtie)
@@ -40,12 +38,5 @@ end
40
38
  require 'castle-rb/models/model'
41
39
  require 'castle-rb/models/account'
42
40
  require 'castle-rb/models/event'
43
- require 'castle-rb/models/challenge'
44
41
  require 'castle-rb/models/context'
45
- require 'castle-rb/models/monitoring'
46
- require 'castle-rb/models/pairing'
47
- require 'castle-rb/models/backup_codes'
48
- require 'castle-rb/models/recommendation'
49
- require 'castle-rb/models/session'
50
- require 'castle-rb/models/trusted_device'
51
42
  require 'castle-rb/models/user'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: castle-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Johan
7
+ - Johan Brissmyr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-17 00:00:00.000000000 Z
11
+ date: 2015-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: her
@@ -193,43 +193,21 @@ files:
193
193
  - lib/castle-rb/ext/her.rb
194
194
  - lib/castle-rb/jwt.rb
195
195
  - lib/castle-rb/models/account.rb
196
- - lib/castle-rb/models/backup_codes.rb
197
- - lib/castle-rb/models/challenge.rb
198
196
  - lib/castle-rb/models/context.rb
199
197
  - lib/castle-rb/models/event.rb
200
198
  - lib/castle-rb/models/model.rb
201
- - lib/castle-rb/models/monitoring.rb
202
- - lib/castle-rb/models/pairing.rb
203
- - lib/castle-rb/models/recommendation.rb
204
- - lib/castle-rb/models/session.rb
205
- - lib/castle-rb/models/trusted_device.rb
206
199
  - lib/castle-rb/models/user.rb
207
200
  - lib/castle-rb/request.rb
208
- - lib/castle-rb/session_store.rb
209
- - lib/castle-rb/session_token.rb
210
201
  - lib/castle-rb/support/cookie_store.rb
211
202
  - lib/castle-rb/support/padrino.rb
212
203
  - lib/castle-rb/support/rails.rb
213
204
  - lib/castle-rb/support/sinatra.rb
214
- - lib/castle-rb/token_store.rb
215
205
  - lib/castle-rb/utils.rb
216
206
  - lib/castle-rb/version.rb
217
- - spec/fixtures/vcr_cassettes/challenge_create.yml
218
- - spec/fixtures/vcr_cassettes/challenge_verify.yml
219
- - spec/fixtures/vcr_cassettes/session_create.yml
220
- - spec/fixtures/vcr_cassettes/session_refresh.yml
221
- - spec/fixtures/vcr_cassettes/session_verify.yml
222
- - spec/fixtures/vcr_cassettes/user_find.yml
223
- - spec/fixtures/vcr_cassettes/user_find_non_existing.yml
224
- - spec/fixtures/vcr_cassettes/user_import.yml
225
- - spec/fixtures/vcr_cassettes/user_update.yml
226
- - spec/helpers_spec.rb
227
- - spec/jwt_spec.rb
228
207
  - spec/models/challenge_spec.rb
229
208
  - spec/models/session_spec.rb
230
209
  - spec/models/user_spec.rb
231
210
  - spec/spec_helper.rb
232
- - spec/utils_spec.rb
233
211
  homepage: https://castle.io
234
212
  licenses:
235
213
  - MIT
@@ -255,19 +233,7 @@ signing_key:
255
233
  specification_version: 4
256
234
  summary: Castle
257
235
  test_files:
258
- - spec/fixtures/vcr_cassettes/challenge_create.yml
259
- - spec/fixtures/vcr_cassettes/challenge_verify.yml
260
- - spec/fixtures/vcr_cassettes/session_create.yml
261
- - spec/fixtures/vcr_cassettes/session_refresh.yml
262
- - spec/fixtures/vcr_cassettes/session_verify.yml
263
- - spec/fixtures/vcr_cassettes/user_find.yml
264
- - spec/fixtures/vcr_cassettes/user_find_non_existing.yml
265
- - spec/fixtures/vcr_cassettes/user_import.yml
266
- - spec/fixtures/vcr_cassettes/user_update.yml
267
- - spec/helpers_spec.rb
268
- - spec/jwt_spec.rb
269
236
  - spec/models/challenge_spec.rb
270
237
  - spec/models/session_spec.rb
271
238
  - spec/models/user_spec.rb
272
239
  - spec/spec_helper.rb
273
- - spec/utils_spec.rb
@@ -1,4 +0,0 @@
1
- module Castle
2
- class BackupCodes < Model
3
- end
4
- end
@@ -1,7 +0,0 @@
1
- module Castle
2
- class Challenge < Model
3
- collection_path "users/:user_id/challenges"
4
- has_one :pairing
5
- instance_post :verify
6
- end
7
- end
@@ -1,6 +0,0 @@
1
- module Castle
2
- class Monitoring < Model
3
- collection_path '/v1' # Her doesn't accept the empty string
4
- custom_post :heartbeat
5
- end
6
- end
@@ -1,11 +0,0 @@
1
- module Castle
2
- class Pairing < Model
3
- collection_path "users/:user_id/pairings"
4
- instance_post :verify
5
- instance_post :set_default!
6
- belongs_to :user
7
- has_one :config
8
- end
9
-
10
- class Config < Model; end
11
- end
@@ -1,3 +0,0 @@
1
- module Castle
2
- class Recommendation < Model; end
3
- end
@@ -1,6 +0,0 @@
1
- module Castle
2
- class Session < Model
3
- collection_path "users/:user_id/sessions"
4
- has_one :context
5
- end
6
- end
@@ -1,5 +0,0 @@
1
- module Castle
2
- class TrustedDevice < Model
3
- collection_path "users/:user_id/trusted_devices"
4
- end
5
- end
@@ -1,35 +0,0 @@
1
- module Castle
2
- class SessionStore
3
- class Rack < SessionStore
4
- def initialize(session)
5
- @session = session
6
- end
7
-
8
- def user_id
9
- @session['Castleuser_id']
10
- end
11
-
12
- def user_id=(value)
13
- @session['Castleuser_id'] = value
14
- end
15
-
16
- def read
17
- @session[key]
18
- end
19
-
20
- def write(value)
21
- @session[key] = value
22
- end
23
-
24
- def destroy
25
- @session.delete(key)
26
- end
27
-
28
- private
29
-
30
- def key
31
- "Castleuser.#{user_id}"
32
- end
33
- end
34
- end
35
- end
@@ -1,39 +0,0 @@
1
- require 'jwt'
2
-
3
- module Castle
4
- class SessionToken
5
- def initialize(token)
6
- if token
7
- @jwt = Castle::JWT.new(token)
8
- end
9
- end
10
-
11
- def to_s
12
- @jwt.to_token
13
- end
14
-
15
- def expired?
16
- @jwt.expired?
17
- end
18
-
19
- def device_trusted?
20
- @jwt.payload['tru'] == 1
21
- end
22
-
23
- def has_default_pairing?
24
- @jwt.payload['dpr'] > 0
25
- end
26
-
27
- def mfa_enabled?
28
- @jwt.payload['mfa'] == 1
29
- end
30
-
31
- def mfa_in_progress?
32
- @jwt.payload['chg'] == 1
33
- end
34
-
35
- def mfa_required?
36
- @jwt.payload['vfy'] > 0
37
- end
38
- end
39
- end
@@ -1,30 +0,0 @@
1
- module Castle
2
- class TokenStore
3
- def initialize(cookies)
4
- @cookies = cookies
5
- end
6
-
7
- def session_token
8
- token = @cookies['_ubs']
9
- Castle::SessionToken.new(token) if token
10
- end
11
-
12
- def session_token=(value)
13
- @cookies['_ubs'] = value
14
-
15
- if value && value != @cookies['_ubs']
16
- @cookies['_ubs']
17
- elsif !value
18
- @cookies['_ubs'] = nil
19
- end
20
- end
21
-
22
- def trusted_device_token
23
- @cookies['_ubt']
24
- end
25
-
26
- def trusted_device_token=(value)
27
- @cookies['_ubt'] = value
28
- end
29
- end
30
- end
@@ -1,48 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://:secretkey@api.castle.io/v1/users/dTxR68nzuRXT4wrB2HJ4hanYtcaGSz2y/challenges
6
- body:
7
- encoding: UTF-8
8
- string: "{}"
9
- headers:
10
- User-Agent:
11
- - Castle/v1 RubyBindings/1.0.4
12
- X-Castle-Client-User-Agent:
13
- - '{"bindings_version":"1.0.4","lang":"ruby","lang_version":"2.1.1 p76 (2014-02-24)","platform":"x86_64-darwin13.0","publisher":"castle","uname":"Darwin
14
- Johans-MacBook-Pro-2.local 13.2.0 Darwin Kernel Version 13.2.0: Thu Apr 17
15
- 23:03:13 PDT 2014; root:xnu-2422.100.13~1/RELEASE_X86_64 x86_64"}'
16
- Content-Type:
17
- - application/json
18
- Accept-Encoding:
19
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
20
- Accept:
21
- - "*/*"
22
- response:
23
- status:
24
- code: 201
25
- message: 'Created '
26
- headers:
27
- Content-Type:
28
- - application/json
29
- Content-Length:
30
- - '476'
31
- X-Ua-Compatible:
32
- - IE=Edge
33
- Etag:
34
- - '"ee1750920e2acc320adbd6826d5299be"'
35
- Cache-Control:
36
- - max-age=0, private, must-revalidate
37
- Server:
38
- - WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
39
- Date:
40
- - Sat, 07 Jun 2014 20:42:33 GMT
41
- Connection:
42
- - Keep-Alive
43
- body:
44
- encoding: UTF-8
45
- string: '{"id":"UWwy5FrWf9DTeoTpJz1LpBp4dPkWZ2Ne","created_at":"2014-06-07T20:42:33Z","channel":{"id":"Ff892rfGx3TwNF33sQUz3S51NsV24w7H","created_at":"2014-06-07T20:25:27Z","primary":true,"type":"token","token":{"id":"VVG3qirUxy8mUSkmzy3QpPcuhLN1JY4r","created_at":"2014-06-07T20:24:39Z","verified":true,"qr_url":"https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth%3A%2F%2Ftotp%2FCastle%3Abrissmyr%40gmail.com%3Fsecret%3Dxulnnls324pajcfn%26issuer%3DCastle"}}}'
46
- http_version:
47
- recorded_at: Sat, 07 Jun 2014 20:42:33 GMT
48
- recorded_with: VCR 2.9.0
@@ -1,42 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://:secretkey@api.castle.io/v1/challenges/UWwy5FrWf9DTeoTpJz1LpBp4dPkWZ2Ne/verify
6
- body:
7
- encoding: UTF-8
8
- string: '{"response":"000000"}'
9
- headers:
10
- User-Agent:
11
- - Castle/v1 RubyBindings/1.0.4
12
- X-Castle-Client-User-Agent:
13
- - '{"bindings_version":"1.0.4","lang":"ruby","lang_version":"2.1.1 p76 (2014-02-24)","platform":"x86_64-darwin13.0","publisher":"castle","uname":"Darwin
14
- Johans-MacBook-Pro-2.local 13.2.0 Darwin Kernel Version 13.2.0: Thu Apr 17
15
- 23:03:13 PDT 2014; root:xnu-2422.100.13~1/RELEASE_X86_64 x86_64"}'
16
- Content-Type:
17
- - application/json
18
- Accept-Encoding:
19
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
20
- Accept:
21
- - "*/*"
22
- response:
23
- status:
24
- code: 204
25
- message: 'No Content '
26
- headers:
27
- X-Ua-Compatible:
28
- - IE=Edge
29
- Cache-Control:
30
- - no-cache
31
- Server:
32
- - WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
33
- Date:
34
- - Sat, 07 Jun 2014 20:52:43 GMT
35
- Connection:
36
- - Keep-Alive
37
- body:
38
- encoding: UTF-8
39
- string: ''
40
- http_version:
41
- recorded_at: Sat, 07 Jun 2014 20:52:43 GMT
42
- recorded_with: VCR 2.9.0
@@ -1,47 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://:secretkey@api.castle.io/v1/users/user-2412/sessions
6
- body:
7
- encoding: UTF-8
8
- string: '{"user":{"email":"valid@example.com"}}'
9
- headers:
10
- User-Agent:
11
- - Faraday v0.9.0
12
- Content-Type:
13
- - application/json
14
- Accept-Encoding:
15
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
- Accept:
17
- - "*/*"
18
- response:
19
- status:
20
- code: 201
21
- message: 'Created '
22
- headers:
23
- Content-Type:
24
- - application/json
25
- Content-Length:
26
- - '716'
27
- X-Ua-Compatible:
28
- - IE=Edge
29
- Etag:
30
- - '"c323dc2244006efcc5629936b73749ff"'
31
- Cache-Control:
32
- - max-age=0, private, must-revalidate
33
- Server:
34
- - WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
35
- Date:
36
- - Wed, 07 May 2014 16:21:05 GMT
37
- Connection:
38
- - Keep-Alive
39
- Set-Cookie:
40
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
41
- body:
42
- encoding: UTF-8
43
- string: '{"id":"S2odxReZnGqhqxPaQ7V7kNkLoXkGZPFz","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlzcyI6InVzZXItMjQxMiIsInN1YiI6IlMyb2R4UmVabkdxaHF4UGFRN1Y3a05rTG9Ya0daUEZ6IiwiYXVkIjoiODAwMDAwMDAwMDAwMDAwIiwiZXhwIjoxMzk5NDc5Njc1LCJpYXQiOjEzOTk0Nzk2NjUsImp0aSI6MH0.eyJjaGFsbGVuZ2UiOnsiaWQiOiJUVENqd3VyM3lwbTRUR1ZwWU43cENzTXFxOW9mWEVBSCIsInR5cGUiOiJvdHBfYXV0aGVudGljYXRvciJ9fQ.LT9mUzJEbsizbFxcpMo3zbms0aCDBzfgMbveMGSi1-s","user":{"id":"8Fpmj9asxpq4mwGzx48MP1EQhdWUHDeb","local_id":"user-2412","created_at":"2014-05-07T15:33:58Z","updated_at":"2014-05-07T16:04:56Z","email":"valid@example.com","username":"valid@example.com","name":"New
44
- Name","first_name":null,"last_name":null,"image":null,"status":"ACTIVE","mfa_enabled":true}}'
45
- http_version:
46
- recorded_at: Wed, 07 May 2014 16:21:05 GMT
47
- recorded_with: VCR 2.9.0
@@ -1,47 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://:secretkey@api.castle.io/v1/sessions/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlzcyI6InVzZXItMjQxMiIsInN1YiI6IlMyb2R4UmVabkdxaHF4UGFRN1Y3a05rTG9Ya0daUEZ6IiwiYXVkIjoiODAwMDAwMDAwMDAwMDAwIiwiZXhwIjoxMzk5NDc5Njc1LCJpYXQiOjEzOTk0Nzk2NjUsImp0aSI6MH0.eyJjaGFsbGVuZ2UiOnsiaWQiOiJUVENqd3VyM3lwbTRUR1ZwWU43cENzTXFxOW9mWEVBSCIsInR5cGUiOiJvdHBfYXV0aGVudGljYXRvciJ9fQ.LT9mUzJEbsizbFxcpMo3zbms0aCDBzfgMbveMGSi1-s/refresh
6
- body:
7
- encoding: UTF-8
8
- string: '{"user":{"name":"New Name"}}'
9
- headers:
10
- User-Agent:
11
- - Faraday v0.9.0
12
- Content-Type:
13
- - application/json
14
- Accept-Encoding:
15
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
- Accept:
17
- - "*/*"
18
- response:
19
- status:
20
- code: 201
21
- message: 'Created '
22
- headers:
23
- Content-Type:
24
- - application/json
25
- Content-Length:
26
- - '716'
27
- X-Ua-Compatible:
28
- - IE=Edge
29
- Etag:
30
- - '"537ee28e9894002208f8f645f6dd2987"'
31
- Cache-Control:
32
- - max-age=0, private, must-revalidate
33
- Server:
34
- - WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
35
- Date:
36
- - Wed, 07 May 2014 16:21:55 GMT
37
- Connection:
38
- - Keep-Alive
39
- Set-Cookie:
40
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
41
- body:
42
- encoding: UTF-8
43
- string: '{"id":"S2odxReZnGqhqxPaQ7V7kNkLoXkGZPFz","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlzcyI6InVzZXItMjQxMiIsInN1YiI6IlMyb2R4UmVabkdxaHF4UGFRN1Y3a05rTG9Ya0daUEZ6IiwiYXVkIjoiODAwMDAwMDAwMDAwMDAwIiwiZXhwIjoxMzk5NDc5NzI1LCJpYXQiOjEzOTk0Nzk3MTUsImp0aSI6MX0.eyJjaGFsbGVuZ2UiOnsiaWQiOiJUVENqd3VyM3lwbTRUR1ZwWU43cENzTXFxOW9mWEVBSCIsInR5cGUiOiJvdHBfYXV0aGVudGljYXRvciJ9fQ.2UuUgl8jKkl1lhU0d2nSKmiw-Qzz130A9XJd7-swvv8","user":{"id":"8Fpmj9asxpq4mwGzx48MP1EQhdWUHDeb","local_id":"user-2412","created_at":"2014-05-07T15:33:58Z","updated_at":"2014-05-07T16:21:55Z","email":"valid@example.com","username":"valid@example.com","name":"New
44
- Name","first_name":null,"last_name":null,"image":null,"status":"ACTIVE","mfa_enabled":true}}'
45
- http_version:
46
- recorded_at: Wed, 07 May 2014 16:21:55 GMT
47
- recorded_with: VCR 2.9.0
@@ -1,47 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://:secretkey@api.castle.io/v1/sessions/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlzcyI6InVzZXItMjQxMiIsInN1YiI6IlMyb2R4UmVabkdxaHF4UGFRN1Y3a05rTG9Ya0daUEZ6IiwiYXVkIjoiODAwMDAwMDAwMDAwMDAwIiwiZXhwIjoxMzk5NDc5Njc1LCJpYXQiOjEzOTk0Nzk2NjUsImp0aSI6MH0.eyJjaGFsbGVuZ2UiOnsiaWQiOiJUVENqd3VyM3lwbTRUR1ZwWU43cENzTXFxOW9mWEVBSCIsInR5cGUiOiJvdHBfYXV0aGVudGljYXRvciJ9fQ.LT9mUzJEbsizbFxcpMo3zbms0aCDBzfgMbveMGSi1-s/verify
6
- body:
7
- encoding: UTF-8
8
- string: '{"response":"017010"}'
9
- headers:
10
- User-Agent:
11
- - Faraday v0.9.0
12
- Content-Type:
13
- - application/json
14
- Accept-Encoding:
15
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
- Accept:
17
- - "*/*"
18
- response:
19
- status:
20
- code: 201
21
- message: 'Created '
22
- headers:
23
- Content-Type:
24
- - application/json
25
- Content-Length:
26
- - '609'
27
- X-Ua-Compatible:
28
- - IE=Edge
29
- Etag:
30
- - '"27b68d405c62c2c4c2a23acafde76b26"'
31
- Cache-Control:
32
- - max-age=0, private, must-revalidate
33
- Server:
34
- - WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
35
- Date:
36
- - Wed, 07 May 2014 16:22:55 GMT
37
- Connection:
38
- - Keep-Alive
39
- Set-Cookie:
40
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
41
- body:
42
- encoding: UTF-8
43
- string: '{"id":"S2odxReZnGqhqxPaQ7V7kNkLoXkGZPFz","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlzcyI6InVzZXItMjQxMiIsInN1YiI6IlMyb2R4UmVabkdxaHF4UGFRN1Y3a05rTG9Ya0daUEZ6IiwiYXVkIjoiODAwMDAwMDAwMDAwMDAwIiwiZXhwIjoxMzk5NDc5NzI1LCJpYXQiOjEzOTk0Nzk3MTUsImp0aSI6MX0.e30.IRwMbLDd2LqRHB_QZvzG47eTr5vPCBrdWI6xPOPa6x4","user":{"id":"8Fpmj9asxpq4mwGzx48MP1EQhdWUHDeb","local_id":"user-2412","created_at":"2014-05-07T15:33:58Z","updated_at":"2014-05-07T16:21:55Z","email":"valid@example.com","username":"valid@example.com","name":"New
44
- Name","first_name":null,"last_name":null,"image":null,"status":"ACTIVE","mfa_enabled":true}}'
45
- http_version:
46
- recorded_at: Wed, 07 May 2014 16:22:55 GMT
47
- recorded_with: VCR 2.9.0
@@ -1,44 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: get
5
- uri: https://:secretkey@api.castle.io/v1/users/9RA2j3cYDxt8gefQUduKnxUxRRGy6Rz4
6
- body:
7
- encoding: US-ASCII
8
- string: ''
9
- headers:
10
- User-Agent:
11
- - Faraday v0.9.0
12
- Accept-Encoding:
13
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
- Accept:
15
- - '*/*'
16
- response:
17
- status:
18
- code: 200
19
- message: OK
20
- headers:
21
- Date:
22
- - Fri, 18 Apr 2014 23:16:44 GMT
23
- Status:
24
- - 200 OK
25
- Connection:
26
- - close
27
- Content-Type:
28
- - application/json
29
- Content-Length:
30
- - '328'
31
- Set-Cookie:
32
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
33
- X-Ua-Compatible:
34
- - IE=Edge
35
- Etag:
36
- - '"73f5664d599cfbc71df7850ca66cda3d"'
37
- Cache-Control:
38
- - max-age=0, private, must-revalidate
39
- body:
40
- encoding: UTF-8
41
- string: '{"id":"9RA2j3cYDxt8gefQUduKnxUxRRGy6Rz4","created_at":"2014-04-18T13:30:22Z","updated_at":"2014-04-18T13:33:51Z","email":"brissmyr@gmail.com","username":"brissmyr@gmail.com","name":null,"first_name":null,"last_name":null,"image":null,"status":"ACTIVE","email_verification_token":"NhFy8wBYccLsEoL-kAVt","identities":[]}'
42
- http_version:
43
- recorded_at: Fri, 18 Apr 2014 23:16:44 GMT
44
- recorded_with: VCR 2.9.0
@@ -1,42 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: get
5
- uri: https://:secretkey@api.castle.io/v1/users/non_existing
6
- body:
7
- encoding: US-ASCII
8
- string: ''
9
- headers:
10
- User-Agent:
11
- - Faraday v0.9.0
12
- Accept-Encoding:
13
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
- Accept:
15
- - '*/*'
16
- response:
17
- status:
18
- code: 404
19
- message: Not Found
20
- headers:
21
- Date:
22
- - Fri, 18 Apr 2014 23:29:27 GMT
23
- Status:
24
- - 404 Not Found
25
- Connection:
26
- - close
27
- Content-Type:
28
- - application/json
29
- Content-Length:
30
- - '42'
31
- Set-Cookie:
32
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
33
- X-Ua-Compatible:
34
- - IE=Edge
35
- Cache-Control:
36
- - no-cache
37
- body:
38
- encoding: UTF-8
39
- string: '{"type":"not_found","message":"Not found"}'
40
- http_version:
41
- recorded_at: Fri, 18 Apr 2014 23:29:27 GMT
42
- recorded_with: VCR 2.9.0
@@ -1,46 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://:secretkey@api.castle.io/v1/users/import
6
- body:
7
- encoding: UTF-8
8
- string: '{"users":[{"email":"10@example.com","username":"10"},{"email":"20@example.com","username":"20"}]}'
9
- headers:
10
- User-Agent:
11
- - Faraday v0.9.0
12
- Content-Type:
13
- - application/json
14
- Accept-Encoding:
15
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
- Accept:
17
- - '*/*'
18
- response:
19
- status:
20
- code: 201
21
- message: Created
22
- headers:
23
- Date:
24
- - Thu, 24 Apr 2014 16:19:14 GMT
25
- Status:
26
- - 201 Created
27
- Connection:
28
- - close
29
- Content-Type:
30
- - application/json
31
- Content-Length:
32
- - '85'
33
- Set-Cookie:
34
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
35
- X-Ua-Compatible:
36
- - IE=Edge
37
- Etag:
38
- - '"e6928187afa7cffb7f793a998b7a1c91"'
39
- Cache-Control:
40
- - max-age=0, private, must-revalidate
41
- body:
42
- encoding: UTF-8
43
- string: '[{"id":"fqnvfRSmxa6wqx3c6xmzDqDZrJCvtZ5P"},{"id":"4n9pcvrCSig2iVcZbk7pAMsjngzX9fgv"}]'
44
- http_version:
45
- recorded_at: Thu, 24 Apr 2014 16:19:14 GMT
46
- recorded_with: VCR 2.9.0
@@ -1,47 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: put
5
- uri: https://:secretkey@api.castle.io/v1/users/AKfwtfrAzdDKp55aty8o14MoudkaS9BL
6
- body:
7
- encoding: UTF-8
8
- string: '{"id":"AKfwtfrAzdDKp55aty8o14MoudkaS9BL","email":"updated@example.com","created_at":"2014-04-27
9
- 22:10:22 +0200"}'
10
- headers:
11
- User-Agent:
12
- - Faraday v0.9.0
13
- Content-Type:
14
- - application/json
15
- Accept-Encoding:
16
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
17
- Accept:
18
- - '*/*'
19
- response:
20
- status:
21
- code: 200
22
- message: OK
23
- headers:
24
- Date:
25
- - Sun, 27 Apr 2014 20:10:22 GMT
26
- Status:
27
- - 200 OK
28
- Connection:
29
- - close
30
- Content-Type:
31
- - application/json
32
- Content-Length:
33
- - '252'
34
- Set-Cookie:
35
- - _ubt=; expires=Thu, 01-Jan-1970 00:00:00 GMT
36
- X-Ua-Compatible:
37
- - IE=Edge
38
- Etag:
39
- - '"de56261eb3d3bef170cc8e837af079fd"'
40
- Cache-Control:
41
- - max-age=0, private, must-revalidate
42
- body:
43
- encoding: UTF-8
44
- string: '{"id":"AKfwtfrAzdDKp55aty8o14MoudkaS9BL","created_at":"2014-04-27T20:10:22Z","updated_at":"2014-04-27T20:10:22Z","email":"updated@example.com","username":"updated@example.com","name":null,"first_name":null,"last_name":null,"image":null,"status":"ACTIVE","identities":[]}'
45
- http_version:
46
- recorded_at: Sun, 27 Apr 2014 20:10:22 GMT
47
- recorded_with: VCR 2.9.0
data/spec/helpers_spec.rb DELETED
@@ -1,38 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Castle helpers' do
4
- let(:token) { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlhdCI6MTM5ODIzOTIwMywiZXhwIjoxMzk4MjQyODAzfQ.eyJ1c2VyX2lkIjoiZUF3djVIdGRiU2s4Yk1OWVpvanNZdW13UXlLcFhxS3IifQ.Apa7EmT5T1sOYz4Af0ERTDzcnUvSalailNJbejZ2ddQ' }
5
-
6
- xit 'creates a session' do
7
- Castle::Session.should_receive(:post).
8
- with("users/user%201234/sessions", user: {email: 'valid@example.com'}).
9
- and_return(Castle::Session.new(token: token))
10
- Castleauthenticate(nil, 'user 1234', properties: {email: 'valid@example.com'})
11
- end
12
-
13
- xit 'refreshes, and does not create a session' do
14
- Castle::Session.should_not_receive(:create)
15
- Castle::Session.any_instance.should_receive(:refresh).
16
- and_return(Castle::Session.new(token: token))
17
- opts = {
18
- user_id: '1234',
19
- properties: {
20
- email: 'valid@example.com'
21
- },
22
- context: {
23
- ip: '8.8.8.8',
24
- user_agent: 'Mozilla'
25
- }
26
- }
27
- Castleauthenticate(token, opts)
28
- end
29
-
30
- xit 'deauthenticates with context' do
31
- Castle::Session.should_receive(:destroy_existing)
32
-
33
- jwt = Castle::JWT.new(token)
34
- jwt.merge!(context: { ip: '8.8.8.8', user_agent: 'Mozilla' })
35
-
36
- Castledeauthenticate(jwt.to_token)
37
- end
38
- end
data/spec/jwt_spec.rb DELETED
@@ -1,67 +0,0 @@
1
- require 'spec_helper'
2
- require 'timecop'
3
-
4
- describe 'Castle::JWT' do
5
- let(:token) { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImlhdCI6MTM5ODIzOTIwMywiZXhwIjoxMzk4MjQyODAzfQ.eyJ1c2VyX2lkIjoiZUF3djVIdGRiU2s4Yk1OWVpvanNZdW13UXlLcFhxS3IifQ.Apa7EmT5T1sOYz4Af0ERTDzcnUvSalailNJbejZ2ddQ' }
6
-
7
- context 'valid JWT' do
8
- it 'returns a payload' do
9
- payload = Castle::JWT.new(token).to_json
10
- payload['user_id'].should == 'eAwv5HtdbSk8bMNYZojsYumwQyKpXqKr'
11
- end
12
-
13
- it 'verifies that JWT has expired' do
14
- new_time = Time.utc(2014, 4, 23, 8, 46, 44)
15
- Timecop.freeze(new_time) do
16
- Castle::JWT.new(token).should be_expired
17
- end
18
- end
19
-
20
- it 'verifies that JWT has not expired' do
21
- new_time = Time.utc(2014, 4, 23, 8, 46, 43)
22
- Timecop.freeze(new_time) do
23
- Castle::JWT.new(token).should_not be_expired
24
- end
25
- end
26
- end
27
-
28
- context 'invalid JWT' do
29
- let(:token) { 'eyJ0eXhtWWNTU3pEUHp6WFF2WmZp26mn7Kkl6UgE' }
30
-
31
- it 'throws error' do
32
- expect {
33
- payload = Castle::JWT.new(token).to_json
34
- }.to raise_error(Castle::SecurityError)
35
- end
36
- end
37
-
38
- context 'nil JWT' do
39
- let(:token) { nil }
40
-
41
- it 'throws error' do
42
- token = nil
43
- expect {
44
- payload = Castle::JWT.new(token).to_json
45
- }.to raise_error(Castle::SecurityError)
46
- end
47
- end
48
-
49
- context '#to_token' do
50
- it 'returns the same token' do
51
- Castle::JWT.new(token).to_token.should == token
52
- end
53
- end
54
-
55
- context '#merge' do
56
- it 'merges payload' do
57
- jwt = Castle::JWT.new(token)
58
- jwt.merge!(context: { ip: '8.8.8.8' })
59
-
60
- merged_token = jwt.to_token
61
- merged_token.should_not == token
62
- Castle::JWT.new(merged_token).
63
- to_json['context']['ip'].should == '8.8.8.8'
64
- end
65
- end
66
-
67
- end
data/spec/utils_spec.rb DELETED
@@ -1,59 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class MemoryStore < Castle::TokenStore
4
- def initialize
5
- @value = nil
6
- end
7
-
8
- def session_token
9
- @value['_ubs']
10
- end
11
-
12
- def session_token=(value)
13
- @value['_ubs'] = value
14
- end
15
-
16
- def trusted_device_token
17
- @value['_ubt']
18
- end
19
-
20
- def trusted_device_token=(value)
21
- @value['_ubt'] = value
22
- end
23
- end
24
-
25
- describe 'Castle utils' do
26
- describe 'ContextHeaders middleware' do
27
- before do
28
- Castle::User.use_api(api = Her::API.new)
29
- @user = api.setup do |c|
30
- c.use Castle::Request::Middleware::ContextHeaders
31
- c.use Her::Middleware::FirstLevelParseJSON
32
- c.adapter :test do |stub|
33
- stub.post('/users') do |env|
34
- @env = env
35
- [200, {}, [].to_json]
36
- end
37
- end
38
- end
39
- end
40
-
41
- let(:env) do
42
- Rack::MockRequest.env_for('/',
43
- "HTTP_USER_AGENT" => "Mozilla", "REMOTE_ADDR" => "8.8.8.8")
44
- end
45
-
46
- it 'handles non-existing context headers' do
47
- Castle::User.create()
48
- end
49
-
50
- it 'sets context headers from env' do
51
- request = Rack::Request.new(Rack::MockRequest.env_for('/',
52
- "HTTP_USER_AGENT" => "Mozilla", "REMOTE_ADDR" => "8.8.8.8"))
53
- Castle::Client.new(request, session_store: MemoryStore.new)
54
- Castle::User.create()
55
- @env['request_headers']['X-Castle-Ip'].should == '8.8.8.8'
56
- @env['request_headers']['X-Castle-User-Agent'].should == 'Mozilla'
57
- end
58
- end
59
- end