castle-rb 3.4.2 → 3.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84dd494b50643e628c57809953e2bb59fa25a5a550dd8b1d58bb712e11a81604
4
- data.tar.gz: 56e6bb84867531074995a550573e9790e9d5a6638931ac77fb83c590cbbc5204
3
+ metadata.gz: 6376098ba7a5b711d926fe187c38d2ed31a68b7ddf53024f2d51a81a14482604
4
+ data.tar.gz: b4ca1f4dbd10607b5c5bfad6a9b7528d3ebb8ebf7a2b6fd1287af08333b110d4
5
5
  SHA512:
6
- metadata.gz: cb4fca490621a4e3349ce70a7036e62a4fa695d45964614308eb34a2024afdb3e333402fc10468e9e66fae24a972388366c078a665e41c73136068021687b7cf
7
- data.tar.gz: 640e52eed4f09cc4060fe6e40e38d2500849640133671fe171fb35e589df0aee27134431630e16bfd03b76b3239f6f3b5fce8b427f468b2d7a3139ce1d29b8f1
6
+ metadata.gz: 70c04aa5f1d0f3f4af5789a7aad877380b3d82fafbd935ec746bd40aab904c110bea4410d161c48521fee108a5212c7d81b8a00b465b82940e7f6f9c48236f26
7
+ data.tar.gz: 345383199ba415d699f97f1ebf242d21dc8e1eb921a4c60f6366464a1e6bc5b5ac0680d7010e6f2b4b9ff5366393c9fd9b70237951ac7b013328e05af3af11b7
data/README.md CHANGED
@@ -128,7 +128,7 @@ track_options = ::Castle::Client.to_options({
128
128
  properties: {
129
129
  key: 'value'
130
130
  },
131
- traits: {
131
+ user_traits: {
132
132
  key: 'value'
133
133
  }
134
134
  })
@@ -17,6 +17,7 @@ module Castle
17
17
 
18
18
  def to_options(options = {})
19
19
  options[:timestamp] ||= Castle::Utils::Timestamp.call
20
+ warn '[DEPRECATION] use user_traits instead of traits key' if options.key?(:traits)
20
21
  options
21
22
  end
22
23
 
@@ -80,7 +81,9 @@ module Castle
80
81
  options = Castle::Utils.deep_symbolize_keys(options || {})
81
82
  add_timestamp_if_necessary(options)
82
83
  command = Castle::Commands::Impersonate.new(@context).build(options)
83
- @api.request(command)
84
+ @api.request(command).tap do |response|
85
+ raise Castle::ImpersonationFailed unless response[:success]
86
+ end
84
87
  end
85
88
 
86
89
  def disable_tracking
@@ -65,8 +65,8 @@ module Castle
65
65
  /^(\w+)=$/ =~ method_name
66
66
  end
67
67
 
68
- def method_missing(m, *_args)
69
- raise Castle::ConfigurationError, "there is no such a config #{m}"
68
+ def method_missing(setting, *_args)
69
+ raise Castle::ConfigurationError, "there is no such a config #{setting}"
70
70
  end
71
71
  end
72
72
  end
@@ -4,7 +4,7 @@ module Castle
4
4
  module Context
5
5
  class Default
6
6
  def initialize(request, cookies = nil)
7
- @client_id = Extractors::ClientId.new(request, cookies || request.cookies).call('__cid')
7
+ @client_id = Extractors::ClientId.new(request, cookies || request.cookies).call
8
8
  @headers = Extractors::Headers.new(request).call
9
9
  @request_ip = Extractors::IP.new(request).call
10
10
  end
@@ -26,4 +26,7 @@ module Castle
26
26
  class UnauthorizedError < Castle::ApiError; end
27
27
  # all internal server errors
28
28
  class InternalServerError < Castle::ApiError; end
29
+
30
+ # impersonation command failed
31
+ class ImpersonationFailed < Castle::ApiError; end
29
32
  end
@@ -9,9 +9,8 @@ module Castle
9
9
  @cookies = cookies || {}
10
10
  end
11
11
 
12
- def call(name)
13
- @cookies[name] ||
14
- @request.env.fetch('HTTP_X_CASTLE_CLIENT_ID', '')
12
+ def call
13
+ @request.env['HTTP_X_CASTLE_CLIENT_ID'] || @cookies['__cid'] || ''
15
14
  end
16
15
  end
17
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castle
4
- VERSION = '3.4.2'
4
+ VERSION = '3.5.0'
5
5
  end
@@ -36,13 +36,14 @@ describe Castle::Client do
36
36
  let(:time_now) { Time.now }
37
37
  let(:time_auto) { time_now.utc.iso8601(3) }
38
38
  let(:time_user) { (Time.now - 10_000).utc.iso8601(3) }
39
+ let(:response_body) { {}.to_json }
39
40
 
40
41
  before do
41
42
  Timecop.freeze(time_now)
42
43
  stub_const('Castle::VERSION', '2.2.0')
43
44
  stub_request(:any, /api.castle.io/).with(
44
45
  basic_auth: ['', 'secret']
45
- ).to_return(status: 200, body: '{}', headers: {})
46
+ ).to_return(status: 200, body: response_body, headers: {})
46
47
  end
