perimeter_x 1.1.0 → 2.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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/Dockerfile +19 -43
- data/Gemfile.lock +36 -30
- data/Rakefile +1 -0
- data/changelog.md +63 -0
- data/examples/app/controllers/home_controller.rb +1 -1
- data/lib/perimeter_x.rb +152 -70
- data/lib/perimeterx/configuration.rb +71 -22
- data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +36 -15
- data/lib/perimeterx/internal/exceptions/px_config_exception.rb +6 -0
- data/lib/perimeterx/internal/{perimeter_x_cookie_v1.rb → payload/perimeter_x_cookie_v1.rb} +1 -1
- data/lib/perimeterx/internal/{perimeter_x_cookie_v3.rb → payload/perimeter_x_cookie_v3.rb} +1 -1
- data/lib/perimeterx/internal/{perimeter_x_cookie.rb → payload/perimeter_x_payload.rb} +12 -4
- data/lib/perimeterx/internal/payload/perimeter_x_token_v1.rb +38 -0
- data/lib/perimeterx/internal/payload/perimeter_x_token_v3.rb +36 -0
- data/lib/perimeterx/internal/perimeter_x_context.rb +66 -31
- data/lib/perimeterx/internal/validators/hash_schema_validator.rb +26 -0
- data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +29 -21
- data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +34 -10
- data/lib/perimeterx/utils/px_constants.rb +35 -17
- data/lib/perimeterx/utils/px_http_client.rb +29 -35
- data/lib/perimeterx/utils/px_template_factory.rb +18 -8
- data/lib/perimeterx/utils/templates/block_template.mustache +175 -0
- data/lib/perimeterx/utils/templates/ratelimit.mustache +9 -0
- data/lib/perimeterx/version.rb +1 -1
- data/perimeter_x.gemspec +5 -4
- data/readme.md +95 -32
- metadata +53 -24
- data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +0 -65
- data/lib/perimeterx/utils/templates/block.mustache +0 -146
- data/lib/perimeterx/utils/templates/captcha.mustache +0 -185
@@ -0,0 +1,26 @@
|
|
1
|
+
def validate_hash_schema(hash, schema)
|
2
|
+
hash.each do |key, value|
|
3
|
+
if schema.key?(key) && value != nil
|
4
|
+
# validate value types in hash are according to schema
|
5
|
+
if !schema[key][:types].include?(value.class)
|
6
|
+
raise PxConfigurationException.new("PerimeterX: Type of #{key} should be one of #{schema[key][:types]} but instead is #{value.class}")
|
7
|
+
end
|
8
|
+
|
9
|
+
# validate arrays elments types are according to schema
|
10
|
+
if value.class == Array
|
11
|
+
value.each do |element|
|
12
|
+
if !schema[key][:allowed_element_types].include?(element.class)
|
13
|
+
raise PxConfigurationException.new("PerimeterX: #{key} may only contain elements of the following types: #{schema[key][:allowed_element_types]} but includes element of type #{element.class}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# validate required fields exist in hash
|
21
|
+
schema.each do |key, value|
|
22
|
+
if value[:required] && hash[key].nil?
|
23
|
+
raise PxConfigurationException.new("PerimeterX: #{key} configuration is missing")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'perimeterx/utils/px_constants'
|
2
|
-
require 'perimeterx/internal/
|
3
|
-
require 'perimeterx/internal/
|
4
|
-
require 'perimeterx/internal/
|
2
|
+
require 'perimeterx/internal/payload/perimeter_x_payload'
|
3
|
+
require 'perimeterx/internal/payload/perimeter_x_token_v1'
|
4
|
+
require 'perimeterx/internal/payload/perimeter_x_token_v3'
|
5
|
+
require 'perimeterx/internal/payload/perimeter_x_cookie_v1'
|
6
|
+
require 'perimeterx/internal/payload/perimeter_x_cookie_v3'
|
5
7
|
|
6
8
|
module PxModule
|
7
9
|
class PerimeterxCookieValidator
|
@@ -16,57 +18,63 @@ module PxModule
|
|
16
18
|
|
17
19
|
def verify(px_ctx)
|
18
20
|
begin
|
19
|
-
# Case no cookie
|
20
21
|
if px_ctx.context[:px_cookie].empty?
|
21
|
-
@logger.warn("PerimeterxCookieValidator:[verify]: cookie
|
22
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
|
22
23
|
px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
|
23
24
|
return false, px_ctx
|
24
25
|
end
|
25
|
-
|
26
|
+
|
26
27
|
# Deserialize cookie start
|
27
|
-
cookie =
|
28
|
-
if
|
28
|
+
cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
|
29
|
+
if !cookie.deserialize()
|
29
30
|
@logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
|
31
|
+
px_ctx.context[:px_orig_cookie] = px_ctx.get_px_cookie
|
30
32
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
31
33
|
return false, px_ctx
|
32
34
|
end
|
33
35
|
px_ctx.context[:decoded_cookie] = cookie.decoded_cookie
|
34
36
|
px_ctx.context[:score] = cookie.cookie_score()
|
35
37
|
px_ctx.context[:uuid] = cookie.decoded_cookie[:u]
|
36
|
-
px_ctx.context[:vid] = cookie.decoded_cookie[:v]
|
37
38
|
px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
|
38
39
|
px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
|
39
40
|
|
40
|
-
|
41
|
+
vid = cookie.decoded_cookie[:v]
|
42
|
+
if vid.is_a?(String) && vid.match(PxModule::VID_REGEX)
|
43
|
+
px_ctx.context[:vid_source] = "risk_cookie"
|
44
|
+
px_ctx.context[:vid] = vid
|
45
|
+
end
|
46
|
+
|
47
|
+
if cookie.expired?
|
41
48
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
|
42
49
|
px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
|
43
50
|
return false, px_ctx
|
44
51
|
end
|
45
52
|
|
46
|
-
if
|
53
|
+
if cookie.high_score?
|
47
54
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
|
48
|
-
px_ctx.context[:
|
55
|
+
px_ctx.context[:blocking_reason] = 'cookie_high_score'
|
49
56
|
return true, px_ctx
|
50
57
|
end
|
51
58
|
|
52
|
-
if
|
59
|
+
if !cookie.secured?
|
53
60
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
|
54
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
|
61
|
+
px_ctx.context[:s2s_call_reason] = PxModule:: COOKIE_VALIDATION_FAILED
|
62
|
+
return false, px_ctx
|
63
|
+
end
|
64
|
+
|
65
|
+
if px_ctx.context[:sensitive_route]
|
66
|
+
@logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
|
67
|
+
px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
|
55
68
|
return false, px_ctx
|
56
69
|
end
|
57
|
-
|
58
|
-
if (px_ctx.context[:sensitive_route])
|
59
|
-
@logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
|
60
|
-
px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
|
61
|
-
return false, px_ctx
|
62
|
-
end
|
63
70
|
|
64
71
|
@logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
|
65
72
|
|
73
|
+
px_ctx.context[:pass_reason] = 'cookie'
|
66
74
|
return true, px_ctx
|
67
75
|
rescue Exception => e
|
68
76
|
@logger.error("PerimeterxCookieValidator:[verify]: exception while verifying cookie => #{e.message}")
|
69
|
-
px_ctx.context[:px_orig_cookie] =
|
77
|
+
px_ctx.context[:px_orig_cookie] = px_ctx.context[:px_cookie]
|
70
78
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
71
79
|
return false, px_ctx
|
72
80
|
end
|
@@ -5,11 +5,11 @@ module PxModule
|
|
5
5
|
|
6
6
|
def initialize(px_config, http_client)
|
7
7
|
super(px_config, http_client)
|
8
|
-
@logger.debug(
|
8
|
+
@logger.debug('PerimeterxS2SValidator[initialize]')
|
9
9
|
end
|
10
10
|
|
11
11
|
def send_risk_request(px_ctx)
|
12
|
-
@logger.debug(
|
12
|
+
@logger.debug('PerimeterxS2SValidator[send_risk_request]: send_risk_request')
|
13
13
|
|
14
14
|
risk_mode = PxModule::RISK_MODE_ACTIVE
|
15
15
|
if @px_config[:module_mode] == PxModule::MONITOR_MODE
|
@@ -31,6 +31,17 @@ module PxModule
|
|
31
31
|
:risk_mode => risk_mode
|
32
32
|
}
|
33
33
|
}
|
34
|
+
|
35
|
+
#Check for cookie_origin
|
36
|
+
if !px_ctx.context[:px_cookie].empty?
|
37
|
+
request_body[:additional][:cookie_origin] = px_ctx.context[:cookie_origin]
|
38
|
+
end
|
39
|
+
|
40
|
+
#Override s2s_call_reason in case of mobile error
|
41
|
+
if !px_ctx.context[:mobile_error].nil?
|
42
|
+
request_body[:additional][:s2s_call_reason] = "mobile_error_#{px_ctx.context[:mobile_error]}"
|
43
|
+
end
|
44
|
+
|
34
45
|
#Check for hmac
|
35
46
|
@logger.debug("px_ctx cookie_hmac key = #{px_ctx.context.key?(:cookie_hmac)}, value is: #{px_ctx.context[:cookie_hmac]}")
|
36
47
|
if px_ctx.context.key?(:cookie_hmac)
|
@@ -66,11 +77,19 @@ module PxModule
|
|
66
77
|
};
|
67
78
|
|
68
79
|
# Custom risk handler
|
80
|
+
risk_start = Time.now
|
69
81
|
if (risk_mode == PxModule::ACTIVE_MODE && @px_config.key?(:custom_risk_handler))
|
70
|
-
response = @px_config[:custom_risk_handler].call(PxModule::
|
82
|
+
response = @px_config[:custom_risk_handler].call(PxModule::API_V3_RISK, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
71
83
|
else
|
72
|
-
response = @http_client.post(PxModule::
|
84
|
+
response = @http_client.post(PxModule::API_V3_RISK , request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
85
|
+
end
|
86
|
+
|
87
|
+
# Set risk_rtt
|
88
|
+
if(response)
|
89
|
+
risk_end = Time.now
|
90
|
+
px_ctx.context[:risk_rtt] = ((risk_end-risk_start)*1000).round
|
73
91
|
end
|
92
|
+
|
74
93
|
return response
|
75
94
|
end
|
76
95
|
|
@@ -78,14 +97,15 @@ module PxModule
|
|
78
97
|
@logger.debug("PerimeterxS2SValidator[verify]")
|
79
98
|
response = send_risk_request(px_ctx)
|
80
99
|
if (!response)
|
100
|
+
px_ctx.context[:pass_reason] = "s2s_timeout"
|
81
101
|
return px_ctx
|
82
102
|
end
|
83
103
|
px_ctx.context[:made_s2s_risk_api_call] = true
|
84
104
|
|
85
105
|
# From here response should be valid, if success or error
|
86
|
-
response_body = eval(response.
|
106
|
+
response_body = eval(response.body);
|
87
107
|
# When success
|
88
|
-
if (response.
|
108
|
+
if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action) && response_body.key?(:status) && response_body[:status] == 0 )
|
89
109
|
@logger.debug("PerimeterxS2SValidator[verify]: response ok")
|
90
110
|
score = response_body[:score]
|
91
111
|
px_ctx.context[:score] = score
|
@@ -96,14 +116,18 @@ module PxModule
|
|
96
116
|
px_ctx.context[:blocking_reason] = 'challenge'
|
97
117
|
elsif (score >= @px_config[:blocking_score])
|
98
118
|
px_ctx.context[:blocking_reason] = 's2s_high_score'
|
119
|
+
else
|
120
|
+
px_ctx.context[:pass_reason] = 's2s'
|
99
121
|
end #end challange or blocking score
|
100
122
|
end #end success response
|
101
123
|
|
102
124
|
# When error
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
px_ctx.context[:
|
125
|
+
risk_error_status = response_body && response_body.key?(:status) && response_body[:status] == -1
|
126
|
+
if(response.code != 200 || risk_error_status)
|
127
|
+
@logger.warn("PerimeterxS2SValidator[verify]: bad response, returned code #{response.code} #{risk_error_status ? "risk status: -1" : ""}")
|
128
|
+
px_ctx.context[:pass_reason] = 'request_failed'
|
129
|
+
px_ctx.context[:uuid] = (!response_body || response_body[:uuid].nil?)? "" : response_body[:uuid]
|
130
|
+
px_ctx.context[:s2s_error_msg] = !response_body || response_body[:message].nil? ? 'unknown' : response_body[:message]
|
107
131
|
end
|
108
132
|
|
109
133
|
@logger.debug("PerimeterxS2SValidator[verify]: done")
|
@@ -4,33 +4,34 @@ module PxModule
|
|
4
4
|
# Misc
|
5
5
|
MONITOR_MODE = 1
|
6
6
|
ACTIVE_MODE = 2
|
7
|
-
RISK_MODE_ACTIVE =
|
8
|
-
RISK_MODE_MONITOR =
|
7
|
+
RISK_MODE_ACTIVE = 'active_blocking'
|
8
|
+
RISK_MODE_MONITOR = 'monitor'
|
9
9
|
SDK_NAME = "RUBY SDK v#{PxModule::VERSION}"
|
10
10
|
|
11
11
|
# Routes
|
12
|
-
API_V1_S2S =
|
13
|
-
|
14
|
-
API_V2_RISK = "/api/v2/risk"
|
12
|
+
API_V1_S2S = '/api/v1/collector/s2s'
|
13
|
+
API_V3_RISK = '/api/v3/risk'
|
15
14
|
|
16
15
|
# Activity Types
|
17
|
-
BLOCK_ACTIVITY =
|
18
|
-
PAGE_REQUESTED_ACTIVITY =
|
16
|
+
BLOCK_ACTIVITY = 'block'
|
17
|
+
PAGE_REQUESTED_ACTIVITY = 'page_requested'
|
19
18
|
|
20
19
|
# PxContext
|
21
|
-
NO_COOKIE =
|
22
|
-
INVALID_COOKIE =
|
23
|
-
EXPIRED_COOKIE =
|
24
|
-
COOKIE_HIGH_SCORE =
|
25
|
-
COOKIE_VALIDATION_FAILED =
|
26
|
-
COOKIE_DECRYPTION_FAILED =
|
27
|
-
SENSITIVE_ROUTE =
|
20
|
+
NO_COOKIE = 'no_cookie'
|
21
|
+
INVALID_COOKIE = 'invalid_cookie'
|
22
|
+
EXPIRED_COOKIE = 'cookie_expired'
|
23
|
+
COOKIE_HIGH_SCORE = 'cookie_high_score'
|
24
|
+
COOKIE_VALIDATION_FAILED = 'cookie_validation_failed'
|
25
|
+
COOKIE_DECRYPTION_FAILED = 'cookie_decryption_failed'
|
26
|
+
SENSITIVE_ROUTE = 'sensitive_route'
|
28
27
|
|
29
28
|
# Templates
|
30
|
-
|
31
|
-
|
29
|
+
CHALLENGE_TEMPLATE = 'block_template'
|
30
|
+
TEMPLATE_EXT = '.mustache'
|
31
|
+
RATELIMIT_TEMPLATE = 'ratelimit'
|
32
32
|
|
33
|
-
|
33
|
+
|
34
|
+
# Template Props
|
34
35
|
PROP_REF_ID = :refId
|
35
36
|
PROP_APP_ID = :appId
|
36
37
|
PROP_VID = :vid
|
@@ -39,7 +40,24 @@ module PxModule
|
|
39
40
|
PROP_CUSTOM_LOGO = :customLogo
|
40
41
|
PROP_CSS_REF = :cssRef
|
41
42
|
PROP_JS_REF = :jsRef
|
43
|
+
PROP_BLOCK_SCRIPT = :blockScript
|
44
|
+
PROP_JS_CLIENT_SRC = :jsClientSrc
|
45
|
+
PROP_HOST_URL = :hostUrl
|
46
|
+
PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
|
47
|
+
|
48
|
+
# Hosts
|
49
|
+
CLIENT_HOST = 'client.px-cloud.net'
|
50
|
+
CAPTCHA_HOST = 'captcha.px-cloud.net'
|
42
51
|
|
43
52
|
VISIBLE = 'visible'
|
44
53
|
HIDDEN = 'hidden'
|
54
|
+
|
55
|
+
# Mobile SDK
|
56
|
+
TOKEN_HEADER = 'X-PX-AUTHORIZATION'
|
57
|
+
ORIGINAL_TOKEN_HEADER = 'X-PX-ORIGINAL-TOKEN'
|
58
|
+
|
59
|
+
# Regular Expressions
|
60
|
+
VID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
|
61
|
+
MOBILE_TOKEN_V3_REGEX = /\A3:(.*)\z/
|
62
|
+
MOBILE_ERROR_REGEX = /\A[0-9]\z/
|
45
63
|
end
|
@@ -1,53 +1,47 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'perimeterx/utils/px_logger'
|
2
|
+
require 'typhoeus'
|
3
|
+
require 'concurrent'
|
3
4
|
|
4
5
|
module PxModule
|
5
6
|
class PxHttpClient
|
7
|
+
include Concurrent::Async
|
8
|
+
|
6
9
|
attr_accessor :px_config
|
7
|
-
attr_accessor :
|
8
|
-
attr_accessor :http_client
|
10
|
+
attr_accessor :px_client
|
9
11
|
|
10
12
|
def initialize(px_config)
|
11
13
|
@px_config = px_config
|
12
|
-
@http_client = HTTPClient.new(:base_url => px_config[:perimeterx_server_host])
|
13
14
|
@logger = px_config[:logger]
|
14
|
-
@logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:
|
15
|
+
@logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:backend_url]}")
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
18
|
+
# Runs a POST command to Perimeter X servers
|
19
|
+
# Params:
|
20
|
+
# +path+:: string containing uri
|
21
|
+
# +body+:: hash object, containing the request body, must be converted to json format
|
22
|
+
# +headers+:: hash object, hold headers
|
23
|
+
# +api_timeout+:: int, sets the timeout for a request
|
24
|
+
# +connection_timeout+:: int, sets the timeout for opening a connection
|
34
25
|
|
35
|
-
def
|
36
|
-
@logger.debug("PxHttpClient[async_post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
|
26
|
+
def post(path, body, headers, api_timeout = 1, connection_timeout = 1)
|
37
27
|
s = Time.now
|
38
28
|
begin
|
39
29
|
@logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
|
40
|
-
response =
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
30
|
+
response = Typhoeus.post(
|
31
|
+
"#{px_config[:backend_url]}#{path}",
|
32
|
+
headers: headers,
|
33
|
+
body: body.to_json,
|
34
|
+
timeout: api_timeout,
|
35
|
+
connecttimeout: connection_timeout
|
36
|
+
)
|
37
|
+
if response.timed_out?
|
38
|
+
@logger.warn('PerimeterxS2SValidator[verify]: request timed out')
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
ensure
|
42
|
+
e = Time.now
|
43
|
+
@logger.debug("PxHttpClient[post]: runtime: #{(e-s) * 1000.0}")
|
48
44
|
end
|
49
|
-
e = Time.now
|
50
|
-
@logger.debug("PxHttpClient[post]: runtime: #{e-s}")
|
51
45
|
return response
|
52
46
|
end
|
53
47
|
|
@@ -3,19 +3,25 @@ require 'perimeterx/utils/px_constants'
|
|
3
3
|
module PxModule
|
4
4
|
module PxTemplateFactory
|
5
5
|
|
6
|
-
def self.get_template(px_ctx, px_config)
|
6
|
+
def self.get_template(px_ctx, px_config, px_template_object)
|
7
7
|
logger = px_config[:logger]
|
8
|
-
if (px_config[:challenge_enabled] && px_ctx.context[:block_action] ==
|
9
|
-
logger.debug(
|
8
|
+
if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
|
9
|
+
logger.debug('PxTemplateFactory[get_template]: px challange triggered')
|
10
10
|
return px_ctx.context[:block_action_data].html_safe
|
11
11
|
end
|
12
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
13
|
view = Mustache.new
|
18
14
|
|
15
|
+
if (px_ctx.context[:block_action] == 'rate_limit')
|
16
|
+
logger.debug('PxTemplateFactory[get_template]: rendering ratelimit template')
|
17
|
+
template_type = RATELIMIT_TEMPLATE
|
18
|
+
else
|
19
|
+
logger.debug('PxTemplateFactory[get_template]: rendering template')
|
20
|
+
template_type = CHALLENGE_TEMPLATE
|
21
|
+
end
|
22
|
+
|
23
|
+
Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
|
24
|
+
|
19
25
|
view[PxModule::PROP_APP_ID] = px_config[:app_id]
|
20
26
|
view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
|
21
27
|
view[PxModule::PROP_VID] = px_ctx.context[:vid]
|
@@ -23,9 +29,13 @@ module PxModule
|
|
23
29
|
view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
|
24
30
|
view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
|
25
31
|
view[PxModule::PROP_JS_REF] = px_config[:js_ref]
|
32
|
+
view[PxModule::PROP_HOST_URL] = "https://collector-#{px_config[:app_id]}.perimeterx.net"
|
26
33
|
view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
|
34
|
+
view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
|
35
|
+
view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
|
36
|
+
view[PxModule::PROP_FIRST_PARTY_ENABLED] = false
|
27
37
|
|
28
38
|
return view.render.html_safe
|
29
39
|
end
|
30
40
|
end #end class
|
31
|
-
end #end module
|
41
|
+
end #end module
|
@@ -0,0 +1,175 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<title>Access to this page has been denied.</title>
|
7
|
+
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300" rel="stylesheet">
|
8
|
+
<style>
|
9
|
+
html, body {
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
font-family: 'Open Sans', sans-serif;
|
13
|
+
color: #000;
|
14
|
+
}
|
15
|
+
|
16
|
+
a {
|
17
|
+
color: #c5c5c5;
|
18
|
+
text-decoration: none;
|
19
|
+
}
|
20
|
+
|
21
|
+
.container {
|
22
|
+
align-items: center;
|
23
|
+
display: flex;
|
24
|
+
flex: 1;
|
25
|
+
justify-content: space-between;
|
26
|
+
flex-direction: column;
|
27
|
+
height: 100%;
|
28
|
+
}
|
29
|
+
|
30
|
+
.container > div {
|
31
|
+
width: 100%;
|
32
|
+
display: flex;
|
33
|
+
justify-content: center;
|
34
|
+
}
|
35
|
+
|
36
|
+
.container > div > div {
|
37
|
+
display: flex;
|
38
|
+
width: 80%;
|
39
|
+
}
|
40
|
+
|
41
|
+
.customer-logo-wrapper {
|
42
|
+
padding-top: 2rem;
|
43
|
+
flex-grow: 0;
|
44
|
+
background-color: #fff;
|
45
|
+
visibility: {{logoVisibility}};
|
46
|
+
}
|
47
|
+
|
48
|
+
.customer-logo {
|
49
|
+
border-bottom: 1px solid #000;
|
50
|
+
}
|
51
|
+
|
52
|
+
.customer-logo > img {
|
53
|
+
padding-bottom: 1rem;
|
54
|
+
max-height: 50px;
|
55
|
+
max-width: 100%;
|
56
|
+
}
|
57
|
+
|
58
|
+
.page-title-wrapper {
|
59
|
+
flex-grow: 2;
|
60
|
+
}
|
61
|
+
|
62
|
+
.page-title {
|
63
|
+
flex-direction: column-reverse;
|
64
|
+
}
|
65
|
+
|
66
|
+
.content-wrapper {
|
67
|
+
flex-grow: 5;
|
68
|
+
}
|
69
|
+
|
70
|
+
.content {
|
71
|
+
flex-direction: column;
|
72
|
+
}
|
73
|
+
|
74
|
+
.page-footer-wrapper {
|
75
|
+
align-items: center;
|
76
|
+
flex-grow: 0.2;
|
77
|
+
background-color: #000;
|
78
|
+
color: #c5c5c5;
|
79
|
+
font-size: 70%;
|
80
|
+
}
|
81
|
+
|
82
|
+
@media (min-width: 768px) {
|
83
|
+
html, body {
|
84
|
+
height: 100%;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
</style>
|
88
|
+
<!-- Custom CSS -->
|
89
|
+
{{#cssRef}}
|
90
|
+
<link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
|
91
|
+
{{/cssRef}}
|
92
|
+
</head>
|
93
|
+
|
94
|
+
<body>
|
95
|
+
<section class="container">
|
96
|
+
<div class="customer-logo-wrapper">
|
97
|
+
<div class="customer-logo">
|
98
|
+
<img src="{{customLogo}}" alt="Logo"/>
|
99
|
+
</div>
|
100
|
+
</div>
|
101
|
+
<div class="page-title-wrapper">
|
102
|
+
<div class="page-title">
|
103
|
+
<h1>Please verify you are a human</h1>
|
104
|
+
</div>
|
105
|
+
</div>
|
106
|
+
<div class="content-wrapper">
|
107
|
+
<div class="content">
|
108
|
+
|
109
|
+
<div id="px-captcha">
|
110
|
+
</div>
|
111
|
+
<p>
|
112
|
+
Access to this page has been denied because we believe you are using automation tools to browse the
|
113
|
+
website.
|
114
|
+
</p>
|
115
|
+
<p>
|
116
|
+
This may happen as a result of the following:
|
117
|
+
</p>
|
118
|
+
<ul>
|
119
|
+
<li>
|
120
|
+
Javascript is disabled or blocked by an extension (ad blockers for example)
|
121
|
+
</li>
|
122
|
+
<li>
|
123
|
+
Your browser does not support cookies
|
124
|
+
</li>
|
125
|
+
</ul>
|
126
|
+
<p>
|
127
|
+
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
128
|
+
them from loading.
|
129
|
+
</p>
|
130
|
+
<p>
|
131
|
+
Reference ID: #{{refId}}
|
132
|
+
</p>
|
133
|
+
</div>
|
134
|
+
</div>
|
135
|
+
<div class="page-footer-wrapper">
|
136
|
+
<div class="page-footer">
|
137
|
+
<p>
|
138
|
+
Powered by
|
139
|
+
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
140
|
+
, Inc.
|
141
|
+
</p>
|
142
|
+
</div>
|
143
|
+
</div>
|
144
|
+
</section>
|
145
|
+
<!-- Px -->
|
146
|
+
<script>
|
147
|
+
window._pxAppId = '{{appId}}';
|
148
|
+
window._pxJsClientSrc = '{{{jsClientSrc}}}';
|
149
|
+
window._pxFirstPartyEnabled = {{firstPartyEnabled}};
|
150
|
+
window._pxVid = '{{vid}}';
|
151
|
+
window._pxUuid = '{{uuid}}';
|
152
|
+
window._pxHostUrl = '{{{hostUrl}}}';
|
153
|
+
</script>
|
154
|
+
<script>
|
155
|
+
var s = document.createElement('script');
|
156
|
+
s.src = '{{{blockScript}}}';
|
157
|
+
var p = document.getElementsByTagName('head')[0];
|
158
|
+
p.insertBefore(s, null);
|
159
|
+
if ({{firstPartyEnabled}}) {
|
160
|
+
s.onerror = function () {
|
161
|
+
s = document.createElement('script');
|
162
|
+
var suffixIndex = '{{{blockScript}}}'.indexOf('captcha.js');
|
163
|
+
var temperedBlockScript = '{{{blockScript}}}'.substring(suffixIndex);
|
164
|
+
s.src = '//captcha.px-cdn.net/{{appId}}/' + temperedBlockScript;
|
165
|
+
p.parentNode.insertBefore(s, p);
|
166
|
+
};
|
167
|
+
}
|
168
|
+
</script>
|
169
|
+
|
170
|
+
<!-- Custom Script -->
|
171
|
+
{{#jsRef}}
|
172
|
+
<script src="{{{jsRef}}}"></script>
|
173
|
+
{{/jsRef}}
|
174
|
+
</body>
|
175
|
+
</html>
|