moesif_rack 1.2.5 → 1.2.6
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/README.md +55 -2
- data/lib/moesif_rack/client_ip.rb +121 -0
- data/lib/moesif_rack/moesif_middleware.rb +58 -2
- data/test/moesif_rack_test.rb +64 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c8bb62325d278fd7e988de98c2da89a2c70997f0b76a734e28adb817f176816
|
4
|
+
data.tar.gz: 364c201149ab8f9247f3964e3e78743fd6cb56a52de086c57cf1276df9ea51de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcda4f70277329914b87572d137021b81e7b592810d5850f8a281fd410335faa0388e7f9ab1ec1e5a92422f34e47200b67ab33a01282e1903dc05a1ef672a468
|
7
|
+
data.tar.gz: 9222266c7fbfc1156ce0f91081d377ef0ccaa59ce71479b44c94e177772b7368aa14545a1a4185079d076dfc1a6311bc47e25bb86b538f71af3a147912d38d6d
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ gem install moesif_rack
|
|
20
20
|
and if you have a `Gemfile` in your project, please add this line to
|
21
21
|
|
22
22
|
```
|
23
|
-
gem 'moesif_rack', '~> 1.2.
|
23
|
+
gem 'moesif_rack', '~> 1.2.6'
|
24
24
|
|
25
25
|
```
|
26
26
|
|
@@ -187,6 +187,59 @@ For details for the spec of event model, please see the [Moesif Ruby API Documen
|
|
187
187
|
|
188
188
|
Optional. Boolean. Default false. If true, it will print out debug messages. In debug mode, the processing is not done in backend thread.
|
189
189
|
|
190
|
+
## Update User
|
191
|
+
|
192
|
+
### update_user method
|
193
|
+
A method is attached to the moesif middleware object to update the users profile or metadata.
|
194
|
+
The metadata field can be any custom data you want to set on the user. The `user_id` field is required.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
metadata = JSON.parse('{'\
|
198
|
+
'"email": "testrubyapi@user.com",'\
|
199
|
+
'"name": "ruby api user",'\
|
200
|
+
'"custom": "testdata"'\
|
201
|
+
'}')
|
202
|
+
|
203
|
+
user_model = { "user_id" => "testrubyapiuser",
|
204
|
+
"modified_time" => Time.now.utc.iso8601,
|
205
|
+
"metadata" => metadata }
|
206
|
+
|
207
|
+
update_user = MoesifRack::MoesifMiddleware.new(@app, @options).update_user(user_model)
|
208
|
+
```
|
209
|
+
|
210
|
+
### update_users_batch method
|
211
|
+
A method is attached to the moesif middleware object to update the users profile or metadata in batch.
|
212
|
+
The metadata field can be any custom data you want to set on the user. The `user_id` field is required.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
metadata = JSON.parse('{'\
|
216
|
+
'"email": "testrubyapi@user.com",'\
|
217
|
+
'"name": "ruby api user",'\
|
218
|
+
'"custom": "testdata"'\
|
219
|
+
'}')
|
220
|
+
|
221
|
+
user_models = []
|
222
|
+
|
223
|
+
user_model_A = { "user_id" => "testrubyapiuser",
|
224
|
+
"modified_time" => Time.now.utc.iso8601,
|
225
|
+
"metadata" => metadata }
|
226
|
+
|
227
|
+
user_model_B = { "user_id" => "testrubyapiuser1",
|
228
|
+
"modified_time" => Time.now.utc.iso8601,
|
229
|
+
"metadata" => metadata }
|
230
|
+
|
231
|
+
user_models << user_model_A << user_model_B
|
232
|
+
response = MoesifRack::MoesifMiddleware.new(@app, @options).update_users_batch(user_models)
|
233
|
+
```
|
234
|
+
|
235
|
+
## How to test
|
236
|
+
|
237
|
+
1. Manually clone the git repo
|
238
|
+
2. From terminal/cmd navigate to the root directory of the middleware.
|
239
|
+
3. Invoke 'gem install moesif_rack'
|
240
|
+
4. Add your own application id to 'test/moesif_rack_test.rb'. You can find your Application Id from [_Moesif Dashboard_](https://www.moesif.com/) -> _Top Right Menu_ -> _Installation_
|
241
|
+
5. Invoke 'ruby test/moesif_rack_test.rb'
|
242
|
+
|
190
243
|
## Example Code
|
191
244
|
|
192
245
|
[Moesif Rack Example](https://github.com/Moesif/moesif-rack-example) is an
|
@@ -195,7 +248,7 @@ for reference.
|
|
195
248
|
|
196
249
|
## Other integrations
|
197
250
|
|
198
|
-
To view more more documentation on integration options, please visit
|
251
|
+
To view more more documentation on integration options, please visit [the Integration Options Documentation](https://www.moesif.com/docs/getting-started/integration-options/).
|
199
252
|
|
200
253
|
[ico-built-for]: https://img.shields.io/badge/built%20for-rack-blue.svg
|
201
254
|
[ico-version]: https://img.shields.io/gem/v/moesif_rack.svg
|
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
def is_ip?(value)
|
3
|
+
if
|
4
|
+
ipv4 = /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/
|
5
|
+
ipv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
|
6
|
+
# We use !! to convert the return value to a boolean
|
7
|
+
!!(value =~ ipv4 or value=~ ipv6)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_client_ip_from_x_forwarded_for(value)
|
12
|
+
begin
|
13
|
+
value = value.encode('utf-8')
|
14
|
+
|
15
|
+
if value.to_s.empty?
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
|
19
|
+
if !value.instance_of?(String)
|
20
|
+
puts ("Expected a string, got - " + value.class.to_s)
|
21
|
+
else
|
22
|
+
# x-forwarded-for may return multiple IP addresses in the format:
|
23
|
+
# "client IP, proxy 1 IP, proxy 2 IP"
|
24
|
+
# Therefore, the right-most IP address is the IP address of the most recent proxy
|
25
|
+
# and the left-most IP address is the IP address of the originating client.
|
26
|
+
# source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html
|
27
|
+
# Azure Web App's also adds a port for some reason, so we'll only use the first part (the IP)
|
28
|
+
forwardedIps = []
|
29
|
+
|
30
|
+
value.gsub(/\s+/, "").split(',').each do |e|
|
31
|
+
if e.include?(':')
|
32
|
+
splitted = e.split(':')
|
33
|
+
if splitted.length == 2
|
34
|
+
forwardedIps << splitted.first
|
35
|
+
end
|
36
|
+
end
|
37
|
+
forwardedIps << e
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sometimes IP addresses in this header can be 'unknown' (http://stackoverflow.com/a/11285650).
|
41
|
+
# Therefore taking the left-most IP address that is not unknown
|
42
|
+
# A Squid configuration directive can also set the value to "unknown" (http://www.squid-cache.org/Doc/config/forwarded_for/)
|
43
|
+
return forwardedIps.find {|e| is_ip?(e) }
|
44
|
+
end
|
45
|
+
rescue
|
46
|
+
return value.encode('utf-8')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_client_address(env)
|
51
|
+
begin
|
52
|
+
# Standard headers used by Amazon EC2, Heroku, and others.
|
53
|
+
if env.key?('HTTP_X_CLIENT_IP')
|
54
|
+
if is_ip?(env['HTTP_X_CLIENT_IP'])
|
55
|
+
return env['HTTP_X_CLIENT_IP']
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Load-balancers (AWS ELB) or proxies.
|
60
|
+
if env.key?('HTTP_X_FORWARDED_FOR')
|
61
|
+
xForwardedFor = get_client_ip_from_x_forwarded_for(env['HTTP_X_FORWARDED_FOR'])
|
62
|
+
if is_ip?(xForwardedFor)
|
63
|
+
return xForwardedFor
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Cloudflare.
|
68
|
+
# @see https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
|
69
|
+
# CF-Connecting-IP - applied to every request to the origin.
|
70
|
+
if env.key?('HTTP_CF_CONNECTING_IP')
|
71
|
+
if is_ip?(env['HTTP_CF_CONNECTING_IP'])
|
72
|
+
return env['HTTP_CF_CONNECTING_IP']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Akamai and Cloudflare: True-Client-IP.
|
77
|
+
if env.key?('HTTP_TRUE_CLIENT_IP')
|
78
|
+
if is_ip?(env['HTTP_TRUE_CLIENT_IP'])
|
79
|
+
return env['HTTP_TRUE_CLIENT_IP']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies.
|
84
|
+
if env.key?('HTTP_X_REAL_IP')
|
85
|
+
if is_ip?(env['HTTP_X_REAL_IP'])
|
86
|
+
return env['HTTP_X_REAL_IP']
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# (Rackspace LB and Riverbed's Stingray)
|
91
|
+
# http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address
|
92
|
+
# https://splash.riverbed.com/docs/DOC-1926
|
93
|
+
if env.key?('HTTP_X_CLUSTER_CLIENT_IP')
|
94
|
+
if is_ip?(env['HTTP_X_CLUSTER_CLIENT_IP'])
|
95
|
+
return env['HTTP_X_CLUSTER_CLIENT_IP']
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if env.key?('HTTP_X_FORWARDED')
|
100
|
+
if is_ip?(env['HTTP_X_FORWARDED'])
|
101
|
+
return env['HTTP_X_FORWARDED']
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if env.key?('HTTP_FORWARDED_FOR')
|
106
|
+
if is_ip?(env['HTTP_FORWARDED_FOR'])
|
107
|
+
return env['HTTP_FORWARDED_FOR']
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
if env.key?('HTTP_FORWARDED')
|
112
|
+
if is_ip?(env['HTTP_FORWARDED'])
|
113
|
+
return env['HTTP_FORWARDED']
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
return env['REMOTE_ADDR']
|
118
|
+
rescue
|
119
|
+
return env['REMOTE_ADDR']
|
120
|
+
end
|
121
|
+
end
|
@@ -2,6 +2,7 @@ require 'moesif_api'
|
|
2
2
|
require 'json'
|
3
3
|
require 'time'
|
4
4
|
require 'base64'
|
5
|
+
require_relative './client_ip.rb'
|
5
6
|
|
6
7
|
module MoesifRack
|
7
8
|
|
@@ -58,6 +59,61 @@ module MoesifRack
|
|
58
59
|
return sample_rate
|
59
60
|
end
|
60
61
|
|
62
|
+
def update_user(user_profile)
|
63
|
+
if user_profile.any?
|
64
|
+
if user_profile.key?("user_id")
|
65
|
+
begin
|
66
|
+
@api_controller.update_user(MoesifApi::UserModel.from_hash(user_profile))
|
67
|
+
if @debug
|
68
|
+
puts "Update User Successfully"
|
69
|
+
end
|
70
|
+
rescue MoesifApi::APIException => e
|
71
|
+
if e.response_code.between?(401, 403)
|
72
|
+
puts "Unathorized accesss updating user to Moesif. Please verify your Application Id."
|
73
|
+
end
|
74
|
+
if @debug
|
75
|
+
puts "Error updating user to Moesif, with status code: "
|
76
|
+
puts e.response_code
|
77
|
+
end
|
78
|
+
end
|
79
|
+
else
|
80
|
+
puts "To update an user, an user_id field is required"
|
81
|
+
end
|
82
|
+
else
|
83
|
+
puts "Expecting the input to be of the type - dictionary while updating user"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_users_batch(user_profiles)
|
88
|
+
userModels = []
|
89
|
+
user_profiles.each { |user|
|
90
|
+
if user.key?("user_id")
|
91
|
+
userModels << MoesifApi::UserModel.from_hash(user)
|
92
|
+
else
|
93
|
+
puts "To update an user, an user_id field is required"
|
94
|
+
end
|
95
|
+
}
|
96
|
+
|
97
|
+
if userModels.any?
|
98
|
+
begin
|
99
|
+
@api_controller.update_users_batch(userModels)
|
100
|
+
if @debug
|
101
|
+
puts "Update Users Successfully"
|
102
|
+
end
|
103
|
+
rescue MoesifApi::APIException => e
|
104
|
+
if e.response_code.between?(401, 403)
|
105
|
+
puts "Unathorized accesss updating user to Moesif. Please verify your Application Id."
|
106
|
+
end
|
107
|
+
if @debug
|
108
|
+
puts "Error updating user to Moesif, with status code: "
|
109
|
+
puts e.response_code
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
puts "Expecting the input to be of the type - Array of hashes while updating users in batch"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
61
117
|
def call env
|
62
118
|
start_time = Time.now.utc.iso8601
|
63
119
|
|
@@ -142,7 +198,7 @@ module MoesifRack
|
|
142
198
|
headers["X-Moesif-Transaction-Id"] = transaction_id
|
143
199
|
end
|
144
200
|
|
145
|
-
event_req.ip_address = req.
|
201
|
+
event_req.ip_address = get_client_address(req.env)
|
146
202
|
event_req.headers = req_headers
|
147
203
|
event_req.body = req_body
|
148
204
|
event_req.transfer_encoding = req_body_transfer_encoding
|
@@ -205,7 +261,7 @@ module MoesifRack
|
|
205
261
|
end
|
206
262
|
else
|
207
263
|
if @debug
|
208
|
-
puts("Skipped Event due to sampling percentage: " + @sampling_percentage.to_s + " and random percentage: " + @random_percentage.to_s)
|
264
|
+
puts("Skipped Event due to sampling percentage: " + @sampling_percentage.to_s + " and random percentage: " + @random_percentage.to_s)
|
209
265
|
end
|
210
266
|
end
|
211
267
|
rescue MoesifApi::APIException => e
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rack'
|
3
|
+
require_relative '../lib/moesif_rack'
|
4
|
+
|
5
|
+
class MoesifRackTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@app = ->(env) { [200, { "Content-Type" => "application/json" }, ["{ \"key\": \"value\"}"]]}
|
8
|
+
@options = { 'application_id' => 'Your Application Id',
|
9
|
+
'debug' => true,
|
10
|
+
'disable_transaction_id' => true}
|
11
|
+
@moesif_rack_app = MoesifRack::MoesifMiddleware.new(@app, @options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_new_calls_to_middleware
|
15
|
+
assert_instance_of MoesifRack::MoesifMiddleware, @moesif_rack_app
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_update_user
|
19
|
+
metadata = JSON.parse('{'\
|
20
|
+
'"email": "testrubyapi@user.com",'\
|
21
|
+
'"name": "ruby api user",'\
|
22
|
+
'"custom": "testdata"'\
|
23
|
+
'}')
|
24
|
+
|
25
|
+
user_model = { "user_id" => "testrubyapiuser",
|
26
|
+
"modified_time" => Time.now.utc.iso8601,
|
27
|
+
"metadata" => metadata }
|
28
|
+
|
29
|
+
response = @moesif_rack_app.update_user(user_model)
|
30
|
+
assert_equal response, nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_update_users_batch
|
34
|
+
metadata = JSON.parse('{'\
|
35
|
+
'"email": "testrubyapi@user.com",'\
|
36
|
+
'"name": "ruby api user",'\
|
37
|
+
'"custom": "testdata"'\
|
38
|
+
'}')
|
39
|
+
|
40
|
+
user_models = []
|
41
|
+
|
42
|
+
user_model_A = { "user_id" => "testrubyapiuser",
|
43
|
+
"modified_time" => Time.now.utc.iso8601,
|
44
|
+
"metadata" => metadata }
|
45
|
+
|
46
|
+
user_model_B = { "user_id" => "testrubyapiuser1",
|
47
|
+
"modified_time" => Time.now.utc.iso8601,
|
48
|
+
"metadata" => metadata }
|
49
|
+
|
50
|
+
user_models << user_model_A << user_model_B
|
51
|
+
response = @moesif_rack_app.update_users_batch(user_models)
|
52
|
+
assert_equal response, nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_log_event
|
56
|
+
response = @moesif_rack_app.call(Rack::MockRequest.env_for("https://acmeinc.com/items/42752/reviews"))
|
57
|
+
assert_equal response, @app.call(nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_get_config
|
61
|
+
assert_operator 100, :>=, @moesif_rack_app.get_config(nil)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moesif_rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moesif, Inc
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-03-
|
12
|
+
date: 2019-03-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: test-unit
|
@@ -48,7 +48,9 @@ files:
|
|
48
48
|
- LICENSE
|
49
49
|
- README.md
|
50
50
|
- lib/moesif_rack.rb
|
51
|
+
- lib/moesif_rack/client_ip.rb
|
51
52
|
- lib/moesif_rack/moesif_middleware.rb
|
53
|
+
- test/moesif_rack_test.rb
|
52
54
|
homepage: https://moesif.com
|
53
55
|
licenses:
|
54
56
|
- Apache-2.0
|