castle-rb 3.4.2 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/castle/client.rb +4 -1
- data/lib/castle/configuration.rb +2 -2
- data/lib/castle/context/default.rb +1 -1
- data/lib/castle/errors.rb +3 -0
- data/lib/castle/extractors/client_id.rb +2 -3
- data/lib/castle/version.rb +1 -1
- data/spec/lib/castle/client_spec.rb +19 -11
- data/spec/lib/castle/commands/authenticate_spec.rb +3 -3
- data/spec/lib/castle/commands/identify_spec.rb +3 -3
- data/spec/lib/castle/commands/track_spec.rb +3 -3
- data/spec/lib/castle/extractors/client_id_spec.rb +21 -6
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6376098ba7a5b711d926fe187c38d2ed31a68b7ddf53024f2d51a81a14482604
|
4
|
+
data.tar.gz: b4ca1f4dbd10607b5c5bfad6a9b7528d3ebb8ebf7a2b6fd1287af08333b110d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70c04aa5f1d0f3f4af5789a7aad877380b3d82fafbd935ec746bd40aab904c110bea4410d161c48521fee108a5212c7d81b8a00b465b82940e7f6f9c48236f26
|
7
|
+
data.tar.gz: 345383199ba415d699f97f1ebf242d21dc8e1eb921a4c60f6366464a1e6bc5b5ac0680d7010e6f2b4b9ff5366393c9fd9b70237951ac7b013328e05af3af11b7
|
data/README.md
CHANGED
data/lib/castle/client.rb
CHANGED
@@ -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
|
data/lib/castle/configuration.rb
CHANGED
@@ -65,8 +65,8 @@ module Castle
|
|
65
65
|
/^(\w+)=$/ =~ method_name
|
66
66
|
end
|
67
67
|
|
68
|
-
def method_missing(
|
69
|
-
raise Castle::ConfigurationError, "there is no such a config #{
|
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
|
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
|
data/lib/castle/errors.rb
CHANGED
data/lib/castle/version.rb
CHANGED
@@ -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:
|
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',
|
69
|
-
let(:result) { { user_id: '1234',
|
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,
|
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',
|
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',
|
122
|
+
let(:options) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_user } }
|
115
123
|
let(:request_body) do
|
116
|
-
{ user_id: '1234',
|
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,
|
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', '
|
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
|
41
|
-
let(:payload) { default_payload.merge(
|
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(
|
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
|
30
|
-
let(:payload) { default_payload.merge(
|
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(
|
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
|
52
|
-
let(:payload) { default_payload.merge(
|
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(
|
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(:
|
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=#{
|
18
|
+
'HTTP_COOKIE' => "__cid=#{client_id_cookie};other=efgh"
|
18
19
|
}
|
19
20
|
end
|
20
21
|
|
21
22
|
it do
|
22
|
-
expect(extractor.call
|
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' =>
|
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
|
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
|
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
|
+
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-
|
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.
|
104
|
+
rubygems_version: 2.7.4
|
105
105
|
signing_key:
|
106
106
|
specification_version: 4
|
107
107
|
summary: Castle
|