47
48
 
48
49
  after { Timecop.return }
@@ -65,8 +66,8 @@ describe Castle::Client do
65
66
  end
66
67
 
67
68
  describe 'to_options' do
68
- let(:options) { { user_id: '1234', traits: { name: 'Jo' } } }
69
- let(:result) { { user_id: '1234', traits: { name: 'Jo' }, timestamp: time_auto } }
69
+ let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
70
+ let(:result) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_auto } }
70
71
 
71
72
  it do
72
73
  expect(described_class.to_options(options)).to eql(result)
@@ -79,29 +80,36 @@ describe Castle::Client do
79
80
  { user_id: '1234', timestamp: time_auto, sent_at: time_auto,
80
81
  impersonator: impersonator, context: context }
81
82
  end
83
+ let(:response_body) { { success: true }.to_json }
82
84
  let(:options) { { user_id: '1234', impersonator: impersonator } }
83
85
 
84
- before { client.impersonate(options) }
85
-
86
86
  context 'when used with symbol keys' do
87
+ before { client.impersonate(options) }
88
+
87
89
  it do
88
90
  assert_requested :post, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
89
91
  JSON.parse(req.body) == JSON.parse(request_body.to_json)
90
92
  end
91
93
  end
92
94
  end
95
+
96
+ context 'when request is not successful' do
97
+ let(:response_body) { {}.to_json }
98
+
99
+ it { expect { client.impersonate(options) }.to raise_error(Castle::ImpersonationFailed) }
100
+ end
93
101
  end
94
102
 
95
103
  describe 'identify' do
96
104
  let(:request_body) do
97
105
  { user_id: '1234', timestamp: time_auto,
98
- sent_at: time_auto, context: context, traits: { name: 'Jo' } }
106
+ sent_at: time_auto, context: context, user_traits: { name: 'Jo' } }
99
107
  end
100
108
 
101
109
  before { client.identify(options) }
102
110
 
103
111
  context 'when used with symbol keys' do
104
- let(:options) { { user_id: '1234', traits: { name: 'Jo' } } }
112
+ let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
105
113
 
106
114
  it do
107
115
  assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
@@ -111,9 +119,9 @@ describe Castle::Client do
111
119
 
112
120
  context 'when passed timestamp in options and no defined timestamp' do
113
121
  let(:client) { client_with_no_timestamp }
114
- let(:options) { { user_id: '1234', traits: { name: 'Jo' }, timestamp: time_user } }
122
+ let(:options) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_user } }
115
123
  let(:request_body) do
116
- { user_id: '1234', traits: { name: 'Jo' }, context: context,
124
+ { user_id: '1234', user_traits: { name: 'Jo' }, context: context,
117
125
  timestamp: time_user, sent_at: time_auto }
118
126
  end
119
127
 
@@ -128,7 +136,7 @@ describe Castle::Client do
128
136
  let(:client) { client_with_user_timestamp }
129
137
  let(:request_body) do
130
138
  { user_id: '1234', timestamp: time_user, sent_at: time_auto,
131
- context: context, traits: { name: 'Jo' } }
139
+ context: context, user_traits: { name: 'Jo' } }
132
140
  end
133
141
 
134
142
  it do
@@ -140,7 +148,7 @@ describe Castle::Client do
140
148
  end
141
149
 
142
150
  context 'when used with string keys' do
143
- let(:options) { { 'user_id' => '1234', 'traits' => { 'name' => 'Jo' } } }
151
+ let(:options) { { 'user_id' => '1234', 'user_traits' => { 'name' => 'Jo' } } }
144
152
 
145
153
  it do
146
154
  assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
@@ -37,10 +37,10 @@ describe Castle::Commands::Authenticate do
37
37
  it { expect(command.data).to be_eql(command_data) }
38
38
  end
39
39
 
40
- context 'with traits' do
41
- let(:payload) { default_payload.merge(traits: { test: '1' }) }
40
+ context 'with user_traits' do
41
+ let(:payload) { default_payload.merge(user_traits: { test: '1' }) }
42
42
  let(:command_data) do
