castle-rb 3.3.1 → 3.4.0.pre.rc.0

Sign up to get free protection for your applications and to get access to all the features.
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