Skip to content

Commit 1c5405d

Browse files
author
Inbal Tako
committed
Update readme and fix minor bugs
1 parent f4bbc1d commit 1c5405d

10 files changed

Lines changed: 222 additions & 40 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ ehthumbs_vista.db
261261
# Recycle Bin used on file shares
262262
$RECYCLE.BIN/
263263

264+
*.ini
264265
# Windows Installer files
265266
*.cab
266267
*.msi

README.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,181 @@ def sn_webhook_handler(headers, body):
9696
pass
9797

9898
```
99+
100+
<p align="center">
101+
<a href="https://www.securenative.com"><img src="https://user-images.githubusercontent.com/45174009/77826512-f023ed80-7120-11ea-80e0-58aacde0a84e.png" alt="SecureNative Logo"/></a>
102+
</p>
103+
104+
<p align="center">
105+
<b>A Cloud-Native Security Monitoring and Protection for Modern Applications</b>
106+
</p>
107+
<p align="center">
108+
<a href="https://github.com/securenative/securenative-python">
109+
<img alt="Github Actions" src="https://github.com/securenative/securenative-java/workflows/CI/badge.svg">
110+
</a>
111+
<a href="https://codecov.io/gh/securenative/securenative-python">
112+
<img src="https://codecov.io/gh/securenative/securenative-java/branch/master/graph/badge.svg" />
113+
</a>
114+
</p>
115+
<p align="center">
116+
<a href="https://docs.securenative.com">Documentation</a> |
117+
<a href="https://docs.securenative.com/quick-start">Quick Start</a> |
118+
<a href="https://blog.securenative.com">Blog</a> |
119+
<a href="">Chat with us on Slack!</a>
120+
</p>
121+
<hr/>
122+
123+
124+
[SecureNative](https://www.securenative.com/) performs user monitoring by analyzing user interactions with your application and various factors such as network, devices, locations and access patterns to stop and prevent account takeover attacks.
125+
126+
## Install the SDK
127+
128+
When using Maven, add the following dependency to your `pom.xml` file:
129+
```xml
130+
<dependency>
131+
<groupId>com.securenative.java</groupId>
132+
<artifactId>sdk-base</artifactId>
133+
<version>LATEST</version>
134+
</dependency>
135+
```
136+
137+
When using Gradle, add the following dependency to your `build.gradle` file:
138+
```gradle
139+
compile group: 'com.securenative.java', name: 'sdk-parent', version: '0.3.1', ext: 'pom'
140+
```
141+
142+
When using SBT, add the following dependency to your `build.sbt` file:
143+
```sbt
144+
libraryDependencies += "com.securenative.java" % "sdk-parent" % "0.3.1" pomOnly()
145+
```
146+
147+
## Initialize the SDK
148+
149+
To get your *API KEY*, login to your SecureNative account and go to project settings page:
150+
151+
### Option 1: Initialize via Config file
152+
SecureNative can automatically load your config from *securenative.properties* file or from the file that is specified in your *SECURENATIVE_CONFIG_FILE* env variable:
153+
154+
```java
155+
SecureNative secureNative = SecureNative.init();
156+
```
157+
### Option 2: Initialize via API Key
158+
159+
```java
160+
SecureNative secureNative = SecureNative.init("YOUR_API_KEY");
161+
```
162+
163+
### Option 3: Initialize via ConfigurationBuilder
164+
```java
165+
SecureNative secureNative = SecureNative.init(SecureNative.configBuilder()
166+
.withApiKey("API_KEY")
167+
.withMaxEvents(10)
168+
.withLogLevel("error")
169+
.build());
170+
```
171+
172+
## Getting SecureNative instance
173+
Once initialized, sdk will create a singleton instance which you can get:
174+
```java
175+
SecureNative secureNative = SecureNative.getInstance();
176+
```
177+
178+
## Tracking events
179+
180+
Once the SDK has been initialized, tracking requests sent through the SDK
181+
instance. Make sure you build event with the EventBuilder:
182+
183+
```java
184+
SecureNative secureNative = SecureNative.getInstance();
185+
186+
SecureNativeContext context = SecureNative.contextBuilder()
187+
.withIp("127.0.0.1")
188+
.withClientToken("SECURED_CLIENT_TOKEN")
189+
.withHeaders(Maps.defaultBuilder()
190+
.put("user-agent", "Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405")
191+
.build())
192+
.build();
193+
194+
EventOptions eventOptions = EventOptionsBuilder.builder(EventTypes.LOG_IN)
195+
.userId("USER_ID")
196+
.userTraits("USER_NAME", "USER_EMAIL")
197+
.context(context)
198+
.properties(Maps.builder()
199+
.put("prop1", "CUSTOM_PARAM_VALUE")
200+
.put("prop2", true)
201+
.put("prop3", 3)
202+
.build())
203+
.timestamp(new Date())
204+
.build();
205+
206+
secureNative.track(eventOptions);
207+
```
208+
209+
You can also create request context from HttpServletRequest:
210+
211+
```java
212+
@RequestMapping("/track")
213+
public void track(HttpServletRequest request, HttpServletResponse response) {
214+
SecureNativeContext context = SecureNative.contextBuilder()
215+
.fromHttpServletRequest(request)
216+
.build();
217+
218+
EventOptions eventOptions = EventOptionsBuilder.builder(EventTypes.LOG_IN)
219+
.userId("USER_ID")
220+
.userTraits("USER_NAME", "USER_EMAIL")
221+
.context(context)
222+
.properties(Maps.builder()
223+
.put("prop1", "CUSTOM_PARAM_VALUE")
224+
.put("prop2", true)
225+
.put("prop3", 3)
226+
.build())
227+
.timestamp(new Date())
228+
.build();
229+
230+
secureNative.track(eventOptions);
231+
}
232+
```
233+
234+
## Verify events
235+
236+
**Example**
237+
238+
```java
239+
@RequestMapping("/track")
240+
public void track(HttpServletRequest request, HttpServletResponse response) {
241+
SecureNativeContext context = SecureNative.contextBuilder()
242+
.fromHttpServletRequest(request)
243+
.build();
244+
245+
EventOptions eventOptions = EventOptionsBuilder.builder(EventTypes.LOG_IN)
246+
.userId("USER_ID")
247+
.userTraits("USER_NAME", "USER_EMAIL")
248+
.context(context)
249+
.properties(Maps.builder()
250+
.put("prop1", "CUSTOM_PARAM_VALUE")
251+
.put("prop2", true)
252+
.put("prop3", 3)
253+
.build())
254+
.timestamp(new Date())
255+
.build();
256+
257+
VerifyResult verifyResult = secureNative.verify(eventOptions);
258+
verifyResult.getRiskLevel() // Low, Medium, High
259+
verifyResult.score() // Risk score: 0 -1 (0 - Very Low, 1 - Very High)
260+
verifyResult.getTriggers() // ["TOR", "New IP", "New City"]
261+
}
262+
```
263+
264+
## Webhook signature verification
265+
266+
Apply our filter to verify the request is from us, example in spring:
267+
268+
```java
269+
@RequestMapping("/webhook")
270+
public void webhookEndpoint(HttpServletRequest request, HttpServletResponse response) {
271+
SecureNative secureNative = SecureNative.getInstance();
272+
273+
// Checks if request is verified
274+
Boolean isVerified = secureNative.verifyRequestPayload(request);
275+
}
276+
```

requirements.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

securenative/api_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self, event_manager, securenative_options):
1515
def track(self, event_options):
1616
Logger.debug("Track event call")
1717
event = SDKEvent(event_options, self.options)
18-
self.event_manager.send_async(event, ApiRoute.TRACK.value)
18+
return self.event_manager.send_async(event, ApiRoute.TRACK.value)
1919

2020
def verify(self, event_options):
2121
Logger.debug("Verify event call")

securenative/config/configuration_builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ def with_log_level(self, log_level):
5252
return self
5353

5454
def with_fail_over_strategy(self, fail_over_strategy):
55+
if fail_over_strategy != FailOverStrategy.FAIL_OPEN.value and \
56+
fail_over_strategy != FailOverStrategy.FAIL_CLOSED.value:
57+
self.fail_over_strategy = FailOverStrategy.FAIL_OPEN.value
58+
return self
5559
self.fail_over_strategy = fail_over_strategy
5660
return self
5761

securenative/config/configuration_manager.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,10 @@ class ConfigurationManager(object):
1212
@classmethod
1313
def read_resource_file(cls, resource_path):
1414
cls.config.read(resource_path)
15-
sections = cls.config.sections()
1615

1716
properties = {}
18-
for section in sections:
19-
options = cls.config.options(section)
20-
for option in options:
21-
try:
22-
properties[option] = cls.config.get(section, option)
23-
except NoSectionError:
24-
properties[option] = None
17+
for key, value in cls.config.defaults().items():
18+
properties[key.upper()] = value
2519

2620
return properties
2721

@@ -40,24 +34,30 @@ def config_builder():
4034

4135
@classmethod
4236
def _get_env_or_default(cls, properties, key, default):
43-
if properties[key]:
44-
return properties[key]
37+
if os.environ.get(key):
38+
return os.environ.get(key)
39+
if properties.get(key):
40+
return properties.get(key)
4541
return default
4642

4743
@classmethod
4844
def load_config(cls):
49-
builder = ConfigurationBuilder()
50-
options = builder.get_default_securenative_options()
51-
resource_path = cls._get_env_or_default(cls.DEFAULT_CONFIG_FILE, cls.CUSTOM_CONFIG_FILE_ENV_NAME)
45+
options = ConfigurationBuilder().get_default_securenative_options()
46+
47+
resource_path = cls.DEFAULT_CONFIG_FILE
48+
if os.environ.get(cls.CUSTOM_CONFIG_FILE_ENV_NAME):
49+
resource_path = os.environ.get(cls.CUSTOM_CONFIG_FILE_ENV_NAME)
50+
5251
properties = cls.read_resource_file(resource_path)
5352

54-
builder.\
55-
with_api_key(cls._get_env_or_default(properties, "SECURENATIVE_API_KEY", options.api)).\
56-
with_api_url(cls._get_env_or_default(properties, "SECURENATIVE_API_URL", options.api_url)).\
57-
with_interval(cls._get_env_or_default(properties, "SECURENATIVE_INTERVAL", options.interval)).\
58-
with_max_events(cls._get_env_or_default(properties, "SECURENATIVE_MAX_EVENTS", options.max_events)).\
59-
with_timeout(cls._get_env_or_default(properties, "SECURENATIVE_TIMEOUT", options.timeout)).\
60-
with_auto_send(cls._get_env_or_default(properties, "SECURENATIVE_AUTO_SEND", options.auto_send)).\
61-
with_disable(cls._get_env_or_default(properties, "SECURENATIVE_DISABLE", options.disable)).\
62-
with_log_level(cls._get_env_or_default(properties, "SECURENATIVE_LOG_LEVEL", options.log_level)).\
63-
with_fail_over_strategy(cls._get_env_or_default(properties, "SECURENATIVE_FAILOVER_STRATEGY", options))
53+
return ConfigurationBuilder(). \
54+
with_api_key(cls._get_env_or_default(properties, "SECURENATIVE_API_KEY", options.api_key)). \
55+
with_api_url(cls._get_env_or_default(properties, "SECURENATIVE_API_URL", options.api_url)). \
56+
with_interval(cls._get_env_or_default(properties, "SECURENATIVE_INTERVAL", options.interval)). \
57+
with_max_events(cls._get_env_or_default(properties, "SECURENATIVE_MAX_EVENTS", options.max_events)). \
58+
with_timeout(cls._get_env_or_default(properties, "SECURENATIVE_TIMEOUT", options.timeout)). \
59+
with_auto_send(cls._get_env_or_default(properties, "SECURENATIVE_AUTO_SEND", options.auto_send)). \
60+
with_disable(cls._get_env_or_default(properties, "SECURENATIVE_DISABLE", options.disable)). \
61+
with_log_level(cls._get_env_or_default(properties, "SECURENATIVE_LOG_LEVEL", options.log_level)). \
62+
with_fail_over_strategy(cls._get_env_or_default(
63+
properties, "SECURENATIVE_FAILOVER_STRATEGY", options.fail_over_strategy))

securenative/config/securenative_options.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
class SecureNativeOptions(object):
55

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

securenative/event_manager.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(self, url, body, retry):
1919

2020
class EventManager:
2121
def __init__(self, options=SecureNativeOptions(), http_client=None):
22-
if options.api_url is None:
22+
if options.api_key is None:
2323
raise ValueError('API key cannot be None, please get your API key from SecureNative console.')
2424

2525
if not http_client:
@@ -82,6 +82,7 @@ def send_sync(self, event, resource_path, retry):
8282
self.queue.insert(0, item)
8383
if self._is_queue_full():
8484
self.queue = self.queue[:len(self.queue - 1)]
85+
return res
8586

8687
def _is_queue_full(self):
8788
return len(self.queue) > self.options.max_events
@@ -98,6 +99,7 @@ def _send_events(self):
9899

99100
Logger.debug("Event successfully sent; {}".format(item.body))
100101
self.queue.remove(item)
102+
return res
101103
except Exception as e:
102104
Logger.error("Failed to send event; {}".format(e))
103105
if item.retry:
@@ -117,23 +119,23 @@ def start_event_persist(self):
117119
if self.options.auto_send or self.send_enabled:
118120
self.send_enabled = True
119121
try:
120-
121122
self.scheduler = sched.scheduler(time.time, time.sleep)
122123
self.scheduler.enter(self.options.interval, 1, self._send_events)
123124
self.thread = threading.Thread(target=self.scheduler.run).start()
124-
except Exception:
125-
pass
125+
except Exception as e:
126+
Logger.error("Could not start event scheduler; {}".format(e))
126127
else:
127128
Logger.debug("Automatic event persistence is disabled, you should persist events manually")
128129

129130
def stop_event_persist(self):
130131
if self.send_enabled:
131132
Logger.debug("Attempting to stop automatic event persistence")
132133
try:
133-
self.thread.stop()
134+
if self.thread:
135+
self.thread.stop()
134136
self.scheduler.cancel(self._send_events)
135-
except ValueError:
136-
pass
137+
except ValueError as e:
138+
Logger.error("Could not stop event scheduler; {}".format(e))
137139

138140
Logger.debug("Stopped event persistence")
139141

securenative/utils/date_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ class DateUtils(object):
66
@staticmethod
77
def to_timestamp(date):
88
if not date:
9-
return datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
10-
return datetime.strptime(date, "%Y-%m-%dT%H:%M:%S%z")
9+
return datetime.now().strftime("%Y-%d-%dT%H:%M:%S.%fZ")
10+
return datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%f%z")

securenative/utils/ip_utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ def is_valid_public_ip(ip_address):
2424

2525
ip = ipaddress.IPv4Address(ip_address)
2626
if ip.is_loopback \
27-
or ip.is_global \
27+
or not ip.is_global \
2828
or ip.is_private \
2929
or ip.is_link_local \
3030
or ip.is_multicast \
31-
or ip.is_multicast \
3231
or ip.is_reserved \
3332
or ip.is_unspecified:
3433
return False

0 commit comments

Comments
 (0)