43
- default_payload.merge(traits: { test: '1' }, context: context)
43
+ default_payload.merge(user_traits: { test: '1' }, context: context)
44
44
  end
45
45
 
46
46
  it { expect(command.method).to be_eql(:post) }
@@ -26,10 +26,10 @@ describe Castle::Commands::Identify do
26
26
  it { expect(command.data).to be_eql(command_data) }
27
27
  end
28
28
 
29
- context 'with traits' do
30
- let(:payload) { default_payload.merge(traits: { test: '1' }) }
29
+ context 'with user_traits' do
30
+ let(:payload) { default_payload.merge(user_traits: { test: '1' }) }
31
31
  let(:command_data) do
32
- default_payload.merge(traits: { test: '1' }, context: context)
32
+ default_payload.merge(user_traits: { test: '1' }, context: context)
33
33
  end
34
34
 
35
35
  it { expect(command.method).to be_eql(:post) }
@@ -48,10 +48,10 @@ describe Castle::Commands::Track do
48
48
  it { expect(command.data).to be_eql(command_data) }
49
49
  end
50
50
 
51
- context 'with traits' do
52
- let(:payload) { default_payload.merge(traits: { test: '1' }) }
51
+ context 'with user_traits' do
52
+ let(:payload) { default_payload.merge(user_traits: { test: '1' }) }
53
53
  let(:command_data) do
54
- default_payload.merge(traits: { test: '1' }, context: context)
54
+ default_payload.merge(user_traits: { test: '1' }, context: context)
55
55
  end
56
56
 
57
57
  it { expect(command.method).to be_eql(:post) }
@@ -3,7 +3,8 @@
3
3
  describe Castle::Extractors::ClientId do
4
4
  subject(:extractor) { described_class.new(request, cookies) }
5
5
 
6
- let(:client_id) { 'abcd' }
6
+ let(:client_id_cookie) { 'abcd' }
7
+ let(:client_id_header) { 'abcde' }
7
8
  let(:cookies) { request.cookies }
8
9
  let(:request) { Rack::Request.new(env) }
9
10
  let(:env) do
@@ -14,12 +15,12 @@ describe Castle::Extractors::ClientId do
14
15
  let(:headers) do
15
16
  {
16
17
  'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
17
- 'HTTP_COOKIE' => "__cid=#{client_id};other=efgh"
18
+ 'HTTP_COOKIE' => "__cid=#{client_id_cookie};other=efgh"
18
19
  }
19
20
  end
20
21
 
21
22
  it do
22
- expect(extractor.call('__cid')).to eql(client_id)
23
+ expect(extractor.call).to eql(client_id_cookie)
23
24
  end
24
25
  end
25
26
 
@@ -27,12 +28,12 @@ describe Castle::Extractors::ClientId do
27
28
  let(:headers) do
28
29
  {
29
30
  'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
30
- 'HTTP_X_CASTLE_CLIENT_ID' => client_id
31
+ 'HTTP_X_CASTLE_CLIENT_ID' => client_id_header
31
32
  }
32
33
  end
33
34
 
34
35
  it 'appends the client_id' do
35
- expect(extractor.call('__cid')).to eql(client_id)
36
+ expect(extractor.call).to eql(client_id_header)
36
37
  end
37
38
  end
38
39
 
@@ -41,7 +42,21 @@ describe Castle::Extractors::ClientId do
41
42
  let(:headers) { {} }
42
43
 
43
44
  it do
44
- expect(extractor.call('__cid')).to eql('')
45
+ expect(extractor.call).to eql('')
46
+ end
47
+ end
48
+
49
+ context 'with X-Castle-Client-Id header and cookies client' do
50
+ let(:headers) do
51
+ {
52
+ 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
53
+ 'HTTP_X_CASTLE_CLIENT_ID' => client_id_header,
54
+ 'HTTP_COOKIE' => "__cid=#{client_id_cookie};other=efgh"
55
+ }
56
+ end
57
+
58
+ it 'appends the client_id' do
59
+ expect(extractor.call).to eql(client_id_header)
45
60
  end
46
61
  end
47
62
  end
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: 3.4.2
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johan Brissmyr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-26 00:00:00.000000000 Z
11
+ date: 2018-04-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Castle protects your users from account compromise
14
14
  email: johan@castle.io
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
101
  version: '0'
102
102
  requirements: []
103
103
  rubyforge_project:
104
- rubygems_version: 2.7.3
104
+ rubygems_version: 2.7.4
105
105
  signing_key:
106
106
  specification_version: 4
107
107
  summary: Castle