castle-rb 3.3.1 → 3.4.0.pre.rc.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: 5d0999873fc06348f78e4663a5bf653fe64f62f8432af21a7596dca3ed935ee3
4
- data.tar.gz: a3e523ef2862b8f2928c180d70242aac5afda8594f5d48f0fc2647186a107fb5
3
+ metadata.gz: 1ccb1875a2285b0243e021b7af9c65d9e85ca3339a9a9545a005cb7647d1c947
4
+ data.tar.gz: 050dc35cdcea679018cdd5d590e05a12248544c7f6de90651134d6ab1348baad
5
5
  SHA512:
6
- metadata.gz: 876a90d7bf0ceebefbd0ee2243f533cd520a91b8415fbbcb9b85eb2df5aa58dcc52717d267bb9edf89debd5a9e7f94f793b3564b34aaf6502a81885025ad6c08
7
- data.tar.gz: 97f910519f2e67c9737219e4f7fe5c91f08bb817942bccc14dc534d67ec0a71db0d408d75454e4c67d148ca9aa60c30f3f9252a279321783d79ea07fd5cc43f6
6
+ metadata.gz: 35e8261649b45f2a60cc39c0e8e2824d23d5f42aeee081c85c2f009588c3e127e767610d6c7ed39cfeed932009c213f85c42c1f95072df4588d93937d61d32a6
7
+ data.tar.gz: 1a88b649dc9b80208e26368f41fe28d19e84b9614b6ca87f36c960e3c83199061e341d77fe7a58946927080f753008a654ef915108d41691d109fb254f7b9cd8
@@ -19,6 +19,7 @@ require 'castle/commands/identify'
19
19
  require 'castle/commands/authenticate'
20
20
  require 'castle/commands/track'
21
21
  require 'castle/commands/review'
22
+ require 'castle/commands/impersonate'
22
23
  require 'castle/configuration'
23
24
  require 'castle/failover_auth_response'
24
25
  require 'castle/client'
@@ -66,6 +66,14 @@ module Castle
66
66
  @api.request(command)
67
67
  end
68
68
 
69
+ def impersonate(options = {})
70
+ options = Castle::Utils.deep_symbolize_keys(options || {})
71
+
72
+ return unless tracked?
73
+ command = Castle::Commands::Impersonate.new(@context).build(options)
74
+ @api.request(command)
75
+ end
76
+
69
77
  def disable_tracking
70
78
  @do_not_track = true
71
79
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module Commands
5
+ # builder for impersonate command
6
+ class Impersonate
7
+ def initialize(context)
8
+ @context = context
9
+ end
10
+
11
+ def build(options = {})
12
+ validate!(options)
13
+ context = ContextMerger.call(@context, options[:context])
14
+ context = ContextSanitizer.call(context)
15
+
16
+ validate_context!(context)
17
+
18
+ Castle::Command.new('impersonate',
19
+ options.merge(context: context),
20
+ :post)
21
+ end
22
+
23
+ private
24
+
25
+ def validate!(options)
26
+ %i[user_id].each do |key|
27
+ next unless options[key].to_s.empty?
28
+ raise Castle::InvalidParametersError, "#{key} is missing or empty"
29
+ end
30
+ end
31
+
32
+ def validate_context!(context)
33
+ %i[user_agent ip].each do |key|
34
+ next unless context[key].to_s.empty?
35
+ raise Castle::InvalidParametersError, "#{key} is missing or empty"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castle
4
- VERSION = '3.3.1'
4
+ VERSION = '3.4.0-rc.0'
5
5
  end
@@ -3,9 +3,11 @@
3
3
  describe Castle::Client do
4
4
  let(:ip) { '1.2.3.4' }
5
5
  let(:cookie_id) { 'abcd' }
6
+ let(:ua) { 'Chrome' }
6
7
  let(:env) do
7
8
  Rack::MockRequest.env_for(
8
9
  '/',
10
+ 'HTTP_USER_AGENT' => ua,
9
11
  'HTTP_X_FORWARDED_FOR' => ip,
10
12
  'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh"
11
13
  )
@@ -18,13 +20,14 @@ describe Castle::Client do
18
20
  end
19
21
  let(:client_with_no_timestamp) { described_class.new(request_to_context) }
