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