perimeter_x 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +47 -0
  3. data/Dockerfile +50 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +55 -0
  6. data/LICENSE.txt +18 -0
  7. data/Rakefile +9 -0
  8. data/changelog.md +0 -0
  9. data/examples/app/controllers/home_controller.rb +9 -0
  10. data/examples/app/views/home/index.html.erb.dist +20 -0
  11. data/examples/config/initializers/perimeterx.rb.dist +8 -0
  12. data/examples/config/routes.rb +62 -0
  13. data/lib/perimeter_x.rb +149 -0
  14. data/lib/perimeterx/configuration.rb +37 -0
  15. data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +92 -0
  16. data/lib/perimeterx/internal/clients/perimeter_x_risk_client.rb +28 -0
  17. data/lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb +5 -0
  18. data/lib/perimeterx/internal/perimeter_x_context.rb +82 -0
  19. data/lib/perimeterx/internal/perimeter_x_cookie.rb +140 -0
  20. data/lib/perimeterx/internal/perimeter_x_cookie_v1.rb +42 -0
  21. data/lib/perimeterx/internal/perimeter_x_cookie_v3.rb +37 -0
  22. data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +65 -0
  23. data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +69 -0
  24. data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +110 -0
  25. data/lib/perimeterx/utils/px_constants.rb +42 -0
  26. data/lib/perimeterx/utils/px_http_client.rb +55 -0
  27. data/lib/perimeterx/utils/px_logger.rb +17 -0
  28. data/lib/perimeterx/utils/px_template_factory.rb +31 -0
  29. data/lib/perimeterx/utils/templates/block.mustache +146 -0
  30. data/lib/perimeterx/utils/templates/captcha.mustache +185 -0
  31. data/lib/perimeterx/version.rb +3 -0
  32. data/perimeter_x.gemspec +39 -0
  33. data/readme.md +294 -0
  34. metadata +192 -0