20
22
 
21
- let(:headers) { { 'X-Forwarded-For' => ip.to_s } }
23
+ let(:headers) { { 'X-Forwarded-For' => ip.to_s, 'User-Agent' => ua } }
22
24
  let(:context) do
23
25
  {
24
26
  client_id: 'abcd',
25
27
  active: true,
26
28
  origin: 'web',
27
- headers: { 'X-Forwarded-For': ip.to_s },
29
+ user_agent: ua,
30
+ headers: { 'X-Forwarded-For': ip.to_s, 'User-Agent': ua },
28
31
  ip: ip,
29
32
  library: { name: 'castle-rb', version: '2.2.0' }
30
33
  }
@@ -42,9 +45,7 @@ describe Castle::Client do
42
45
  ).to_return(status: 200, body: '{}', headers: {})
43
46
  end
44
47
 
45
- after do
46
- Timecop.return
47
- end
48
+ after { Timecop.return }
48
49
 
49
50
  describe 'parses the request' do
50
51
  before do
@@ -72,6 +73,24 @@ describe Castle::Client do
72
73
  end
73
74
  end
74
75
 
76
+ describe 'impersonate' do
77
+ let(:impersonator) { 'test@castle.io' }
78
+ let(:request_body) do
79
+ { user_id: '1234', impersonator: impersonator, context: context }
80
+ end
81
+ let(:options) { { user_id: '1234', impersonator: impersonator } }
82
+
83
+ before { client.impersonate(options) }
84
+
85
+ context 'when used with symbol keys' do
86
+ it do
87
+ assert_requested :post, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
88
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
75
94
  describe 'identify' do
76
95
  let(:request_body) do
77
96
  { user_id: '1234', timestamp: time_auto,
@@ -80,7 +99,7 @@ describe Castle::Client do
80
99
 
81
100
  before { client.identify(options) }
82
101
 
83
- context 'symbol keys' do
102
+ context 'when used with symbol keys' do
84
103
  let(:options) { { user_id: '1234', traits: { name: 'Jo' } } }
85
104
 
86
105
  it do
@@ -119,7 +138,7 @@ describe Castle::Client do
119
138
  end
120
139
  end
121
140
 
122
- context 'string keys' do
141
+ context 'when used with string keys' do
123
142
  let(:options) { { 'user_id' => '1234', 'traits' => { 'name' => 'Jo' } } }
124
143
 
125
144
  it do
@@ -138,7 +157,7 @@ describe Castle::Client do
138
157
  timestamp: time_auto, sent_at: time_auto }
139
158
  end
140
159
 
141
- context 'symbol keys' do
160
+ context 'when used with symbol keys' do
142
161
  before { request_response }
143
162
 
144
163
  it do
@@ -177,7 +196,7 @@ describe Castle::Client do
177
196
  end
178
197
  end
179
198
 
180
- context 'string keys' do
199
+ context 'when used with string keys' do
181
200
  let(:options) { { 'event' => '$login.succeeded', 'user_id' => '1234' } }
182
201
 
183
202
  before { request_response }
@@ -189,7 +208,7 @@ describe Castle::Client do
189
208
  end
190
209
  end
191
210
 
192
- context 'tracking enabled' do
211
+ context 'when tracking enabled' do
193
212
  before { request_response }
194
213
 
195
214
  it do
@@ -202,7 +221,7 @@ describe Castle::Client do
202
221
  it { expect(request_response[:failover_reason]).to be_nil }
203
222
  end
204
223
 
205
- context 'tracking disabled' do
224
+ context 'when tracking disabled' do
206
225
  before do
207
226
  client.disable_tracking
208
227
  request_response
@@ -260,7 +279,7 @@ describe Castle::Client do
260
279
 
261
280
  before { client.track(options) }
262
281
 
263
- context 'symbol keys' do
282
+ context 'when used with symbol keys' do
264
283
  let(:options) { { event: '$login.succeeded', user_id: '1234' } }
265
284
 
266
285
  it do
@@ -299,7 +318,7 @@ describe Castle::Client do
299
318
  end
300
319
  end
301
320
 
302
- context 'string keys' do
321
+ context 'when used with string keys' do
303
322
  let(:options) { { 'event' => '$login.succeeded', 'user_id' => '1234' } }
