Skip to content

Commit 2e376e7

Browse files
authored
Merge pull request #43 from securenative/dev
Dev
2 parents eb80d72 + b9fc52b commit 2e376e7

16 files changed

+119
-45
lines changed

README.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ You can also create request context from requests:
111111

112112
```python
113113
from securenative.securenative import SecureNative
114-
from securenative.context.securenative_context import SecureNativeContext
115114
from securenative.models.event_options import EventOptions
116115
from securenative.enums.event_types import EventTypes
117116
from securenative.models.user_traits import UserTraits
@@ -120,7 +119,7 @@ from securenative.models.user_traits import UserTraits
120119
def track(request):
121120
securenative = SecureNative.get_instance()
122121

123-
context = SecureNativeContext.from_http_request(request)
122+
context = securenative.from_http_request(request)
124123
event_options = EventOptions(event=EventTypes.LOG_IN,
125124
user_id="1234",
126125
user_traits=UserTraits("Your Name", "[email protected]", "+1234567890"),
@@ -137,15 +136,14 @@ def track(request):
137136
```python
138137
from securenative.securenative import SecureNative
139138
from securenative.models.event_options import EventOptions
140-
from securenative.context.securenative_context import SecureNativeContext
141139
from securenative.enums.event_types import EventTypes
142140
from securenative.models.user_traits import UserTraits
143141

144142

145143
def verify(request):
146144
securenative = SecureNative.get_instance()
147145