@@ -0,0 +1,82 @@
1
+ require 'perimeterx/utils/px_logger'
2
+
3
+ module PxModule
4
+ class PerimeterXContext
5
+
6
+ attr_accessor :context
7
+ attr_accessor :px_config
8
+
9
+ def initialize(px_config, req)
10
+ @logger = px_config[:logger];
11
+ @logger.debug("PerimeterXContext[initialize] ")
12
+ @context = Hash.new
13
+
14
+ @context[:px_cookie] = Hash.new
15
+ @context[:headers] = Hash.new
16
+ cookies = req.cookies
17
+ if (!cookies.empty?)
18
+ # Prepare hashed cookies
19
+ cookies.each do |k, v|
20
+ case k.to_s
21
+ when "_px3"
22
+ @context[:px_cookie][:v3] = v
23
+ when "_px"
24
+ @context[:px_cookie][:v1] = v
25
+ when "_pxCaptcha"
26
+ @context[:px_captcha] = v
27
+ end
28
+ end #end case
29
+ end #end empty cookies
30
+
31
+ req.headers.each do |k, v|
32
+ if (k.start_with? "HTTP_")
33
+ header = k.to_s.gsub("HTTP_", "")
34
+ header = header.gsub("_", "-").downcase
35
+ @context[:headers][header.to_sym] = v
36
+ end
37
+ end #end headers foreach
38
+
39
+ @context[:hostname]= req.server_name
40
+ @context[:user_agent] = req.user_agent ? req.user_agent : ''
41
+ @context[:uri] = px_config[:custom_uri] ? px_config[:custom_uri].call(req) : req.headers['REQUEST_URI']
42
+ @context[:full_url] = req.original_url
43
+ @context[:score] = 0
44
+
45
+ if px_config.key?(:custom_user_ip)
46
+ @context[:ip] = req.headers[px_config[:custom_user_ip]]
47
+ elsif px_config.key?(:px_custom_user_ip_method)
48
+ @context[:ip] = px_config[:px_custom_user_ip_method].call(req)
49
+ else
50
+ @context[:ip] = req.ip
51
+ end
52
+
53
+ if req.server_protocol
54
+ httpVer = req.server_protocol.split("/")
55
+ if httpVer.size > 0
56
+ @context[:http_version] = httpVer[1];
57
+ end
58
+ end
59
+ @context[:http_method] = req.method
60
+
61
+ end #end init
62
+
63
+ def set_block_action_type(action)
64
+ @context[:block_action] = case action
65
+ when "c"
66
+ "captcha"
67
+ when "b"
68
+ return "block"
69
+ when "j"
70
+ return "challenge"
71
+ else
72
+ return "captcha"
73
+ end
74
+ end
75
+
76
+ def get_px_cookie
77
+ cookie = @context[:px_cookie].key?(:v3) ? @context[:px_cookie][:v3] : @context[:px_cookie][:v1]
78
+ return cookie.tr(' ','+') if !cookie.nil?
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,140 @@
1
+ require 'active_support/security_utils'
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'perimeterx/internal/exceptions/px_cookie_decryption_exception'
5
+
6
+ module PxModule
7
+ class PerimeterxCookie
8
+ attr_accessor :px_cookie, :px_config, :px_ctx, :cookie_secret, :decoded_cookie
9
+
10
+ def initialize(px_config)
11
+ @px_config = px_config
12
+ @logger = px_config[:logger]
13
+ end
14
+
15
+ def self.px_cookie_factory(px_ctx, px_config)
16
+ if (px_ctx.context[:px_cookie].key?(:v3))
17
+ return PerimeterxCookieV3.new(px_config, px_ctx)
18
+ end
19
+ return PerimeterxCookieV1.new(px_config, px_ctx)
20
+ end
21
+
22
+ def cookie_score
23
+ #abstract, must be implemented
24
+ raise Exception.new("Unimplemented method")
25
+ end
26
+
27
+ def cookie_hmac
28
+ #abstract, must be implemented
29
+ raise Exception.new("Unimplemented method")
30
+ end
31
+
32
+ def valid_format?(cookie)
33
+ #abstract, must be implemented
34
+ raise Exception.new("Unimplemented method")
35
+ end
36
+
37
+ def cookie_block_action
38
+ #abstract, must be implemented
39
+ raise Exception.new("Unimplemented method")
40
+ end
41
+
42
+ def secured?
43
+ #abstract, must be implemented
44
+ raise Exception.new("Unimplemented method")
45
+ end
46
+
47
+ def is_valid?
48
+ return deserialize && !expired? && secured?
49
+ end
50
+
51
+ def cookie_time
52
+ return @decoded_cookie[:t]
53
+ end
54
+
55
+ def cookie_uuid
56
+ return @decoded_cookie[:u]
57
+ end
58
+
59
+ def cookie_vid
60
+ return @decoded_cookie[:v]
61
+ end
62
+
63
+ def high_score?
64
+ return cookie_score >= @px_config[:blocking_score]
65
+ end
66
+
67
+ def expired?
68
+ return cookie_time < (Time.now.to_f*1000).floor
69
+ end
70
+
71
+
72
+ def deserialize
73
+ if (!@decoded_cookie.nil?)
74
+ return true
75
+ end
76
+
77
+ # Decode or decrypt, depends on configuration
78
+ if (@px_config[:encryption_enabled])
79
+ cookie = decrypt(@px_cookie)
80
+ else
81
+ cookie = decode(@px_cookie)
82
+ end
83
+
84
+ if (cookie.nil?)
85
+ return false
86
+ end
87
+
88
+ if (!valid_format?(cookie))
89
+ return false
90
+ end
91
+
92
+ @decoded_cookie = cookie
93
+
94
+ return true
95
+ end
96
+
97
+
98
+ def decrypt(px_cookie)
99
+ begin
100
+ if (px_cookie.nil?)
101
+ return
102
+ end
103
+ px_cookie = px_cookie.gsub(' ', '+')
104
+ salt, iterations, cipher_text = px_cookie.split(':')
105
+ iterations = iterations.to_i
106
+ salt = Base64.decode64(salt)
107
+ cipher_text = Base64.decode64(cipher_text)
108
+ digest = OpenSSL::Digest::SHA256.new
109
+ value = OpenSSL::PKCS5.pbkdf2_hmac(@px_config[:cookie_key], salt, iterations, 48, digest)
110
+ key = value[0..31]
111
+ iv = value[32..-1]
112
+ cipher = OpenSSL::Cipher::AES256.new(:CBC)
113
+ cipher.decrypt
114
+ cipher.key = key
115
+ cipher.iv = iv
116
+ plaintext = cipher.update(cipher_text) + cipher.final
117
+
118
+ return eval(plaintext)
119
+ rescue Exception => e
120
+ @logger.debug("PerimeterxCookie[decrypt]: Cookie decrypt fail #{e.message}")
121
+ raise PxCookieDecryptionException.new("Cookie decrypt fail => #{e.message}");
122
+ end
123
+ end
124
+
125
+ def decode(px_cookie)
126
+ return eval(Base64.decode64(px_cookie))
127
+ end
128
+
129
+
130
+ def hmac_valid?(hmac_str, cookie_hmac)
131
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @cookie_secret, hmac_str)
132
+ # ref: https://thisdata.com/blog/timing-attacks-against-string-comparison/
133
+ password_correct = ActiveSupport::SecurityUtils.secure_compare(
134
+ ::Digest::SHA256.hexdigest(cookie_hmac),
135
+ ::Digest::SHA256.hexdigest(hmac)
136
+ )
137
+
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,42 @@
1
+ module PxModule
2
+ class PerimeterxCookieV1 < PerimeterxCookie
3
+
4
+ attr_accessor :px_config, :px_ctx
5
+
6
+ def initialize(px_config, px_ctx)
7
+ super(px_config)
8
+ @px_ctx = px_ctx
9
+ @px_cookie = px_ctx.get_px_cookie
10
+ @cookie_secret = px_config[:cookie_key]
11
+ @logger.debug("PerimeterxCookieV1[initialize]")
12
+ end
13
+
14
+ def cookie_score
15
+ return @decoded_cookie[:s][:b]
16
+ end
17
+
18
+ def cookie_hmac
19
+ return @decoded_cookie[:h]
20
+ end
21
+
22
+ def valid_format?(cookie)
23
+ return cookie.key?(:t) && cookie.key?(:s) && cookie[:s].key?(:b) && cookie.key?(:s) && cookie.key?(:v) && cookie.key?(:h)
24
+ end
25
+
26
+ def cookie_block_action
27
+ return 'c'
28
+ end
29
+
30
+ def secured?
31
+ base_hmac_str = "#{cookie_time}#{@decoded_cookie[:s][:a]}#{cookie_score}#{cookie_uuid}#{cookie_vid}"
32
+
33
+ hmac_str_withip = "#{base_hmac_str}#{@px_ctx.context[:ip]}#{@px_ctx.context[:user_agent]}"
34
+
35
+ hmac_str_withoutip = "#{base_hmac_str}#{@px_ctx.context[:user_agent]}"
36
+
37
+ return (hmac_valid?(hmac_str_withoutip, cookie_hmac) || hmac_valid?(hmac_str_withip, cookie_hmac))
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,37 @@
1
+ module PxModule
2
+ class PerimeterxCookieV3 < PerimeterxCookie
3
+
4
+ attr_accessor :px_config, :px_ctx, :cookie_hash
5
+
6
+ def initialize(px_config, px_ctx)
7
+ super(px_config)
8
+ hash, cookie = px_ctx.get_px_cookie().split(':', 2)
9
+ @px_cookie = cookie
10
+ @cookie_hash = hash
11
+ @px_ctx = px_ctx
12
+ @cookie_secret = px_config[:cookie_key]
13
+ @logger.debug("PerimeterxCookieV3[initialize]")
14
+ end
15
+
16
+ def cookie_score
17
+ return @decoded_cookie[:s]
18
+ end
19
+
20
+ def cookie_hmac
21
+ return @cookie_hash
22
+ end
23
+
24
+ def valid_format?(cookie)
25
+ return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:u) && cookie.key?(:a)
26
+ end
27
+
28
+ def cookie_block_action
29
+ @decoded_cookie[:a]
30
+ end
31
+
32
+ def secured?
33
+ hmac_string = "#{@px_cookie}#{@px_ctx.context[:user_agent]}"
34
+ return hmac_valid?(hmac_string, cookie_hmac)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ require 'perimeterx/internal/clients/perimeter_x_risk_client'
2
+
3
+ module PxModule
4
+ class PerimeterxCaptchaValidator < PerimeterxRiskClient
5
+
6
+ def initialize(px_config, http_client)
7
+ super(px_config, http_client)
8
+ end
9
+
10
+ def send_captcha_request(vid, uuid, captcha, px_ctx)
11
+
12
+ request_body = {
13
+ :request => {
14
+ :ip => px_ctx.context[:ip],
15
+ :headers => format_headers(px_ctx),
16
+ :uri => px_ctx.context[:uri]
17
+ },
18
+ :pxCaptcha => captcha,
19
+ :vid => vid,
20
+ :uuid => uuid,
21
+ :hostname => px_ctx.context[:hostname]
22
+ }
23
+
24
+ # Prepare request
25
+ headers = {
26
+ "Authorization" => "Bearer #{@px_config[:auth_token]}" ,
27
+ "Content-Type" => "application/json"
28
+ };
29
+
30
+ return @http_client.post(PxModule::API_V1_CAPTCHA, request_body, headers, @px_config[:api_timeout])
31
+
32
+ end
33
+
34
+ def verify(px_ctx)
35
+ captcha_validated = false
36
+ begin
37
+ if(!px_ctx.context.key?(:px_captcha))
38
+ return captcha_validated, px_ctx
39
+ end
40
+ captcha, vid, uuid = px_ctx.context[:px_captcha].split(':', 3)
41
+ if captcha.nil? || vid.nil? || uuid.nil?
42
+ return captcha_validated, px_ctx
43
+ end
44
+
45
+ px_ctx.context[:vid] = vid
46
+ px_ctx.context[:uuid] = uuid
47
+ response = send_captcha_request(vid, uuid, captcha, px_ctx)
48
+
49
+ if (response.status_code == 200)
50
+ response_body = eval(response.body)
51
+ if ( response_body[:status] == 0 )
52
+ captcha_validated = true
53
+ end
54
+ end
55
+
56
+ return captcha_validated, px_ctx
57
+
58
+ rescue Exception => e
59
+ @logger.error("PerimeterxCaptchaValidator[verify]: failed, returning false")
60
+ return captcha_validated, px_ctx
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,69 @@
1
+ require 'perimeterx/utils/px_constants'
2
+ require 'perimeterx/internal/perimeter_x_cookie'
3
+ require 'perimeterx/internal/perimeter_x_cookie_v1'
4
+ require 'perimeterx/internal/perimeter_x_cookie_v3'
5
+
6
+ module PxModule
7
+ class PerimeterxCookieValidator
8
+
9
+ attr_accessor :px_config
10
+
11
+ def initialize(px_config)
12
+ @px_config = px_config
13
+ @logger = px_config[:logger]
14
+ end
15
+
16
+
17
+ def verify(px_ctx)
18
+ begin
19
+ # Case no cookie
20
+ if px_ctx.context[:px_cookie].empty?
21
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie not found")
22
+ px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
23
+ return false, px_ctx
24
+ end
25
+
26
+ # Deserialize cookie start
27
+ cookie = PerimeterxCookie.px_cookie_factory(px_ctx, @px_config)
28
+ if (!cookie.deserialize())
29
+ @logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
30
+ px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
31
+ return false, px_ctx
32
+ end
33
+ px_ctx.context[:decoded_cookie] = cookie.decoded_cookie
34
+ px_ctx.context[:score] = cookie.cookie_score()
35
+ px_ctx.context[:uuid] = cookie.decoded_cookie[:u]
36
+ px_ctx.context[:vid] = cookie.decoded_cookie[:v]
37
+ px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
38
+ px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
39
+
40
+ if (cookie.expired?)
41
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
42
+ px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
43
+ return false, px_ctx
44
+ end
45
+
46
+ if (cookie.high_score?)
47
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
48
+ px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_HIGH_SCORE
49
+ return false, px_ctx
50
+ end
51
+
52
+ if (!cookie.secured?)
53
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
54
+ px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
55
+ return false, px_ctx
56
+ end
57
+
58
+ @logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
59
+
60
+ return true, px_ctx
61
+ rescue Exception => e
62
+ @logger.error("PerimeterxCookieValidator:[verify]: exception while verifying cookie => #{e.message}")
63
+ px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
64
+ return false, px_ctx
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,110 @@
1
+ require 'perimeterx/internal/clients/perimeter_x_risk_client'
2
+
3
+ module PxModule
4
+ class PerimeterxS2SValidator < PerimeterxRiskClient
5
+
6
+ def initialize(px_config, http_client)
7
+ super(px_config, http_client)
8
+ @logger.debug("PerimeterxS2SValidator[initialize]")
9
+ end
10
+
11
+ def send_risk_request(px_ctx)
12
+ @logger.debug("PerimeterxS2SValidator[send_risk_request]: send_risk_request")
13
+
14
+ risk_mode = PxModule::RISK_MODE_ACTIVE
15
+ if @px_config[:module_mode] == PxModule::MONITOR_MODE
16
+ risk_mode = PxModule::RISK_MODE_MONITOR
17
+ end
18
+
19
+ request_body = {
20
+ 'request' => {
21
+ 'ip' => px_ctx.context[:ip],
22
+ 'headers' => format_headers(px_ctx),
23
+ 'uri' => px_ctx.context[:uri],
24
+ 'url' => px_ctx.context[:full_url]
25
+ },
26
+ 'additional' => {
27
+ 's2s_call_reason' => px_ctx.context[:s2s_call_reason],
28
+ 'module_version' => @px_config[:sdk_name],
29
+ 'http_method' => px_ctx.context[:http_method],
30
+ 'http_version' => px_ctx.context[:http_version],
31
+ 'risk_mode' => risk_mode
32
+ }
33
+ }
34
+ #Check for hmac
35
+ if px_ctx.context.key?(:cookie_hmac)
36
+ request_body[:additional][:px_cookie_hmac] = px_ctx.context[:cookie_hmac]
37
+ end
38
+
39
+
40
+ #Check for VID
41
+ if px_ctx.context.key?(:vid)
42
+ request_body[:vid] = px_ctx.context[:vid]
43
+ end
44
+
45
+ #Check for uuid
46
+ if px_ctx.context.key?(:uuid)
47
+ request_body[:uuid] = px_ctx.context[:uuid]
48
+ end
49
+
50
+ #S2S Call reason
51
+ decode_cookie_reasons = ['cookie_expired', 'cookie_validation_failed']
52
+ if decode_cookie_reasons.include? (px_ctx.context[:s2s_call_reason])
53
+ if (px_ctx.context.key?(:decoded_cookie))
54
+ request_body[:additional][:px_cookie] = px_ctx.context[:decoded_cookie]
55
+ end
56
+ end
57
+
58
+ # Prepare request
59
+ headers = {
60
+ "Authorization" => "Bearer #{@px_config[:auth_token]}" ,
61
+ "Content-Type" => "application/json"
62
+ };
63
+
64
+ # Custom risk handler
65
+ if (risk_mode == PxModule::ACTIVE_MODE && @px_config.key?(:custom_risk_handler))
66
+ response = @px_config[:custom_risk_handler].call(PxModule::API_V2_RISK, request_body, headers, @px_config[:api_timeout])
67
+ else
68
+ response = @http_client.post(PxModule::API_V2_RISK , request_body, headers)
69
+ end
70
+ return response
71
+ end
72
+
73
+ def verify(px_ctx)
74
+ @logger.debug("PerimeterxS2SValidator[verify]")
75
+ response = send_risk_request(px_ctx)
76
+ if (!response)
77
+ return px_ctx
78
+ end
79
+ px_ctx.context[:made_s2s_risk_api_call] = true
80
+
81
+ # From here response should be valid, if success or error
82
+ response_body = eval(response.content);
83
+ # When success
84
+ if (response.status == 200 && response_body.key?(:score) && response_body.key?(:action))
85
+ @logger.debug("PerimeterxS2SValidator[verify]: response ok")
86
+ score = response_body[:score]
87
+ px_ctx.context[:score] = score
88
+ px_ctx.context[:uuid] = response_body[:uuid]
89
+ px_ctx.context[:block_action] = px_ctx.set_block_action_type(response_body[:action])
90
+ if (response_body[:action] == 'j' && response_body.key?(:action_data) && response_body[:action_data].key?(:body))
91
+ px_ctx.context[:block_action_data] = response_body[:action_data][:body]
92
+ px_ctx.context[:blocking_reason] = 'challenge'
93
+ elsif (score >= @px_config[:blocking_score])
94
+ px_ctx.context[:blocking_reason] = 's2s_high_score'
95
+ end #end challange or blocking score
96
+ end #end success response
97
+
98
+ # When error
99
+ if(response.status != 200)
100
+ @logger.warn("PerimeterxS2SValidator[verify]: bad response, return code #{response.code}")
101
+ px_ctx.context[:uuid] = ""
102
+ px_ctx.context[:s2s_error_msg] = response_body[:message]
103
+ end
104
+
105
+ @logger.debug("PerimeterxS2SValidator[verify]: done")
106
+ return px_ctx
107
+ end #end method
108
+
109
+ end
110
+ end
@@ -0,0 +1,42 @@
1
+ module PxModule
2
+ # Misc
3
+ MONITOR_MODE = 1
4
+ ACTIVE_MODE = 2
5
+ RISK_MODE_ACTIVE = "active_blocking"
6
+ RISK_MODE_MONITOR = "monitor"
7
+ SDK_NAME = "RUBY SDK v#{PxModule::VERSION}"
8
+
9
+ # Routes
10
+ API_V1_S2S = "/api/v1/collector/s2s"
11
+ API_V1_CAPTCHA = "/api/v1/risk/captcha"
12
+ API_V2_RISK = "/api/v2/risk"
13
+
14
+ # Activity Types
15
+ BLOCK_ACTIVITY = "block"
16
+ PAGE_REQUESTED_ACTIVITY = "page_requested"
17
+
18
+ # PxContext
19
+ NO_COOKIE = "no_cookie"
20
+ INVALID_COOKIE = "invalid cookie"
21
+ EXPIRED_COOKIE = "cookie_expired"
22
+ COOKIE_HIGH_SCORE = "cookie_high_score"
23
+ COOKIE_VALIDATION_FAILED = "cookie_validation_failed"
24
+ COOKIE_DECRYPTION_FAILED = "cookie_decryption_failed"
25
+
26
+ # Templates
27
+ BLOCK_TEMPLATE = "block.mustache"
28
+ CAPTCHA_TEMPLATE = "captcha.mustache"
29
+
30
+ # Tempalte Props
31
+ PROP_REF_ID = :refId
32
+ PROP_APP_ID = :appId
33
+ PROP_VID = :vid
34
+ PROP_UUID = :uuid
35
+ PROP_LOGO_VISIBILITY = :logoVisibility
36
+ PROP_CUSTOM_LOGO = :customLogo
37
+ PROP_CSS_REF = :cssRef
38
+ PROP_JS_REF = :jsRef
39
+
40
+ VISIBLE = 'visible'
41
+ HIDDEN = 'hidden'
42
+ end
@@ -0,0 +1,55 @@
1
+ require "perimeterx/utils/px_logger"
2
+ require "httpclient"
3
+
4
+ module PxModule
5
+ class PxHttpClient
6
+ attr_accessor :px_config
7
+ attr_accessor :BASE_URL
8
+ attr_accessor :http_client
9
+
10
+ def initialize(px_config)
11
+ @px_config = px_config
12
+ @http_client = HTTPClient.new(:base_url => px_config[:perimeterx_server_host])
13
+ @logger = px_config[:logger]
14
+ @logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:perimeterx_server_host]}")
15
+ end
16
+
17
+ def post(path, body, headers, api_timeout = 0, timeoute = 0)
18
+ s = Time.now
19
+ begin
20
+ @logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
21
+ response = @http_client.post(path,
22
+ :header => headers,
23
+ :body => body.to_json(),
24
+ :timeout => api_timeout
25
+ )
26
+ rescue Net::OpenTimeout, Net::ReadTimeout => error
27
+ @logger.warn("PerimeterxS2SValidator[verify]: request timedout")
28
+ return false
29
+ end
30
+ e = Time.now
31
+ @logger.debug("PxHttpClient[post]: runtime: #{e-s}")
32
+ return response
33
+ end
34
+
35
+ def async_post(path, body, headers, api_timeout = 0, timeoute = 0)
36
+ @logger.debug("PxHttpClient[async_post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
37
+ s = Time.now
38
+ begin
39
+ @logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
40
+ response = @http_client.post_async(path,
41
+ :header => headers,
42
+ :body => body.to_json(),
43
+ :timeout => api_timeout
44
+ )
45
+ rescue Net::OpenTimeout, Net::ReadTimeout => error
46
+ @logger.warn("PerimeterxS2SValidator[verify]: request timedout")
47
+ return false
48
+ end
49
+ e = Time.now
50
+ @logger.debug("PxHttpClient[post]: runtime: #{e-s}")
51
+ return response
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,17 @@
1
+ require 'logger'
2
+ module PxModule
3
+
4
+ class PxLogger < Logger
5
+
6
+ def initialize(debug)
7
+ if debug
8
+ super(STDOUT)
9
+ else
10
+ super(nil)
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,31 @@
1
+ require 'mustache'
2
+ require 'perimeterx/utils/px_constants'
3
+ module PxModule
4
+ module PxTemplateFactory
5
+
6
+ def self.get_template(px_ctx, px_config)
7
+ logger = px_config[:logger]
8
+ if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == "challenge")
9
+ logger.debug("PxTemplateFactory[get_template]: px challange triggered")
10
+ return px_ctx.context[:block_action_data].html_safe
11
+ end
12
+
13
+ logger.debug("PxTemplateFactory[get_template]: rendering template")
14
+ template_type = px_config[:captcha_enabled] ? PxModule::CAPTCHA_TEMPLATE : BLOCK_TEMPLATE
15
+
16
+ Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}"
17
+ view = Mustache.new
18
+
19
+ view[PxModule::PROP_APP_ID] = px_config[:app_id]
20
+ view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
21
+ view[PxModule::PROP_VID] = px_ctx.context[:vid]
22
+ view[PxModule::PROP_UUID] = px_ctx.context[:uuid]
23
+ view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
24
+ view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
25
+ view[PxModule::PROP_JS_REF] = px_config[:js_ref]
26
+ view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
27
+
28
+ return view.render.html_safe
29
+ end
30
+ end #end class
31
+ end #end module