304
323
 
305
324
  it do
@@ -311,13 +330,13 @@ describe Castle::Client do
311
330
  end
312
331
 
313
332
  describe 'tracked?' do
314
- context 'off' do
333
+ context 'when off' do
315
334
  before { client.disable_tracking }
316
335
 
317
336
  it { expect(client).not_to be_tracked }
318
337
  end
319
338
 
320
- context 'on' do
339
+ context 'when on' do
321
340
  before { client.enable_tracking }
322
341
 
323
342
  it { expect(client).to be_tracked }
@@ -9,13 +9,8 @@ describe Castle::Commands::Authenticate do
9
9
  let(:time_now) { Time.now }
10
10
  let(:time_auto) { time_now.utc.iso8601(3) }
11
11
 
12
- before do
13
- Timecop.freeze(time_now)
14
- end
15
-
16
- after do
17
- Timecop.return
18
- end
12
+ before { Timecop.freeze(time_now) }
13
+ after { Timecop.return }
19
14
 
20
15
  describe '.build' do
21
16
  subject(:command) { instance.build(payload) }
@@ -9,13 +9,8 @@ describe Castle::Commands::Identify do
9
9
  let(:time_now) { Time.now }
10
10
  let(:time_auto) { time_now.utc.iso8601(3) }
11
11
 
12
- before do
13
- Timecop.freeze(time_now)
14
- end
15
-
16
- after do
17
- Timecop.return
18
- end
12
+ before { Timecop.freeze(time_now) }
13
+ after { Timecop.return }
19
14
 
20
15
  describe '.build' do
21
16
  subject(:command) { instance.build(payload) }
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Commands::Impersonate do
4
+ subject(:instance) { described_class.new(context) }
5
+
6
+ let(:context) { { user_agent: 'test', ip: '127.0.0.1', client_id: 'test' } }
7
+ let(:impersonator) { 'test@castle.io' }
8
+ let(:default_payload) { { user_id: '1234' } }
9
+
10
+ let(:time_now) { Time.now }
11
+ let(:time_auto) { time_now.utc.iso8601(3) }
12
+
13
+ before { Timecop.freeze(time_now) }
14
+ after { Timecop.return }
15
+
16
+ describe '.build' do
17
+ subject(:command) { instance.build(payload) }
18
+
19
+ context 'with simple merger' do
20
+ let(:payload) { default_payload.merge(context: { test: { test2: '1' } }) }
21
+ let(:command_data) do
22
+ default_payload.merge(
23
+ context: {
24
+ test: { test2: '1' },
25
+ user_agent: 'test',
26
+ ip: '127.0.0.1',
27
+ client_id: 'test'
28
+ }
29
+ )
30
+ end
31
+
32
+ it { expect(command.method).to be_eql(:post) }
33
+ it { expect(command.path).to be_eql('impersonate') }
34
+ it { expect(command.data).to be_eql(command_data) }
35
+ end
36
+
37
+ context 'with impersonator' do
38
+ let(:payload) { default_payload.merge(impersonator: impersonator) }
39
+ let(:command_data) do
40
+ default_payload.merge(impersonator: impersonator, context: context)
41
+ end
42
+
43
+ it { expect(command.method).to be_eql(:post) }
44
+ it { expect(command.path).to be_eql('impersonate') }
45
+ it { expect(command.data).to be_eql(command_data) }
46
+ end
47
+
48
+ context 'when active true' do
49
+ let(:payload) { default_payload.merge(context: { active: true }) }
50
+ let(:command_data) do
51
+ default_payload.merge(context: context.merge(active: true))
52
+ end
53
+
54
+ it { expect(command.method).to be_eql(:post) }
55
+ it { expect(command.path).to be_eql('impersonate') }
56
+ it { expect(command.data).to be_eql(command_data) }
57
+ end
58
+
59
+ context 'when active false' do
60
+ let(:payload) { default_payload.merge(context: { active: false }) }
61
+ let(:command_data) do
62
+ default_payload.merge(context: context.merge(active: false))
63
+ end
64
+
65
+ it { expect(command.method).to be_eql(:post) }
66
+ it { expect(command.path).to be_eql('impersonate') }
67
+ it { expect(command.data).to be_eql(command_data) }
68
+ end
69
+
70
+ context 'when active string' do
71
+ let(:payload) { default_payload.merge(context: { active: 'string' }) }
72
+ let(:command_data) { default_payload.merge(context: context) }
73
+
74
+ it { expect(command.method).to be_eql(:post) }
75
+ it { expect(command.path).to be_eql('impersonate') }
76
+ it { expect(command.data).to be_eql(command_data) }
77
+ end
78
+ end
79
+
80
+ describe '#validate!' do
81
+ subject(:validate!) { instance.build(payload) }
82
+
83
+ context 'when user_id not present' do
84
+ let(:payload) { {} }
85
+
86
+ it do
87
+ expect do
88
+ validate!
89
+ end.to raise_error(Castle::InvalidParametersError, 'user_id is missing or empty')
90
+ end
91
+ end
92
+
93
+ context 'when user_id present' do
94
+ let(:payload) { { user_id: '1234' } }
95
+
96
+ it { expect { validate! }.not_to raise_error }
97
+ end
98
+ end
99
+ end
@@ -9,13 +9,8 @@ describe Castle::Commands::Track do
9
9
  let(:time_now) { Time.now }
