Skip to content

Commit 79fc4bc

Browse files
alexbpxYaron Schwimmer
authored andcommitted
Dev refactor config (#17)
* Fxied errors * Config refactoring * Config * Deleted Notes * Feactored static files check * Refactored literals * Block activity * Block action added to block acitivity * Removed socket_ip from context * property * Whitelist and sensitive route capabilities * Update px_constants.py * Update px_activities_client.py * |Removed monitor_mode
1 parent e05ada9 commit 79fc4bc

14 files changed

+320
-168
lines changed

perimeterx/middleware.py

Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,70 @@
1-
from px_logger import Logger
21
import px_context
32
import px_activities_client
43
import px_cookie_validator
54
import px_httpc
65
import px_blocker
76
import px_api
8-
import px_template
9-
from px_proxy import PXProxy
10-
import Cookie
117
import px_constants
8+
import px_utils
9+
from px_proxy import PXProxy
10+
from px_config import PXConfig
11+
1212

1313
class PerimeterX(object):
1414
def __init__(self, app, config=None):
1515
self.app = app
1616
# merging user's defined configurations with the default one
17-
self.config = {
18-
'blocking_score': 60,
19-
'debug_mode': False,
20-
'perimeterx_server_host': 'sapi.perimeterx.net',
21-
'captcha_enabled': True,
22-
'server_calls_enabled': True,
23-
'encryption_enabled': True,
24-
'sensitive_headers': ['cookie', 'cookies'],
25-
'send_page_activities': True,
26-
'api_timeout': 1,
27-
'custom_logo': None,
28-
'css_ref': None,
29-
'js_ref': None,
30-
'is_mobile': False,
31-
'first_party': True,
32-
'first_party_xhr_enabled': True,
33-
'monitor_mode': px_constants.MODULE_MODE_MONITORING,
34-
}
35-
36-
self.config = dict(self.config.items() + config.items())
37-
self.config['logger'] = logger = Logger(self.config['debug_mode'])
38-
if not config['app_id']:
17+
px_config = PXConfig(config)
18+
logger = px_config.logger
19+
if not px_config.app_id:
3920
logger.error('PX App ID is missing')
4021
raise ValueError('PX App ID is missing')
41-
url = px_constants.COLLECTOR_URL
4222

43-
self.config['collector_url'] = url.format(config.get('app_id').lower())
4423
# if APP_ID is not set, use the deafult perimeterx server - else, use the appid specific sapi.
45-
self.config['perimeterx_server_host'] = 'sapi.perimeterx.net' if self.config['app_id'] == 'PX_APP_ID' else 'sapi-' + self.config['app_id'].lower() + '.perimeterx.net'
46-
if not config['auth_token']:
24+
if not px_config.auth_token:
4725
logger.error('PX Auth Token is missing')
4826
raise ValueError('PX Auth Token is missing')
4927

50-
if not config['cookie_key']:
28+
if not px_config.cookie_key:
5129
logger.error('PX Cookie Key is missing')
5230
raise ValueError('PX Cookie Key is missing')
53-
self.reverse_proxy_prefix = self.config['app_id'][2:].lower()
54-
55-
self.PXBlocker = px_blocker.PXBlocker()
56-
px_httpc.init(self.config)
31+
self.reverse_proxy_prefix = px_config.app_id[2:].lower()
32+
if px_config.custom_request_handler:
33+
self.handle_verification = px_config.custom_request_handler.__get__(self, PerimeterX)
34+
self._PXBlocker = px_blocker.PXBlocker()
35+
self._config = px_config
36+
px_httpc.init(px_config)
5737

5838
def __call__(self, environ, start_response):
5939
return self._verify(environ, start_response)
6040

6141
def _verify(self, environ, start_response):
62-
logger = self.config['logger']
42+
config = self.config
43+
logger = config.logger
6344
try:
64-
ctx = px_context.build_context(environ, self.config)
45+
ctx = px_context.build_context(environ, config)
6546
uri = ctx.get('uri')
66-
px_proxy = PXProxy(self.config, ctx)
67-
if px_proxy.should_reverse_request(uri):
47+
px_proxy = PXProxy(config)
48+
if px_proxy.should_reverse_request(uri):
6849
return px_proxy.handle_reverse_request(self.config, ctx, start_response)
69-
if ctx.get('module_mode') == 'inactive' or is_static_file(ctx):
50+
if px_utils.is_static_file(ctx):
7051
logger.debug('Filter static file request. uri: ' + uri)
7152
return self.app(environ, start_response)
53+
if not self._config._module_enabled:
54+
logger.debug('Module is disabled, request will not be verified')
55+
return self.app(environ, start_response)
7256

73-
cookies = Cookie.SimpleCookie(environ.get('HTTP_COOKIE'))
57+
if ctx.get('whitelist'):
58+
logger.debug('The requested uri is whitelisted, passing request')
59+
return self.app(environ, start_response)
7460

7561
# PX Cookie verification
76-
if not px_cookie_validator.verify(ctx, self.config) and self.config.get('server_calls_enabled', True):
62+
if not px_cookie_validator.verify(ctx, config):
7763
# Server-to-Server verification fallback
7864
if not px_api.verify(ctx, self.config):
7965
return self.app(environ, start_response)
80-
66+
if config.custom_request_handler:
67+
return config.custom_request_handler(ctx, self.config, environ, start_response)
8168
return self.handle_verification(ctx, self.config, environ, start_response)
8269
except:
8370
logger.error("Cought exception, passing request")
@@ -86,33 +73,31 @@ def _verify(self, environ, start_response):
8673
def handle_verification(self, ctx, config, environ, start_response):
8774
score = ctx.get('risk_score', -1)
8875

89-
if score < config['blocking_score']:
76+
if score < config.blocking_score:
9077
return self.pass_traffic(environ, start_response, ctx)
9178

92-
if config.get('custom_block_handler', False):
79+
if config.custom_block_handler:
9380
px_activities_client.send_block_activity(ctx, config)
94-
return config['custom_block_handler'](ctx, start_response)
95-
elif config.get('module_mode') == px_constants.MODULE_MODE_BLOCKING:
96-
return self.PXBlocker.handle_blocking(ctx=ctx, config=config, start_response=start_response)
81+
return config.custom_block_handler(ctx, start_response)
82+
elif config.module_mode == px_constants.MODULE_MODE_BLOCKING:
83+
return self.px_blocker.handle_blocking(ctx=ctx, config=config, start_response=start_response)
9784
else:
9885
return self.pass_traffic(environ, start_response, ctx)
9986

10087
def pass_traffic(self, environ, start_response, ctx):
10188
details = {}
10289
if ctx.get('decoded_cookie', ''):
10390
details = {"px_cookie": ctx['decoded_cookie']}
104-
px_activities_client.send_to_perimeterx('page_requested', ctx, self.config, details)
91+
px_activities_client.send_to_perimeterx(px_constants.PAGE_REQUESTED_ACTIVITY, ctx, self.config, details)
10592
return self.app(environ, start_response)
10693

94+
@property
95+
def config(self):
96+
return self._config
97+
98+
@property
99+
def px_blocker(self):
100+
return self._PXBlocker
101+
107102

108-
def is_static_file(ctx):
109-
uri = ctx.get('uri', '')
110-
static_extensions = ['.css', '.bmp', '.tif', '.ttf', '.docx', '.woff2', '.js', '.pict', '.tiff', '.eot',
111-
'.xlsx', '.jpg', '.csv', '.eps', '.woff', '.xls', '.jpeg', '.doc', '.ejs', '.otf', '.pptx',
112-
'.gif', '.pdf', '.swf', '.svg', '.ps', '.ico', '.pls', '.midi', '.svgz', '.class', '.png',
113-
'.ppt', '.mid', 'webp', '.jar']
114103

115-
for ext in static_extensions:
116-
if uri.endswith(ext):
117-
return True
118-
return False

perimeterx/px_activities_client.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,18 @@ def send_activities():
2626
def send_to_perimeterx(activity_type, ctx, config, detail):
2727
global CONFIG
2828
try:
29-
if not config.get('server_calls_enabled', True):
30-
return
31-
32-
if activity_type == 'page_requested' and not config.get('send_page_activities', False):
29+
if activity_type == 'page_requested' and not config.send_page_activities:
3330
print 'Page activities disabled in config - skipping.'
3431
return
3532

36-
if len(CONFIG.keys()) == 0:
33+
if not CONFIG:
3734
CONFIG = config
3835

3936
_details = {
4037
'http_method': ctx.get('http_method', ''),
4138
'http_version': ctx.get('http_version', ''),
42-
'module_version': config.get('module_version', ''),
43-
'risk_mode': config.get('module_mode', '')
39+
'module_version': config.module_version,
40+
'risk_mode': config.module_mode,
4441
}
4542

4643
if len(detail.keys()) > 0:
@@ -50,8 +47,8 @@ def send_to_perimeterx(activity_type, ctx, config, detail):
5047
'type': activity_type,
5148
'headers': ctx.get('headers'),
5249
'timestamp': int(round(time.time() * 1000)),
53-
'socket_ip': ctx.get('socket_ip'),
54-
'px_app_id': config.get('app_id'),
50+
'socket_ip': ctx.get('ip'),
51+
'px_app_id': config.app_id,
5552
'url': ctx.get('full_url'),
5653
'details': _details,
5754
'vid': ctx.get('vid', ''),
@@ -64,16 +61,16 @@ def send_to_perimeterx(activity_type, ctx, config, detail):
6461

6562

6663
def send_block_activity(ctx, config):
67-
send_to_perimeterx('block', ctx, config, {
64+
send_to_perimeterx(px_constants.BLOCK_ACTIVITY, ctx, config, {
6865
'block_score': ctx.get('risk_score'),
6966
'client_uuid': ctx.get('uuid'),
7067
'block_reason': ctx.get('block_reason'),
71-
'http_method' : ctx.get('http_method'),
68+
'http_method': ctx.get('http_method'),
7269
'http_version': ctx.get('http_version'),
7370
'px_cookie': ctx.get('decoded_cookie'),
7471
'risk_rtt': ctx.get('risk_rtt'),
7572
#'cookie_origin':,
73+
'block_action': ctx.get('block_action', ''),
7674
'module_version': px_constants.MODULE_VERSION,
77-
'simulated_block': config.get('monitor_mode') is px_constants.MODULE_MODE_MONITORING
78-
75+
'simulated_block': config.monitor_mode is 0,
7976
})

perimeterx/px_api.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import sys
22
import px_httpc
33
import time
4+
import px_constants
45

56

67

78
def send_risk_request(ctx, config):
89
body = prepare_risk_body(ctx, config)
9-
return px_httpc.send('/api/v2/risk', body, config)
10+
return px_httpc.send(px_constants.API_RISK, body, config)
1011

1112

1213
def verify(ctx, config):
13-
logger = config['logger']
14+
logger = config.logger
1415
logger.debug("PXVerify")
1516
try:
1617
start = time.time()
@@ -24,7 +25,7 @@ def verify(ctx, config):
2425
ctx['uuid'] = response['uuid']
2526
ctx['block_action'] = response['action']
2627
ctx['risk_rtt'] = risk_rtt
27-
if score >= config['blocking_score']:
28+
if score >= config.blocking_score:
2829
logger.debug("PXVerify block score threshold reached, will initiate blocking")
2930
ctx['block_reason'] = 's2s_high_score'
3031
elif response['action'] is 'j' and response.get('action_data') is not None and response.get('action_data').get('body') is not None:
@@ -47,11 +48,11 @@ def verify(ctx, config):
4748

4849

4950
def prepare_risk_body(ctx, config):
50-
logger = config['logger']
51+
logger = config.logger
5152
logger.debug("PxAPI[send_risk_request]")
5253
body = {
5354
'request': {
54-
'ip': ctx.get('socket_ip'),
55+
'ip': ctx.get('ip'),
5556
'headers': format_headers(ctx.get('headers')),
5657
'uri': ctx.get('uri'),
5758
'url': ctx.get('full_url', '')
@@ -62,8 +63,8 @@ def prepare_risk_body(ctx, config):
6263
's2s_call_reason': ctx.get('s2s_call_reason', ''),
6364
'http_method': ctx.get('http_method', ''),
6465
'http_version': ctx.get('http_version', ''),
65-
'module_version': config.get('module_version', ''),
66-
'risk_mode': config.get('module_mode', ''),
66+
'module_version': config.module_version,
67+
'risk_mode': config.module_mode,
6768
'px_cookie_hmac': ctx.get('cookie_hmac', ''),
6869
'request_cookie_names': ctx.get('cookie_names', '')
6970
}

perimeterx/px_blocker.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ def handle_blocking(self, ctx, config, start_response):
3737
return str(blocking_response)
3838

3939
def prepare_properties(self, ctx, config):
40-
app_id = config.get('app_id').lower()
40+
app_id = config.app_id.lower()
4141
vid = ctx.get('vid') if ctx.get('vid') is not None else ''
4242
uuid = ctx.get('uuid')
43-
custom_logo = config.get('CUSTOM_LOGO') if config.get('CUSTOM_LOGO') is not None else ''
43+
custom_logo = config.custom_logo
4444
is_mobile_num = 1 if ctx.get('is_mobile') else 0
4545
captcha_uri = 'captcha.js?a={}&u={}&v={}&m={}'.format(ctx.get('block_action'), uuid, vid, is_mobile_num)
4646

47-
if config.get('first_party') and not ctx.get('is_mobile'):
47+
if config.first_party and not ctx.get('is_mobile'):
4848
prefix = app_id[2:]
4949
js_client_src = '/{}/{}'.format(prefix, px_constants.CLIENT_FP_PATH)
5050
captcha_src = '/{}/{}/{}'.format(prefix, px_constants.CAPTCHA_FP_PATH, captcha_uri)
@@ -60,12 +60,12 @@ def prepare_properties(self, ctx, config):
6060
'vid': vid,
6161
'uuid': uuid,
6262
'customLogo': custom_logo,
63-
'cssRef': config.get('css_ref'),
64-
'jsRef': config.get('js_ref'),
63+
'cssRef': config.css_ref,
64+
'jsRef': config.js_ref,
6565
'logoVisibility': 'visible' if custom_logo is not None else 'hidden',
6666
'hostUrl': host_url,
6767
'jsClientSrc': js_client_src,
68-
'firstPartyEnabled': config.get('first_party'),
68+
'firstPartyEnabled': config.first_party,
6969
'blockScript': captcha_src
7070
}
7171

0 commit comments

Comments
 (0)