-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from melissahardware/oomnitza-connector
Oomnitza activities connector
- Loading branch information
Showing
11 changed files
with
2,950 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright (c) HashiCorp, Inc. | ||
# SPDX-License-Identifier: MPL-2.0 | ||
|
||
"""Oomnitza connectors for Grove.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Copyright (c) HashiCorp, Inc. | ||
# SPDX-License-Identifier: MPL-2.0 | ||
|
||
"""Oomnitza Activities connector for Grove.""" | ||
import time | ||
from datetime import datetime, timedelta | ||
|
||
from grove.connectors import BaseConnector | ||
from grove.connectors.oomnitza.api import Client | ||
from grove.constants import REVERSE_CHRONOLOGICAL | ||
from grove.exceptions import NotFoundException | ||
|
||
|
||
class Connector(BaseConnector): | ||
NAME = "oomnitza_activities" | ||
POINTER_PATH = "timestamp" | ||
LOG_ORDER = REVERSE_CHRONOLOGICAL | ||
|
||
def collect(self): | ||
"""Collects Oomnitza activities from the Oomnitza API. | ||
This will first check whether there are any pointers cached to indicate previous | ||
collections. If not, the last week of data will be collected. | ||
""" | ||
client = Client(token=self.key, identity=self.identity) | ||
|
||
# Set cursor | ||
cursor = 0 | ||
|
||
# If no pointer is stored then a previous run hasn't been performed, so set the | ||
# pointer to 2 days ago. In the case of the Oomnitza activities API the pointer is | ||
# the value of the "timestamp" field from the latest record retrieved from | ||
# the API - which is in epoch. The Oomnitza API doesnt account for milliseconds. | ||
now = datetime.fromtimestamp(time.time()).strftime("%s") | ||
|
||
try: | ||
_ = self.pointer | ||
except NotFoundException: | ||
self.pointer = ( | ||
datetime.fromtimestamp(time.time()) - timedelta(days=2) | ||
).strftime("%s") | ||
|
||
# Get log data from the upstream API. A "start_date" and "end_date" datetime query | ||
# parameters are required. | ||
while True: | ||
log = client.get_activites( | ||
start_date=self.pointer, end_date=now, cursor=cursor | ||
) | ||
|
||
# Save this batch of log entries. | ||
self.save(log.entries) | ||
|
||
# Check if we need to continue paging. | ||
cursor = log.cursor # type: ignore | ||
if not cursor: | ||
break |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Copyright (c) HashiCorp, Inc. | ||
# SPDX-License-Identifier: MPL-2.0 | ||
|
||
"""Oomnitza API client.""" | ||
|
||
import logging | ||
from typing import Dict, Optional | ||
|
||
import requests | ||
from grove.exceptions import RequestFailedException | ||
from grove.types import AuditLogEntries, HTTPResponse | ||
|
||
API_BASE_URI = "https://{identity}.oomnitza.com" | ||
API_PAGE_SIZE = 200 | ||
|
||
|
||
class Client: | ||
def __init__( | ||
self, | ||
identity: Optional[str] = None, | ||
token: Optional[str] = None, | ||
): | ||
"""Setup a new client. | ||
:param identity: The name of the Oomnitza organisation. | ||
:param token: The Oomnitza API token. | ||
""" | ||
self.logger = logging.getLogger(__name__) | ||
self.headers = { | ||
"content-type": "application/json", | ||
} | ||
if token: | ||
self.headers["Authorization2"] = token | ||
|
||
# We need to push the identity into the URI, so we'll keep track of this. | ||
self._api_base_uri = API_BASE_URI.format(identity=identity) | ||
|
||
def _get( | ||
self, | ||
url: str, | ||
params: Optional[Dict[str, Optional[str]]] = None, | ||
) -> HTTPResponse: | ||
"""A GET wrapper to handle retries for the caller. | ||
:param url: URL to perform the HTTP GET against. | ||
:param params: HTTP parameters to add to the request. | ||
:raises RequestFailedException: An HTTP request failed. | ||
:return: HTTP Response object containing the headers and body of a response. | ||
""" | ||
try: | ||
response = requests.get(url, headers=self.headers, params=params) | ||
response.raise_for_status() | ||
except requests.exceptions.RequestException as err: | ||
raise RequestFailedException(err) | ||
|
||
return HTTPResponse(headers=response.headers, body=response.json()) | ||
|
||
def get_activites( | ||
self, | ||
cursor: int = 0, | ||
start_date: Optional[str] = None, | ||
end_date: Optional[str] = None, | ||
) -> AuditLogEntries: | ||
"""Fetches a list of signing attempt logs. | ||
:param cursor: Cursor to use when fetching results. | ||
:param start_date: The earliest date an event represented as an epoch value. | ||
:param end_date: The latest date an event represented as an epoch value. | ||
:return: AuditLogEntries object containing a pagination cursor, and log entries. | ||
""" | ||
url = f"{self._api_base_uri}/api/v3/activities" | ||
|
||
result = self._get( | ||
url, | ||
params={ | ||
"start_date": start_date, | ||
"end_date": end_date, | ||
"limit": str(API_PAGE_SIZE), | ||
"skip": str(cursor), | ||
}, | ||
) | ||
# Keep paging until we run out of results. | ||
data = result.body | ||
|
||
if len(data) == API_PAGE_SIZE: | ||
cursor += API_PAGE_SIZE | ||
else: | ||
cursor = 0 | ||
|
||
# Return the cursor and the results to allow the caller to page as required. | ||
return AuditLogEntries(cursor=cursor, entries=data) # type: ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"key": "TOKEN_HERE", | ||
"identity": "ORGANIZATION_ID_HERE", | ||
"name": "oomnitza-activities", | ||
"connector": "oomnitza-activities" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
[ | ||
{ | ||
"id": 605452, | ||
"user_id": "0038389812878318237129898", | ||
"username": "[email protected]", | ||
"timestamp": 1680895957, | ||
"event_source_id": "TESTER", | ||
"event_type_id": "EDIT", | ||
"module_id": "TEST", | ||
"oomnitza_id": "[email protected]", | ||
"sub_module_id": null, | ||
"extra_json": null, | ||
"full_name": "test" | ||
}, | ||
{ | ||
"id": 605449, | ||
"user_id": "0038389812878318237129898", | ||
"username": "[email protected]", | ||
"timestamp": 1680895948, | ||
"event_source_id": "TESTER", | ||
"event_type_id": "EDIT", | ||
"module_id": "TEST", | ||
"oomnitza_id": "[email protected]", | ||
"sub_module_id": null, | ||
"extra_json": null, | ||
"full_name": "test" | ||
}, | ||
{ | ||
"id": 605448, | ||
"user_id": "0038389812878318237129898", | ||
"username": "[email protected]", | ||
"timestamp": 1680895947, | ||
"event_source_id": "TESTER", | ||
"event_type_id": "EDIT", | ||
"module_id": "TEST", | ||
"oomnitza_id": "[email protected]", | ||
"sub_module_id": null, | ||
"extra_json": null, | ||
"full_name": "test" | ||
}, | ||
{ | ||
"id": 605447, | ||
"user_id": "0038389812878318237129898", | ||
"username": "[email protected]", | ||
"timestamp": 1680895947, | ||
"event_source_id": "TESTER", | ||
"event_type_id": "EDIT", | ||
"module_id": "TEST", | ||
"oomnitza_id": "[email protected]", | ||
"sub_module_id": null, | ||
"extra_json": null, | ||
"full_name": "test" | ||
}, | ||
{ | ||
"id": 605446, | ||
"user_id": "0038389812878318237129898", | ||
"username": "[email protected]", | ||
"timestamp": 1680895937, | ||
"event_source_id": "TESTER", | ||
"event_type_id": "EDIT", | ||
"module_id": "TEST", | ||
"oomnitza_id": "[email protected]", | ||
"sub_module_id": null, | ||
"extra_json": null, | ||
"full_name": "test" | ||
} | ||
] |
Oops, something went wrong.