10
10
  let(:time_auto) { time_now.utc.iso8601(3) }
11
11
 
12
- before do
13
- Timecop.freeze(time_now)
14
- end
15
-
16
- after do
17
- Timecop.return
18
- end
12
+ before { Timecop.freeze(time_now) }
13
+ after { Timecop.return }
19
14
 
20
15
  describe '#build' do
21
16
  subject(:command) { instance.build(payload) }
@@ -6,13 +6,8 @@ describe Castle::Utils::Timestamp do
6
6
  let(:time_string) { '2018-01-10T14:14:24.407Z' }
7
7
  let(:time) { Time.parse(time_string) }
8
8
 
9
- before do
10
- Timecop.freeze(time)
11
- end
12
-
13
- after do
14
- Timecop.return
15
- end
9
+ before { Timecop.freeze(time) }
10
+ after { Timecop.return }
16
11
 
17
12
  describe '#call' do
18
13
  it do
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.3.1
4
+ version: 3.4.0.pre.rc.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-01-22 00:00:00.000000000 Z
11
+ date: 2018-01-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Castle protects your users from account compromise
14
14
  email: johan@castle.io
@@ -24,6 +24,7 @@ files:
24
24
  - lib/castle/command.rb
25
25
  - lib/castle/commands/authenticate.rb
26
26
  - lib/castle/commands/identify.rb
27
+ - lib/castle/commands/impersonate.rb
27
28
  - lib/castle/commands/review.rb
28
29
  - lib/castle/commands/track.rb
29
30
  - lib/castle/configuration.rb
@@ -54,6 +55,7 @@ files:
54
55
  - spec/lib/castle/command_spec.rb
55
56
  - spec/lib/castle/commands/authenticate_spec.rb
56
57
  - spec/lib/castle/commands/identify_spec.rb
58
+ - spec/lib/castle/commands/impersonate_spec.rb
57
59
  - spec/lib/castle/commands/review_spec.rb
58
60
  - spec/lib/castle/commands/track_spec.rb
59
61
  - spec/lib/castle/configuration_spec.rb
@@ -90,9 +92,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
92
  version: '0'
91
93
  required_rubygems_version: !ruby/object:Gem::Requirement
92
94
  requirements:
93
- - - ">="
95
+ - - ">"
94
96
  - !ruby/object:Gem::Version
95
- version: '0'
97
+ version: 1.3.1
96
98
  requirements: []
97
99
  rubyforge_project:
98
100
  rubygems_version: 2.7.4
@@ -119,6 +121,7 @@ test_files:
119
121
  - spec/lib/castle/commands/review_spec.rb
120
122
  - spec/lib/castle/commands/authenticate_spec.rb
121
123
  - spec/lib/castle/commands/track_spec.rb
124
+ - spec/lib/castle/commands/impersonate_spec.rb
122
125
  - spec/lib/castle/commands/identify_spec.rb
123
126
  - spec/lib/castle/context_merger_spec.rb
124
127
  - spec/lib/castle/extractors/ip_spec.rb