Skip to content

Commit 942f32e

Browse files
author
Juliya Smith
authored
Jules/upgrades (#7)
1 parent 8cb29fe commit 942f32e

File tree

13 files changed

+351
-164
lines changed

13 files changed

+351
-164
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
The intended audience of this file is for py42 consumers -- as such, changes that don't affect
99
how a consumer would use the library (e.g. adding unit tests, updating documentation, etc) are not captured here.
1010

11+
## Unreleased
12+
13+
### Added
14+
15+
- Begin and end date now support specifying time: `code42 securitydata print -b 2020-02-02 12:00:00`.
16+
1117
## 0.2.0 - 2020-02-25
1218

1319
### Removed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
packages=find_packages("src"),
2121
package_dir={"": "src"},
2222
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4",
23-
install_requires=["c42eventextractor==0.1.3", "keyring==18.0.1","py42==0.4.4"],
23+
install_requires=["c42eventextractor==0.2.0", "keyring==18.0.1","py42==0.5.1"],
2424
license="MIT",
2525
include_package_data=True,
2626
zip_safe=False,

src/code42cli/date_helper.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from datetime import datetime, timedelta
2+
from c42eventextractor.common import convert_datetime_to_timestamp
3+
from py42.sdk.file_event_query.event_query import EventTimestamp
4+
5+
_DEFAULT_LOOK_BACK_DAYS = 60
6+
_MAX_LOOK_BACK_DAYS = 90
7+
_FORMAT_VALUE_ERROR_MESSAGE = u"input must be a date in YYYY-MM-DD or YYYY-MM-DD HH:MM:SS format."
8+
9+
10+
def create_event_timestamp_range(begin_date=None, end_date=None):
11+
min_timestamp = _parse_min_timestamp(begin_date)
12+
max_timestamp = _parse_max_timestamp(end_date)
13+
_verify_timestamp_order(min_timestamp, max_timestamp)
14+
return EventTimestamp.in_range(min_timestamp, max_timestamp)
15+
16+
17+
def _parse_min_timestamp(begin_date_str):
18+
if not begin_date_str:
19+
return _get_default_min_timestamp()
20+
min_timestamp = _parse_timestamp(begin_date_str)
21+
boundary_date = datetime.utcnow() - timedelta(days=_MAX_LOOK_BACK_DAYS)
22+
boundary = convert_datetime_to_timestamp(boundary_date)
23+
if min_timestamp and min_timestamp < boundary:
24+
raise ValueError(u"'Begin date' must be within 90 days.")
25+
return min_timestamp
26+
27+
28+
def _parse_max_timestamp(end_date_str):
29+
if not end_date_str:
30+
return _get_default_max_timestamp()
31+
return _parse_timestamp(end_date_str)
32+
33+
34+
def _verify_timestamp_order(min_timestamp, max_timestamp):
35+
if min_timestamp is None or max_timestamp is None:
36+
return
37+
if min_timestamp >= max_timestamp:
38+
raise ValueError(u"Begin date cannot be after end date")
39+
40+
41+
def _parse_timestamp(date_tuple):
42+
try:
43+
date_str = _join_date_tuple(date_tuple)
44+
date_format = u"%Y-%m-%d" if len(date_tuple) == 1 else u"%Y-%m-%d %H:%M:%S"
45+
time = datetime.strptime(date_str, date_format)
46+
except ValueError:
47+
raise ValueError(_FORMAT_VALUE_ERROR_MESSAGE)
48+
return convert_datetime_to_timestamp(time)
49+
50+
51+
def _join_date_tuple(date_tuple):
52+
if not date_tuple:
53+
return None
54+
date_str = date_tuple[0]
55+
if len(date_tuple) == 1:
56+
return date_str
57+
if len(date_tuple) == 2:
58+
date_str = "{0} {1}".format(date_str, date_tuple[1])
59+
else:
60+
raise ValueError(_FORMAT_VALUE_ERROR_MESSAGE)
61+
return date_str
62+
63+
64+
def _get_default_min_timestamp():
65+
now = datetime.utcnow()
66+
start_day = timedelta(days=_DEFAULT_LOOK_BACK_DAYS)
67+
days_ago = now - start_day
68+
return convert_datetime_to_timestamp(days_ago)
69+
70+
71+
def _get_default_max_timestamp():
72+
return convert_datetime_to_timestamp(datetime.utcnow())

src/code42cli/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from argparse import ArgumentParser
22

3+
from code42cli.compat import str
34
from code42cli.profile import profile
45
import code42cli.securitydata.main as securitydata
56

src/code42cli/profile/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_config_profile():
2121
util.print_error("Profile is not set.")
2222
print("")
2323
print("To set, use: ")
24-
util.print_bold("\tcode42cli profile set -s <authority-URL> -u <username>")
24+
util.print_bold("\tcode42 profile set -s <authority-URL> -u <username>")
2525
print("")
2626
exit(1)
2727

src/code42cli/securitydata/arguments/search.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,23 @@ def _add_begin_date_arg(parser):
3333
parser.add_argument(
3434
"-b",
3535
"--begin",
36+
nargs="+",
3637
action="store",
3738
dest=SearchArguments.BEGIN_DATE,
3839
help="The beginning of the date range in which to look for events, "
39-
"in YYYY-MM-DD UTC format OR a number (number of minutes ago).",
40+
"in YYYY-MM-DD (UTC) or YYYY-MM-DD HH:MM:SS (UTC+24-hr time) format.",
4041
)
4142

4243

4344
def _add_end_date_arg(parser):
4445
parser.add_argument(
4546
"-e",
4647
"--end",
48+
nargs="+",
4749
action="store",
4850
dest=SearchArguments.END_DATE,
4951
help="The end of the date range in which to look for events, "
50-
"in YYYY-MM-DD UTC format OR a number (number of minutes ago).",
52+
"in YYYY-MM-DD (UTC) or YYYY-MM-DD HH:MM:SS (UTC+24-hr time) format.",
5153
)
5254

5355

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from __future__ import print_function
22
import json
3-
from datetime import datetime, timedelta
43
from py42.sdk import SDK
54
from py42 import debug_level
65
from py42 import settings
7-
from c42eventextractor.common import FileEventHandlers
8-
from c42eventextractor.extractors import AEDEventExtractor
9-
from c42eventextractor.common import convert_datetime_to_timestamp
6+
from c42eventextractor import FileEventHandlers
7+
from c42eventextractor.extractors import FileEventExtractor
108

9+
from code42cli.compat import str
1110
from code42cli.securitydata.options import ExposureType
1211
from code42cli.util import print_error
12+
from code42cli import date_helper as date_helper
1313
from code42cli.securitydata.cursor_store import AEDCursorStore
1414
from code42cli.securitydata.logger_factory import get_error_logger
1515
from code42cli.profile.profile import get_profile
@@ -21,7 +21,7 @@ def extract(output_logger, args):
2121
handlers = _create_event_handlers(output_logger, args.is_incremental)
2222
profile = get_profile()
2323
sdk = _get_sdk(profile, args.is_debug_mode)
24-
extractor = AEDEventExtractor(sdk, handlers)
24+
extractor = FileEventExtractor(sdk, handlers)
2525
_call_extract(extractor, args)
2626

2727

@@ -53,44 +53,13 @@ def _get_sdk(profile, is_debug_mode):
5353
return code42
5454

5555

56-
def _parse_min_timestamp(begin_date):
57-
min_timestamp = _parse_timestamp(begin_date)
58-
boundary_date = datetime.utcnow() - timedelta(days=90)
59-
boundary = convert_datetime_to_timestamp(boundary_date)
60-
if min_timestamp and min_timestamp < boundary:
61-
print("Argument '--begin' must be within 90 days.")
62-
exit(1)
63-
return min_timestamp
64-
65-
66-
def _parse_timestamp(input_string):
67-
try:
68-
# Input represents date str like '2020-02-13'
69-
time = datetime.strptime(input_string, "%Y-%m-%d")
70-
except ValueError:
71-
# Input represents amount of seconds ago like '86400'
72-
if input_string and input_string.isdigit():
73-
now = datetime.utcnow()
74-
time = now - timedelta(minutes=int(input_string))
75-
else:
76-
raise ValueError("input must be a positive integer or a date in YYYY-MM-DD format.")
77-
78-
return convert_datetime_to_timestamp(time)
79-
80-
8156
def _call_extract(extractor, args):
8257
if not _determine_if_advanced_query(args):
83-
min_timestamp = _parse_min_timestamp(args.begin_date) if args.begin_date else None
84-
max_timestamp = _parse_timestamp(args.end_date) if args.end_date else None
58+
event_timestamp_filter_group = _get_event_timestamp_filter(args)
8559
_verify_exposure_types(args.exposure_types)
86-
_verify_timestamp_order(min_timestamp, max_timestamp)
87-
extractor.extract(
88-
initial_min_timestamp=min_timestamp,
89-
max_timestamp=max_timestamp,
90-
exposure_types=args.exposure_types,
91-
)
60+
extractor.extract(args.exposure_types, event_timestamp_filter_group)
9261
else:
93-
extractor.extract_raw(args.advanced_query)
62+
extractor.extract_advanced(args.advanced_query)
9463

9564

9665
def _determine_if_advanced_query(args):
@@ -99,7 +68,7 @@ def _determine_if_advanced_query(args):
9968
for key in given_args:
10069
val = given_args[key]
10170
if not _verify_compatibility_with_advanced_query(key, val):
102-
print_error("You cannot use --advanced-query with additional search args.")
71+
print_error(u"You cannot use --advanced-query with additional search args.")
10372
exit(1)
10473
return True
10574
return False
@@ -113,20 +82,19 @@ def _verify_compatibility_with_advanced_query(key, val):
11382
return True
11483

11584

85+
def _get_event_timestamp_filter(args):
86+
try:
87+
return date_helper.create_event_timestamp_range(args.begin_date, args.end_date)
88+
except ValueError as ex:
89+
print_error(str(ex))
90+
exit(1)
91+
92+
11693
def _verify_exposure_types(exposure_types):
11794
if exposure_types is None:
11895
return
11996
options = list(ExposureType())
12097
for exposure_type in exposure_types:
12198
if exposure_type not in options:
122-
print_error("'{0}' is not a valid exposure type.".format(exposure_type))
99+
print_error(u"'{0}' is not a valid exposure type.".format(exposure_type))
123100
exit(1)
124-
125-
126-
def _verify_timestamp_order(begin_timestamp, end_timestamp):
127-
if begin_timestamp is None or end_timestamp is None:
128-
return
129-
130-
if begin_timestamp >= end_timestamp:
131-
print_error("Begin date cannot be after end date")
132-
exit(1)

src/code42cli/securitydata/logger_factory.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
import logging
33
from logging.handlers import RotatingFileHandler
44
from c42eventextractor.logging.formatters import (
5-
AEDDictToJSONFormatter,
6-
AEDDictToCEFFormatter,
7-
AEDDictToRawJSONFormatter,
5+
FileEventDictToJSONFormatter,
6+
FileEventDictToCEFFormatter,
7+
FileEventDictToRawJSONFormatter,
88
)
9-
from c42eventextractor.logging.handlers import NoPrioritySysLogHandler
9+
from c42eventextractor.logging.handlers import NoPrioritySysLogHandlerWrapper
1010

11+
from code42cli.compat import str
1112
from code42cli.securitydata.options import OutputFormat
12-
from code42cli.util import get_user_project_path
13+
from code42cli.util import get_user_project_path, print_error
1314

1415

1516
def get_logger_for_stdout(output_format):
@@ -35,7 +36,7 @@ def get_logger_for_server(hostname, protocol, output_format):
3536
if _logger_has_handlers(logger):
3637
return logger
3738

38-
handler = NoPrioritySysLogHandler(hostname, protocol=protocol)
39+
handler = NoPrioritySysLogHandlerWrapper(hostname, protocol=protocol).handler
3940
return _init_logger(logger, handler, output_format)
4041

4142

@@ -62,15 +63,19 @@ def _init_logger(logger, handler, output_format):
6263

6364

6465
def _apply_logger_dependencies(logger, handler, formatter):
65-
handler.setFormatter(formatter)
66-
logger.addHandler(handler)
66+
try:
67+
handler.setFormatter(formatter)
68+
logger.addHandler(handler)
69+
except Exception as ex:
70+
print_error(str(ex))
71+
exit(1)
6772
return logger
6873

6974

7075
def _get_formatter(output_format):
7176
if output_format == OutputFormat.JSON:
72-
return AEDDictToJSONFormatter()
77+
return FileEventDictToJSONFormatter()
7378
elif output_format == OutputFormat.CEF:
74-
return AEDDictToCEFFormatter()
79+
return FileEventDictToCEFFormatter()
7580
else:
76-
return AEDDictToRawJSONFormatter()
81+
return FileEventDictToRawJSONFormatter()

src/code42cli/securitydata/subcommands/clear_checkpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def init(subcommand_parser):
1212

1313
def clear_checkpoint(*args):
1414
"""Removes the stored checkpoint that keeps track of the last event you got.
15-
To use, do `code42cli clear-checkpoint`.
15+
To use, run `code42 clear-checkpoint`.
1616
This affects `incremental` mode by causing it to behave like it has never been run before.
1717
"""
1818
AEDCursorStore().reset()

tests/conftest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import pytest
2+
import json as json_module
3+
from datetime import datetime, timedelta
24
from argparse import Namespace
35

46
from code42cli.profile.config import ConfigurationKeys
@@ -47,3 +49,34 @@ class ConfigParserMocks(object):
4749
section_adder = None
4850
reader = None
4951
sections = None
52+
53+
54+
def get_first_filter_value_from_json(json):
55+
return json_module.loads(str(json))["filters"][0]["value"]
56+
57+
58+
def get_second_filter_value_from_json(json):
59+
return json_module.loads(str(json))["filters"][1]["value"]
60+
61+
62+
def parse_date_from_first_filter_value(json):
63+
date_str = get_first_filter_value_from_json(json)
64+
return convert_str_to_date(date_str)
65+
66+
67+
def parse_date_from_second_filter_value(json):
68+
date_str = get_second_filter_value_from_json(json)
69+
return convert_str_to_date(date_str)
70+
71+
72+
def convert_str_to_date(date_str):
73+
return datetime.strptime(date_str, u"%Y-%m-%dT%H:%M:%S.%fZ")
74+
75+
76+
def get_test_date(days_ago):
77+
now = datetime.utcnow()
78+
return now - timedelta(days=days_ago)
79+
80+
81+
def get_test_date_str(days_ago):
82+
return get_test_date(days_ago).strftime("%Y-%m-%d")

0 commit comments

Comments
 (0)