148-
context = SecureNativeContext.from_http_request(request)
146+
context = securenative.from_http_request(request)
149147
event_options = EventOptions(event=EventTypes.LOG_IN,
150148
user_id="1234",
151149
user_traits=UserTraits("Your Name", "[email protected]", "+1234567890"),
@@ -173,3 +171,26 @@ def webhook_endpoint(request):
173171
is_verified = securenative.verify_request_payload(request)
174172
```
175173

174+
## Extract proxy headers from Cloudflare
175+
176+
You can specify custom header keys to allow extraction of client ip from different providers.
177+
This example demonstrates the usage of proxy headers for ip extraction from Cloudflare.
178+
179+
### Option 1: Using config file
180+
```ini
181+
SECURENATIVE_API_KEY: dsbe27fh3437r2yd326fg3fdg36f43
182+
SECURENATIVE_PROXY_HEADERS: ["CF-Connecting-IP"]
183+
```
184+
185+
Initialize sdk as shown above.
186+
187+
### Options 2: Using ConfigurationBuilder
188+
189+
```python
190+
from securenative.securenative import SecureNative
191+
from securenative.config.securenative_options import SecureNativeOptions
192+
193+
194+
options = SecureNativeOptions(api_key="YOUR_API_KEY", max_events=10, log_level="ERROR", proxy_headers=['CF-Connecting-IP'])
195+
securenative = SecureNative.init_with_options(options)
196+
```

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.1
1+
0.3.2

securenative/api_manager.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ def verify(self, event_options):
2323
Logger.debug("Verify event call")
2424
event = SDKEvent(event_options, self.options)
2525
try:
26-
res = json.loads(self.event_manager.send_sync(event, ApiRoute.VERIFY.value, False).text)
26+
res = json.loads(self.event_manager.send_sync(event, ApiRoute.VERIFY.value).text)
2727
return VerifyResult(res["riskLevel"], res["score"], res["triggers"])
2828
except Exception as e:
2929
Logger.debug("Failed to call verify; {}".format(e))
3030
if self.options.fail_over_strategy is FailOverStrategy.FAIL_OPEN.value:
31-
return VerifyResult(RiskLevel.LOW.value, 0, None)
32-
return VerifyResult(RiskLevel.HIGH.value, 1, None)
31+
return VerifyResult(RiskLevel.LOW.value, 0, [])
32+
return VerifyResult(RiskLevel.HIGH.value, 1, [])

securenative/config/configuration_manager.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from configparser import ConfigParser
33

44
from securenative.config.securenative_options import SecureNativeOptions
5-
from securenative.exceptions.securenative_config_exception import SecureNativeConfigException
5+
from securenative.logger import Logger
66

77

88
class ConfigurationManager(object):
@@ -15,7 +15,7 @@ def read_resource_file(cls, resource_path):
1515
try:
1616
cls.config.read(resource_path)
1717
except Exception as e:
18-
raise SecureNativeConfigException("Invalid config file; %s", e)
18+
Logger.debug("Invalid config file; {}, using default options".format(e))
1919

2020
properties = {}
2121
for key, value in cls.config.defaults().items():
@@ -63,4 +63,6 @@ def load_config(cls, resource_path):
6363
options.log_level),
6464
fail_over_strategy=cls._get_env_or_default(properties,
6565
"SECURENATIVE_FAILOVER_STRATEGY",
66-
options.fail_over_strategy))
66+
options.fail_over_strategy),
67+
proxy_headers=cls._get_env_or_default(properties, "SECURENATIVE_PROXY_HEADERS",
68+
options.proxy_headers))

securenative/config/securenative_options.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ class SecureNativeOptions(object):
55

66
def __init__(self, api_key=None, api_url="https://api.securenative.com/collector/api/v1", interval=1000,
77
max_events=1000, timeout=1500, auto_send=True, disable=False, log_level="CRITICAL",
8-
fail_over_strategy=FailOverStrategy.FAIL_OPEN.value):
8+
fail_over_strategy=FailOverStrategy.FAIL_OPEN.value, proxy_headers=None):
99

10+
if proxy_headers is None:
11+
proxy_headers = []
1012
if fail_over_strategy != FailOverStrategy.FAIL_OPEN.value and \
1113
fail_over_strategy != FailOverStrategy.FAIL_CLOSED.value:
1214
self.fail_over_strategy = FailOverStrategy.FAIL_OPEN.value
@@ -21,3 +23,4 @@ def __init__(self, api_key=None, api_url="https://api.securenative.com/collector
2123
self.auto_send = auto_send
2224
self.disable = disable
2325
self.log_level = log_level
26+
self.proxy_headers = proxy_headers

securenative/context/securenative_context.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def __init__(self, client_token=None, ip=None, remote_ip=None, headers=None, url
1414
self.body = body
1515

1616
@staticmethod
17-
def from_http_request(request):
17+
def from_http_request(request, options):
1818
try:
1919
client_token = request.cookies[RequestUtils.SECURENATIVE_COOKIE]
2020
except Exception:
@@ -28,6 +28,6 @@ def from_http_request(request):
2828
if Utils.is_null_or_empty(client_token):
2929
client_token = RequestUtils.get_secure_header_from_request(headers)
3030

31-
return SecureNativeContext(client_token, RequestUtils.get_client_ip_from_request(request),
31+
return SecureNativeContext(client_token, RequestUtils.get_client_ip_from_request(request, options),
3232
RequestUtils.get_remote_ip_from_request(request), headers, request.url,
3333
request.method, None)

securenative/event_manager.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def flush(self):
5454
for item in self.queue:
5555
self.http_client.post(item.url, item.body)
5656

57-
def send_sync(self, event, resource_path, retry):
57+
def send_sync(self, event, resource_path):
5858
if self.options.disable:
5959
Logger.warning("SDK is disabled. no operation will be performed")
6060
return
@@ -64,17 +64,9 @@ def send_sync(self, event, resource_path, retry):
6464
resource_path,
6565
json.dumps(EventManager.serialize(event))
6666
)
67-
if res.status_code != 200:
68-
Logger.info("SecureNative failed to call endpoint {} with event {}. adding back to queue".format(
69-
resource_path, event))
70-
item = QueueItem(
71-
resource_path,
72-
json.dumps(EventManager.serialize(event)),
73-
retry
74-
)
75-
self.queue.append(item)
76-
if self._is_queue_full():
77-
self.queue = self.queue[:len(self.queue - 1)]
67+
if res is None or res.status_code != 200:
68+
Logger.info("SecureNative failed to call endpoint {} with event {}.".format(resource_path, event))
69+
7870
return res
7971

8072
def _is_queue_full(self):

securenative/http/securenative_http_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import requests
2+
from requests import Timeout
23

34
from securenative.utils.version_utils import VersionUtils
45

@@ -24,4 +25,8 @@ def _headers(self):
2425

2526
def post(self, path, body):
2627
url = "{}/{}".format(self.options.api_url, path)
27-
return requests.post(url=url, data=body, headers=self._headers())
28+
try:
29+
res = requests.post(url=url, data=body, headers=self._headers(), timeout=self.options.timeout / 1000)
30+
return res
31+
except Timeout:
32+
return None

securenative/securenative.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from securenative.api_manager import ApiManager
22
from securenative.config.configuration_manager import ConfigurationManager
33
from securenative.config.securenative_options import SecureNativeOptions
4+
from securenative.context.securenative_context import SecureNativeContext
45
from securenative.event_manager import EventManager
56
from securenative.exceptions.securenative_config_exception import SecureNativeConfigException
67
from securenative.exceptions.securenative_sdk_Illegal_state_exception import SecureNativeSDKIllegalStateException
@@ -75,6 +76,9 @@ def verify(self, event_options):
7576
def _flush(cls):
7677
cls._securenative = None
7778

79+
def from_http_request(self, request):
80+
return SecureNativeContext.from_http_request(request, self._options)
81+
7882
def verify_request_payload(self, request):
7983
request_signature = request.header[SignatureUtils.SignatureHeader]
8084
body = request.body

securenative/utils/request_utils.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,34 @@ def get_secure_header_from_request(headers):
1010
return ""
1111

1212
@staticmethod
13-
def get_client_ip_from_request(request):
13+
def get_client_ip_from_request(request, options):
14+
if options and len(options.proxy_headers) > 0:
15+
for header in options.proxy_headers:
16+
try:
17+
if request.environ.get(header) is not None:
18+
return request.environ.get(header)
19+
if request.headers[header] is not None:
20+
return request.headers[header]
21+
except Exception:
22+
try:
23+
if request.headers[header] is not None:
24+
return request.headers[header]
25+
except Exception:
26+
continue
27+
1428
try:
1529
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
1630
if x_forwarded_for:
1731
ip = x_forwarded_for.split(',')[-1].strip()
1832
else:
1933
ip = request.META.get('REMOTE_ADDR')
34+
35+
if ip is None or ip == "":
36+
ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.environ.get('REMOTE_ADDR', ""))
37+
2038
return ip
2139
except Exception:
22-
return request.environ.get('HTTP_X_FORWARDED_FOR', request.environ.get('REMOTE_ADDR', ""))
40+
return ""
2341

2442
@staticmethod
2543
def get_remote_ip_from_request(request):

0 commit comments

Comments
 (0)