castle-rb 4.0.0 → 4.1.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 +4 -4
- data/lib/castle.rb +4 -4
- data/lib/castle/api.rb +12 -6
- data/lib/castle/api/request.rb +15 -10
- data/lib/castle/api/session.rb +39 -0
- data/lib/castle/client.rb +4 -4
- data/lib/castle/configuration.rb +8 -2
- data/lib/castle/context/default.rb +1 -1
- data/lib/castle/extractors/ip.rb +7 -1
- data/lib/castle/{header_filter.rb → headers_filter.rb} +7 -7
- data/lib/castle/headers_formatter.rb +22 -0
- data/lib/castle/version.rb +1 -1
- data/spec/lib/castle/api/request_spec.rb +42 -30
- data/spec/lib/castle/api_spec.rb +4 -4
- data/spec/lib/castle/configuration_spec.rb +3 -2
- data/spec/lib/castle/context/default_spec.rb +3 -2
- data/spec/lib/castle/extractors/client_id_spec.rb +1 -1
- data/spec/lib/castle/extractors/headers_spec.rb +0 -1
- data/spec/lib/castle/extractors/ip_spec.rb +5 -0
- data/spec/lib/castle/{header_filter_spec.rb → headers_filter_spec.rb} +4 -5
- data/spec/lib/castle/{header_formatter_spec.rb → headers_formatter_spec.rb} +2 -2
- data/spec/spec_helper.rb +1 -2
- metadata +9 -11
- data/lib/castle/api/request/build.rb +0 -27
- data/lib/castle/header_formatter.rb +0 -12
- data/spec/lib/castle/api/request/build_spec.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0d6aa4996eb091692c0a7dfcf9658b2b26fb011cfa94103604a79e60d401ac0
|
4
|
+
data.tar.gz: 8e7eb4c21cf3edd7a3845fc946857dd25ad1031d75dfa8cf8cb8b911b928eebe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bdf75588225fd282a36d1e37c1ee3eb69ecfab3f3e50a2c27d695f8c7c7ad1a2bccdb8b097dbe9ff361c17481c9449808d9c26adf56924eb249a18ffb2e9a71
|
7
|
+
data.tar.gz: '089b2507cd099a863b9592c8a194f5971f2126a7ff2892d0f211ace1a9b06261872a9a13d98a72b617f59815d70d87a4c49ca9e348c06031554a3a8cbe905140'
|
data/lib/castle.rb
CHANGED
@@ -29,15 +29,15 @@
|
|
29
29
|
castle/configuration
|
30
30
|
castle/failover_auth_response
|
31
31
|
castle/client
|
32
|
-
castle/
|
33
|
-
castle/
|
32
|
+
castle/headers_filter
|
33
|
+
castle/headers_formatter
|
34
34
|
castle/secure_mode
|
35
35
|
castle/extractors/client_id
|
36
36
|
castle/extractors/headers
|
37
37
|
castle/extractors/ip
|
38
38
|
castle/api/response
|
39
39
|
castle/api/request
|
40
|
-
castle/api/
|
40
|
+
castle/api/session
|
41
41
|
castle/review
|
42
42
|
castle/api
|
43
43
|
].each(&method(:require))
|
@@ -54,7 +54,7 @@ module Castle
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def config
|
57
|
-
|
57
|
+
Configuration.instance
|
58
58
|
end
|
59
59
|
|
60
60
|
def api_secret=(api_secret)
|
data/lib/castle/api.rb
CHANGED
@@ -17,16 +17,16 @@ module Castle
|
|
17
17
|
private_constant :HANDLED_ERRORS
|
18
18
|
|
19
19
|
class << self
|
20
|
+
# @param command [String]
|
21
|
+
# @param headers [Hash]
|
20
22
|
def request(command, headers = {})
|
21
23
|
raise Castle::ConfigurationError, 'configuration is not valid' unless Castle.config.valid?
|
22
24
|
|
23
25
|
begin
|
24
|
-
Castle::API::
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
headers
|
29
|
-
)
|
26
|
+
Castle::API::Request.call(
|
27
|
+
command,
|
28
|
+
Castle.config.api_secret,
|
29
|
+
headers
|
30
30
|
)
|
31
31
|
rescue *HANDLED_ERRORS => e
|
32
32
|
# @note We need to initialize the error, as the original error is a cause for this
|
@@ -35,6 +35,12 @@ module Castle
|
|
35
35
|
raise Castle::RequestError.new(e) # rubocop:disable Style/RaiseArgs
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
# @param command [String]
|
40
|
+
# @param headers [Hash]
|
41
|
+
def call(command, headers = {})
|
42
|
+
Castle::API::Response.call(request(command, headers))
|
43
|
+
end
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
data/lib/castle/api/request.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Castle
|
4
|
-
# this class is responsible for making requests to api
|
5
4
|
module API
|
5
|
+
# this class is responsible for making requests to api
|
6
6
|
module Request
|
7
7
|
# Default headers that we add to passed ones
|
8
8
|
DEFAULT_HEADERS = {
|
@@ -13,8 +13,8 @@ module Castle
|
|
13
13
|
|
14
14
|
class << self
|
15
15
|
def call(command, api_secret, headers)
|
16
|
-
|
17
|
-
|
16
|
+
Castle::API::Session.get.request(
|
17
|
+
build(
|
18
18
|
command,
|
19
19
|
headers.merge(DEFAULT_HEADERS),
|
20
20
|
api_secret
|
@@ -22,14 +22,19 @@ module Castle
|
|
22
22
|
)
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
def build(command, headers, api_secret)
|
26
|
+
request_obj = Net::HTTP.const_get(
|
27
|
+
command.method.to_s.capitalize
|
28
|
+
).new("#{Castle.config.url_prefix}/#{command.path}", headers)
|
29
|
+
|
30
|
+
unless command.method == :get
|
31
|
+
request_obj.body = ::Castle::Utils.replace_invalid_characters(
|
32
|
+
command.data
|
33
|
+
).to_json
|
31
34
|
end
|
32
|
-
|
35
|
+
|
36
|
+
request_obj.basic_auth('', api_secret)
|
37
|
+
request_obj
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module Castle
|
6
|
+
module API
|
7
|
+
# this class keeps http config object
|
8
|
+
# and provides start/finish methods for persistent connection usage
|
9
|
+
# when there is a need of sending multiple requests at once
|
10
|
+
class Session
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
attr_accessor :http
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
reset
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset
|
20
|
+
@http = Net::HTTP.new(Castle.config.host, Castle.config.port)
|
21
|
+
@http.read_timeout = Castle.config.request_timeout / 1000.0
|
22
|
+
|
23
|
+
if Castle.config.port == 443
|
24
|
+
@http.use_ssl = true
|
25
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
26
|
+
end
|
27
|
+
|
28
|
+
@http
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# @return [Net::HTTP]
|
33
|
+
def get
|
34
|
+
instance.http
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/castle/client.rb
CHANGED
@@ -44,7 +44,7 @@ module Castle
|
|
44
44
|
add_timestamp_if_necessary(options)
|
45
45
|
command = Castle::Commands::Authenticate.new(@context).build(options)
|
46
46
|
begin
|
47
|
-
Castle::API.
|
47
|
+
Castle::API.call(command).merge(failover: false, failover_reason: nil)
|
48
48
|
rescue Castle::RequestError, Castle::InternalServerError => e
|
49
49
|
self.class.failover_response_or_raise(
|
50
50
|
FailoverAuthResponse.new(options[:user_id], reason: e.to_s), e
|
@@ -60,7 +60,7 @@ module Castle
|
|
60
60
|
add_timestamp_if_necessary(options)
|
61
61
|
|
62
62
|
command = Castle::Commands::Identify.new(@context).build(options)
|
63
|
-
Castle::API.
|
63
|
+
Castle::API.call(command)
|
64
64
|
end
|
65
65
|
|
66
66
|
def track(options = {})
|
@@ -71,14 +71,14 @@ module Castle
|
|
71
71
|
add_timestamp_if_necessary(options)
|
72
72
|
|
73
73
|
command = Castle::Commands::Track.new(@context).build(options)
|
74
|
-
Castle::API.
|
74
|
+
Castle::API.call(command)
|
75
75
|
end
|
76
76
|
|
77
77
|
def impersonate(options = {})
|
78
78
|
options = Castle::Utils.deep_symbolize_keys(options || {})
|
79
79
|
add_timestamp_if_necessary(options)
|
80
80
|
command = Castle::Commands::Impersonate.new(@context).build(options)
|
81
|
-
Castle::API.
|
81
|
+
Castle::API.call(command).tap do |response|
|
82
82
|
raise Castle::ImpersonationFailed unless response[:success]
|
83
83
|
end
|
84
84
|
end
|
data/lib/castle/configuration.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
module Castle
|
4
4
|
# manages configuration variables
|
5
5
|
class Configuration
|
6
|
+
include Singleton
|
7
|
+
|
6
8
|
HOST = 'api.castle.io'
|
7
9
|
PORT = 443
|
8
|
-
URL_PREFIX = 'v1'
|
10
|
+
URL_PREFIX = '/v1'
|
9
11
|
FAILOVER_STRATEGY = :allow
|
10
12
|
REQUEST_TIMEOUT = 500 # in milliseconds
|
11
13
|
FAILOVER_STRATEGIES = %i[allow deny challenge throw].freeze
|
@@ -44,8 +46,12 @@ module Castle
|
|
44
46
|
attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy, :ip_headers, :trusted_proxies
|
45
47
|
|
46
48
|
def initialize
|
47
|
-
@formatter = Castle::
|
49
|
+
@formatter = Castle::HeadersFormatter
|
48
50
|
@request_timeout = REQUEST_TIMEOUT
|
51
|
+
reset
|
52
|
+
end
|
53
|
+
|
54
|
+
def reset
|
49
55
|
self.failover_strategy = FAILOVER_STRATEGY
|
50
56
|
self.host = HOST
|
51
57
|
self.port = PORT
|
data/lib/castle/extractors/ip.rb
CHANGED
@@ -45,7 +45,13 @@ module Castle
|
|
45
45
|
# @param ips [Array<String>]
|
46
46
|
# @return [Array<String>]
|
47
47
|
def remove_proxies(ips)
|
48
|
-
ips.reject { |ip|
|
48
|
+
ips.reject { |ip| proxy?(ip) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param ip [String]
|
52
|
+
# @return [Boolean]
|
53
|
+
def proxy?(ip)
|
54
|
+
@proxies.any? { |proxy| proxy.match(ip) }
|
49
55
|
end
|
50
56
|
|
51
57
|
# @param header [String]
|
@@ -2,23 +2,23 @@
|
|
2
2
|
|
3
3
|
module Castle
|
4
4
|
# used for preparing valuable headers list
|
5
|
-
class
|
5
|
+
class HeadersFilter
|
6
6
|
# headers filter
|
7
7
|
# HTTP_ - this is how Rack prefixes incoming HTTP headers
|
8
8
|
# CONTENT_LENGTH - for responses without Content-Length or Transfer-Encoding header
|
9
9
|
# REMOTE_ADDR - ip address header returned by web server
|
10
|
-
VALUABLE_HEADERS = /^
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
VALUABLE_HEADERS = /^
|
11
|
+
HTTP(?:_|-).*|
|
12
|
+
CONTENT(?:_|-)LENGTH|
|
13
|
+
REMOTE(?:_|-)ADDR
|
14
|
+
$/xi.freeze
|
15
15
|
|
16
16
|
private_constant :VALUABLE_HEADERS
|
17
17
|
|
18
18
|
# @param request [Rack::Request]
|
19
19
|
def initialize(request)
|
20
20
|
@request_env = request.env
|
21
|
-
@formatter =
|
21
|
+
@formatter = HeadersFormatter
|
22
22
|
end
|
23
23
|
|
24
24
|
# Serialize HTTP headers
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
# formats header name
|
5
|
+
class HeadersFormatter
|
6
|
+
class << self
|
7
|
+
# @param header [String]
|
8
|
+
# @return [String]
|
9
|
+
def call(header)
|
10
|
+
format(header.to_s.gsub(/^HTTP(?:_|-)/i, ''))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @param header [String]
|
16
|
+
# @return [String]
|
17
|
+
def format(header)
|
18
|
+
header.split(/_|-/).map(&:capitalize).join('-')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/castle/version.rb
CHANGED
@@ -4,57 +4,69 @@ describe Castle::API::Request do
|
|
4
4
|
describe '#call' do
|
5
5
|
subject(:call) { described_class.call(command, api_secret, headers) }
|
6
6
|
|
7
|
+
let(:session) { instance_double('Castle::API::Session') }
|
7
8
|
let(:http) { instance_double('Net::HTTP') }
|
8
|
-
let(:request_build) { instance_double('Castle::API::Request::Build') }
|
9
9
|
let(:command) { Castle::Commands::Track.new({}).build(event: '$login.succeeded') }
|
10
10
|
let(:headers) { {} }
|
11
11
|
let(:api_secret) { 'secret' }
|
12
|
+
let(:request_build) { {} }
|
12
13
|
let(:expected_headers) { { 'Content-Type' => 'application/json' } }
|
13
14
|
|
14
15
|
before do
|
15
|
-
allow(
|
16
|
-
allow(
|
17
|
-
allow(
|
18
|
-
|
19
|
-
.and_return(request_build)
|
16
|
+
allow(Castle::API::Session).to receive(:instance).and_return(session)
|
17
|
+
allow(session).to receive(:http).and_return(http)
|
18
|
+
allow(http).to receive(:request)
|
19
|
+
allow(described_class).to receive(:build).and_return(request_build)
|
20
20
|
call
|
21
21
|
end
|
22
22
|
|
23
23
|
it do
|
24
|
-
expect(
|
25
|
-
.with(command, expected_headers, api_secret)
|
24
|
+
expect(described_class).to have_received(:build).with(command, expected_headers, api_secret)
|
26
25
|
end
|
27
26
|
|
28
27
|
it { expect(http).to have_received(:request).with(request_build) }
|
29
28
|
end
|
30
29
|
|
31
|
-
describe '#
|
32
|
-
subject(:
|
30
|
+
describe '#build' do
|
31
|
+
subject(:build) { described_class.build(command, headers, api_secret) }
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
Castle.config.host = 'localhost'
|
37
|
-
Castle.config.port = 3002
|
38
|
-
end
|
33
|
+
let(:headers) { { 'SAMPLE-HEADER' => '1' } }
|
34
|
+
let(:api_secret) { 'secret' }
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
36
|
+
context 'when get' do
|
37
|
+
let(:command) { Castle::Commands::Review.build(review_id) }
|
38
|
+
let(:review_id) { SecureRandom.uuid }
|
44
39
|
|
45
|
-
it { expect(
|
46
|
-
it { expect(
|
47
|
-
it { expect(
|
48
|
-
it { expect(
|
49
|
-
it { expect(
|
40
|
+
it { expect(build.body).to be_nil }
|
41
|
+
it { expect(build.method).to eql('GET') }
|
42
|
+
it { expect(build.path).to eql("/v1/#{command.path}") }
|
43
|
+
it { expect(build.to_hash).to have_key('authorization') }
|
44
|
+
it { expect(build.to_hash).to have_key('sample-header') }
|
45
|
+
it { expect(build.to_hash['sample-header']).to eql(['1']) }
|
50
46
|
end
|
51
47
|
|
52
|
-
context 'when
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
48
|
+
context 'when post' do
|
49
|
+
let(:time) { Time.now.utc.iso8601(3) }
|
50
|
+
let(:command) do
|
51
|
+
Castle::Commands::Track.new({}).build(event: '$login.succeeded', name: "\xC4")
|
52
|
+
end
|
53
|
+
let(:expected_body) do
|
54
|
+
{
|
55
|
+
event: '$login.succeeded',
|
56
|
+
name: '�',
|
57
|
+
context: {},
|
58
|
+
sent_at: time
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
before { allow(Castle::Utils::Timestamp).to receive(:call).and_return(time) }
|
63
|
+
|
64
|
+
it { expect(build.body).to be_eql(expected_body.to_json) }
|
65
|
+
it { expect(build.method).to eql('POST') }
|
66
|
+
it { expect(build.path).to eql("/v1/#{command.path}") }
|
67
|
+
it { expect(build.to_hash).to have_key('authorization') }
|
68
|
+
it { expect(build.to_hash).to have_key('sample-header') }
|
69
|
+
it { expect(build.to_hash['sample-header']).to eql(['1']) }
|
58
70
|
end
|
59
71
|
end
|
60
72
|
end
|
data/spec/lib/castle/api_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
describe Castle::API do
|
4
|
-
subject(:
|
4
|
+
subject(:call) { described_class.call(command) }
|
5
5
|
|
6
6
|
let(:command) { Castle::Commands::Track.new({}).build(event: '$login.succeeded') }
|
7
7
|
|
@@ -10,7 +10,7 @@ describe Castle::API do
|
|
10
10
|
|
11
11
|
it do
|
12
12
|
expect do
|
13
|
-
|
13
|
+
call
|
14
14
|
end.to raise_error(Castle::RequestError)
|
15
15
|
end
|
16
16
|
end
|
@@ -20,7 +20,7 @@ describe Castle::API do
|
|
20
20
|
|
21
21
|
it do
|
22
22
|
expect do
|
23
|
-
|
23
|
+
call
|
24
24
|
end.to raise_error(Castle::BadRequestError)
|
25
25
|
end
|
26
26
|
end
|
@@ -30,7 +30,7 @@ describe Castle::API do
|
|
30
30
|
|
31
31
|
it do
|
32
32
|
expect do
|
33
|
-
|
33
|
+
call
|
34
34
|
end.to raise_error(Castle::ConfigurationError)
|
35
35
|
end
|
36
36
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
describe Castle::Configuration do
|
4
4
|
subject(:config) do
|
5
|
-
described_class.
|
5
|
+
described_class.instance
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'host' do
|
@@ -38,6 +38,7 @@ describe Castle::Configuration do
|
|
38
38
|
allow(ENV).to receive(:fetch).with(
|
39
39
|
'CASTLE_API_SECRET', ''
|
40
40
|
).and_return(secret_key_env)
|
41
|
+
config.reset
|
41
42
|
end
|
42
43
|
|
43
44
|
it do
|
@@ -66,7 +67,7 @@ describe Castle::Configuration do
|
|
66
67
|
end
|
67
68
|
|
68
69
|
it do
|
69
|
-
expect(config.api_secret).to be_eql('')
|
70
|
+
expect(config.api_secret).to be_eql('secret')
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
@@ -4,14 +4,14 @@ describe Castle::Context::Default do
|
|
4
4
|
subject { described_class.new(request, nil) }
|
5
5
|
|
6
6
|
let(:ip) { '1.2.3.4' }
|
7
|
-
let(:
|
7
|
+
let(:client_id) { 'abcd' }
|
8
8
|
|
9
9
|
let(:env) do
|
10
10
|
Rack::MockRequest.env_for('/',
|
11
11
|
'HTTP_X_FORWARDED_FOR' => ip,
|
12
12
|
'HTTP_ACCEPT_LANGUAGE' => 'en',
|
13
13
|
'HTTP_USER_AGENT' => 'test',
|
14
|
-
'HTTP_COOKIE' => "__cid=#{
|
14
|
+
'HTTP_COOKIE' => "__cid=#{client_id};other=efgh")
|
15
15
|
end
|
16
16
|
let(:request) { Rack::Request.new(env) }
|
17
17
|
let(:default_context) { subject.call }
|
@@ -34,6 +34,7 @@ describe Castle::Context::Default do
|
|
34
34
|
it { expect(default_context[:origin]).to be_eql('web') }
|
35
35
|
it { expect(default_context[:headers]).to be_eql(result_headers) }
|
36
36
|
it { expect(default_context[:ip]).to be_eql(ip) }
|
37
|
+
it { expect(default_context[:client_id]).to be_eql(client_id) }
|
37
38
|
it { expect(default_context[:library][:name]).to be_eql('castle-rb') }
|
38
39
|
it { expect(default_context[:library][:version]).to be_eql(version) }
|
39
40
|
it { expect(default_context[:user_agent]).to be_eql('test') }
|
@@ -3,7 +3,7 @@
|
|
3
3
|
describe Castle::Extractors::ClientId do
|
4
4
|
subject(:extractor) { described_class.new(formatted_headers, cookies) }
|
5
5
|
|
6
|
-
let(:formatted_headers) { Castle::
|
6
|
+
let(:formatted_headers) { Castle::HeadersFilter.new(request).call }
|
7
7
|
let(:client_id_cookie) { 'abcd' }
|
8
8
|
let(:client_id_header) { 'abcde' }
|
9
9
|
let(:cookies) { request.cookies }
|
@@ -4,6 +4,11 @@ describe Castle::Extractors::IP do
|
|
4
4
|
subject(:extractor) { described_class.new(headers) }
|
5
5
|
|
6
6
|
describe 'ip' do
|
7
|
+
after do
|
8
|
+
Castle.config.ip_headers = []
|
9
|
+
Castle.config.trusted_proxies = []
|
10
|
+
end
|
11
|
+
|
7
12
|
context 'when regular ip' do
|
8
13
|
let(:headers) { { 'X-Forwarded-For' => '1.2.3.5' } }
|
9
14
|
|
@@ -1,16 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
describe Castle::
|
3
|
+
describe Castle::HeadersFilter do
|
4
4
|
subject(:headers) { described_class.new(request).call }
|
5
5
|
|
6
|
-
let(:client_id) { 'abcd' }
|
7
6
|
let(:env) do
|
8
7
|
Rack::MockRequest.env_for(
|
9
8
|
'/',
|
10
9
|
'Action-Dispatch.request.content-Type' => 'application/json',
|
11
10
|
'HTTP_AUTHORIZATION' => 'Basic 123456',
|
12
|
-
'HTTP_COOKIE' =>
|
13
|
-
'
|
11
|
+
'HTTP_COOKIE' => '__cid=abcd;other=efgh',
|
12
|
+
'http-ok' => 'OK',
|
14
13
|
'HTTP_ACCEPT' => 'application/json',
|
15
14
|
'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
|
16
15
|
'HTTP_USER_AGENT' => 'Mozilla 1234',
|
@@ -22,7 +21,7 @@ describe Castle::HeaderFilter do
|
|
22
21
|
{
|
23
22
|
'Accept' => 'application/json',
|
24
23
|
'Authorization' => 'Basic 123456',
|
25
|
-
'Cookie' =>
|
24
|
+
'Cookie' => '__cid=abcd;other=efgh',
|
26
25
|
'Content-Length' => '0',
|
27
26
|
'Ok' => 'OK',
|
28
27
|
'User-Agent' => 'Mozilla 1234',
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
describe Castle::
|
4
|
-
subject(:formatter) { described_class
|
3
|
+
describe Castle::HeadersFormatter do
|
4
|
+
subject(:formatter) { described_class }
|
5
5
|
|
6
6
|
it 'removes HTTP_' do
|
7
7
|
expect(formatter.call('HTTP_X_TEST')).to be_eql('X-Test')
|
data/spec/spec_helper.rb
CHANGED
@@ -16,8 +16,7 @@ WebMock.disable_net_connect!(allow_localhost: true)
|
|
16
16
|
|
17
17
|
RSpec.configure do |config|
|
18
18
|
config.before do
|
19
|
-
Castle.
|
20
|
-
|
19
|
+
Castle.config.reset
|
21
20
|
Castle.configure do |cfg|
|
22
21
|
cfg.api_secret = 'secret'
|
23
22
|
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: 4.
|
4
|
+
version: 4.1.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: 2020-03-
|
11
|
+
date: 2020-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appraisal
|
@@ -35,8 +35,8 @@ files:
|
|
35
35
|
- lib/castle.rb
|
36
36
|
- lib/castle/api.rb
|
37
37
|
- lib/castle/api/request.rb
|
38
|
-
- lib/castle/api/request/build.rb
|
39
38
|
- lib/castle/api/response.rb
|
39
|
+
- lib/castle/api/session.rb
|
40
40
|
- lib/castle/client.rb
|
41
41
|
- lib/castle/command.rb
|
42
42
|
- lib/castle/commands/authenticate.rb
|
@@ -54,8 +54,8 @@ files:
|
|
54
54
|
- lib/castle/extractors/headers.rb
|
55
55
|
- lib/castle/extractors/ip.rb
|
56
56
|
- lib/castle/failover_auth_response.rb
|
57
|
-
- lib/castle/
|
58
|
-
- lib/castle/
|
57
|
+
- lib/castle/headers_filter.rb
|
58
|
+
- lib/castle/headers_formatter.rb
|
59
59
|
- lib/castle/review.rb
|
60
60
|
- lib/castle/secure_mode.rb
|
61
61
|
- lib/castle/support/hanami.rb
|
@@ -73,7 +73,6 @@ files:
|
|
73
73
|
- spec/integration/rails/support/all.rb
|
74
74
|
- spec/integration/rails/support/application.rb
|
75
75
|
- spec/integration/rails/support/home_controller.rb
|
76
|
-
- spec/lib/castle/api/request/build_spec.rb
|
77
76
|
- spec/lib/castle/api/request_spec.rb
|
78
77
|
- spec/lib/castle/api/response_spec.rb
|
79
78
|
- spec/lib/castle/api_spec.rb
|
@@ -92,8 +91,8 @@ files:
|
|
92
91
|
- spec/lib/castle/extractors/client_id_spec.rb
|
93
92
|
- spec/lib/castle/extractors/headers_spec.rb
|
94
93
|
- spec/lib/castle/extractors/ip_spec.rb
|
95
|
-
- spec/lib/castle/
|
96
|
-
- spec/lib/castle/
|
94
|
+
- spec/lib/castle/headers_filter_spec.rb
|
95
|
+
- spec/lib/castle/headers_formatter_spec.rb
|
97
96
|
- spec/lib/castle/review_spec.rb
|
98
97
|
- spec/lib/castle/secure_mode_spec.rb
|
99
98
|
- spec/lib/castle/utils/cloner_spec.rb
|
@@ -136,6 +135,7 @@ test_files:
|
|
136
135
|
- spec/integration/rails/rails_spec.rb
|
137
136
|
- spec/lib/castle_spec.rb
|
138
137
|
- spec/lib/castle/review_spec.rb
|
138
|
+
- spec/lib/castle/headers_filter_spec.rb
|
139
139
|
- spec/lib/castle/client_spec.rb
|
140
140
|
- spec/lib/castle/context/default_spec.rb
|
141
141
|
- spec/lib/castle/context/merger_spec.rb
|
@@ -143,15 +143,13 @@ test_files:
|
|
143
143
|
- spec/lib/castle/api_spec.rb
|
144
144
|
- spec/lib/castle/configuration_spec.rb
|
145
145
|
- spec/lib/castle/version_spec.rb
|
146
|
-
- spec/lib/castle/header_formatter_spec.rb
|
147
146
|
- spec/lib/castle/utils/cloner_spec.rb
|
148
147
|
- spec/lib/castle/utils/timestamp_spec.rb
|
149
148
|
- spec/lib/castle/utils/merger_spec.rb
|
150
149
|
- spec/lib/castle/command_spec.rb
|
150
|
+
- spec/lib/castle/headers_formatter_spec.rb
|
151
151
|
- spec/lib/castle/api/request_spec.rb
|
152
152
|
- spec/lib/castle/api/response_spec.rb
|
153
|
-
- spec/lib/castle/api/request/build_spec.rb
|
154
|
-
- spec/lib/castle/header_filter_spec.rb
|
155
153
|
- spec/lib/castle/commands/review_spec.rb
|
156
154
|
- spec/lib/castle/commands/authenticate_spec.rb
|
157
155
|
- spec/lib/castle/commands/track_spec.rb
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Castle
|
4
|
-
module API
|
5
|
-
# generate api request
|
6
|
-
module Request
|
7
|
-
module Build
|
8
|
-
class << self
|
9
|
-
def call(command, headers, api_secret)
|
10
|
-
request = Net::HTTP.const_get(
|
11
|
-
command.method.to_s.capitalize
|
12
|
-
).new("/#{Castle.config.url_prefix}/#{command.path}", headers)
|
13
|
-
|
14
|
-
unless command.method == :get
|
15
|
-
request.body = ::Castle::Utils.replace_invalid_characters(
|
16
|
-
command.data
|
17
|
-
).to_json
|
18
|
-
end
|
19
|
-
|
20
|
-
request.basic_auth('', api_secret)
|
21
|
-
request
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Castle
|
4
|
-
# formats header name
|
5
|
-
class HeaderFormatter
|
6
|
-
# @param header [String]
|
7
|
-
# @return [String]
|
8
|
-
def call(header)
|
9
|
-
header.to_s.gsub(/^HTTP(?:_|-)/i, '').split(/_|-/).map(&:capitalize).join('-')
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Castle::API::Request::Build do
|
4
|
-
subject(:call) { described_class.call(command, headers, api_secret) }
|
5
|
-
|
6
|
-
let(:headers) { { 'SAMPLE-HEADER' => '1' } }
|
7
|
-
let(:api_secret) { 'secret' }
|
8
|
-
|
9
|
-
describe 'call' do
|
10
|
-
context 'when get' do
|
11
|
-
let(:command) { Castle::Commands::Review.build(review_id) }
|
12
|
-
let(:review_id) { SecureRandom.uuid }
|
13
|
-
|
14
|
-
it { expect(call.body).to be_nil }
|
15
|
-
it { expect(call.method).to eql('GET') }
|
16
|
-
it { expect(call.path).to eql("/v1/#{command.path}") }
|
17
|
-
it { expect(call.to_hash).to have_key('authorization') }
|
18
|
-
it { expect(call.to_hash).to have_key('sample-header') }
|
19
|
-
it { expect(call.to_hash['sample-header']).to eql(['1']) }
|
20
|
-
end
|
21
|
-
|
22
|
-
context 'when post' do
|
23
|
-
let(:time) { Time.now.utc.iso8601(3) }
|
24
|
-
let(:command) do
|
25
|
-
Castle::Commands::Track.new({}).build(event: '$login.succeeded', name: "\xC4")
|
26
|
-
end
|
27
|
-
let(:expected_body) do
|
28
|
-
{
|
29
|
-
event: '$login.succeeded',
|
30
|
-
name: '�',
|
31
|
-
context: {},
|
32
|
-
sent_at: time
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
before { allow(Castle::Utils::Timestamp).to receive(:call).and_return(time) }
|
37
|
-
|
38
|
-
it { expect(call.body).to be_eql(expected_body.to_json) }
|
39
|
-
it { expect(call.method).to eql('POST') }
|
40
|
-
it { expect(call.path).to eql("/v1/#{command.path}") }
|
41
|
-
it { expect(call.to_hash).to have_key('authorization') }
|
42
|
-
it { expect(call.to_hash).to have_key('sample-header') }
|
43
|
-
it { expect(call.to_hash['sample-header']).to eql(['1']) }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|