Skip to content

Commit

Permalink
update action triggers
Browse files Browse the repository at this point in the history
  • Loading branch information
patricksanders committed Sep 13, 2024
1 parent 52b20e4 commit 026b16f
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 67 deletions.
8 changes: 8 additions & 0 deletions aardvark/__version__.py.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# this is a placeholder and will be overwritten at build time
try:
from setuptools_scm import get_version
__version__ = get_version(root="..", relative_to=__file__)
except (ImportError, OSError):
# ImportError: setuptools_scm isn't installed
# OSError: git isn't installed
__version__ = "0.0.0.dev0+placeholder"
4 changes: 2 additions & 2 deletions aardvark/persistence/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from aardvark.plugins import AardvarkPlugin

if TYPE_CHECKING:
from dynaconf import Dynaconf
from dynaconf.utils import DynaconfDict


class PersistencePlugin(AardvarkPlugin):
def __init__(self, alternative_config: Dynaconf = None):
def __init__(self, alternative_config: DynaconfDict | None = None):
super().__init__(alternative_config=alternative_config)

def init_db(self):
Expand Down
95 changes: 40 additions & 55 deletions aardvark/persistence/sqlalchemy/sa_persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from aardvark.persistence.sqlalchemy.models import AdvisorData, AWSIAMObject, Base

if TYPE_CHECKING:
from dynaconf import Dynaconf
from dynaconf.utils import DynaconfDict

log = logging.getLogger("aardvark")
session_type = scoped_session | Session
Expand All @@ -27,7 +27,7 @@ class SQLAlchemyPersistence(PersistencePlugin):
session_factory: sessionmaker
session: session_type

def __init__(self, *, alternative_config: Dynaconf = None, initialize: bool = True):
def __init__(self, *, alternative_config: DynaconfDict | None = None, initialize: bool = True):
super().__init__(alternative_config=alternative_config)
if initialize:
self.init_db()
Expand Down Expand Up @@ -216,7 +216,7 @@ def get_role_data(
def create_or_update_advisor_data(
self,
item_id: int,
last_authenticated: int,
last_authenticated: datetime.datetime,
service_name: str,
service_namespace: str,
last_authenticated_entity: str,
Expand All @@ -226,12 +226,14 @@ def create_or_update_advisor_data(
with self.session_scope(session) as session:
service_name = service_name[:128]
service_namespace = service_namespace[:64]
item = None
item: AdvisorData | None = None
try:
item = (
session.query(AdvisorData)
.filter(AdvisorData.item_id == item_id)
.filter(AdvisorData.serviceNamespace == service_namespace)
.filter(
AdvisorData.item_id == item_id,
AdvisorData.serviceNamespace == service_namespace,
)
.scalar()
)
except SQLAlchemyError:
Expand All @@ -243,70 +245,53 @@ def create_or_update_advisor_data(
raise

if not item:
item = AdvisorData(
item_id=item_id,
lastAuthenticated=last_authenticated,
serviceName=service_name,
serviceNamespace=service_namespace,
lastAuthenticatedEntity=last_authenticated_entity,
totalAuthenticatedEntities=total_authenticated_entities,
)
try:
session.add(item)
except SQLAlchemyError:
log.exception("failed to add AdvisorData item to session")
raise
return
item = AdvisorData()

# Save existing lastAuthenticated timestamp for later comparison
# sqlite will return a string for item.lastAuthenticated, so we parse that into a datetime
if isinstance(item.lastAuthenticated, str):
ts = datetime.datetime.strptime(item.lastAuthenticated, "%Y-%m-%d %H:%M:%S.%f")
existing_last_authenticated = datetime.datetime.strptime(item.lastAuthenticated, "%Y-%m-%d %H:%M:%S.%f")
else:
ts = item.lastAuthenticated

if last_authenticated > ts:
item.lastAuthenticated = last_authenticated
try:
session.add(item)
except SQLAlchemyError:
log.exception("failed to add AdvisorData item to session")
raise

elif last_authenticated < ts:
"""
lastAuthenticated is obtained by calling get_service_last_accessed_details() method of the boto3 iam client.
When there is no AA data about a service, the lastAuthenticated key is missing from the returned dictionary.
This is perfectly valid, either because the service in question was not accessed in the past 365 days or
the entity granting access to it was created recently enough that no AA data is available yet (it can take up to
4 hours for this to happen).
When this happens, the AccountToUpdate._get_job_results() method will set lastAuthenticated to 0.
Usually we don't want to persist such an entity, with one exception: there's already a recorded, non-zero lastAuthenticated
timestamp persisted for this item. That means the service was accessed at some point in time, but now more than 365 passed since
the last access, so AA no longer returns a timestamp for it.
"""
existing_last_authenticated = item.lastAuthenticated

# Set all fields to the provided values. SQLAlchemy will only mark the model instance as modified if the actual
# values have changed, so this will be a no-op if the values are all the same.
item.item_id = item_id
item.lastAuthenticated = last_authenticated
item.lastAuthenticatedEntity = last_authenticated_entity
item.serviceName = service_name
item.serviceNamespace = service_namespace
item.totalAuthenticatedEntities = total_authenticated_entities

# When there is no AA data about a service, the lastAuthenticated key is missing from the returned data.
# This is perfectly valid, either because the service in question was not accessed in the past 365 days or
# the entity granting access to it was created recently enough that no AA data is available yet (it can
# take up to 4 hours for this to happen).
#
# When this happens, the AccountToUpdate._get_job_results() method will set lastAuthenticated to 0. Usually
# we don't want to persist such an entity, with one exception: there's already a recorded, non-zero
# lastAuthenticated timestamp persisted for this item. That means the service was accessed at some point in
# time, but now more than 365 passed since the last access, so AA no longer returns a timestamp for it.
if existing_last_authenticated is not None and existing_last_authenticated > last_authenticated:
if last_authenticated == 0:
log.warning(
"Previously seen object not accessed in the past 365 days "
"(got null lastAuthenticated from AA). Setting to 0. "
"Object %d service %s previous timestamp %d",
log.info(
"Previously seen object not accessed in the past 365 days (got null lastAuthenticated from "
"AA). Setting to 0. Object %s service %s previous timestamp %d",
item.item_id,
item.serviceName,
item.lastAuthenticated,
)
item.lastAuthenticated = 0
try:
session.add(item)
except SQLAlchemyError:
log.exception("failed to add AdvisorData item to session")
raise
else:
log.warning(
"Received an older time than previously seen for object %s service %s (%d < %d)!",
"Received an older time than previously seen for object %s service %s (%d < %d). Not updating!",
item.item_id,
item.serviceName,
item.lastAuthenticated,
last_authenticated,
existing_last_authenticated,
)
item.lastAuthenticated = existing_last_authenticated

session.add(item)

def get_or_create_iam_object(self, arn: str, session: session_type = None):
with self.session_scope(session) as session:
Expand Down
9 changes: 7 additions & 2 deletions aardvark/plugins/plugin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from dynaconf import Dynaconf
from __future__ import annotations

from typing import TYPE_CHECKING

from aardvark.config import settings

if TYPE_CHECKING:
from dynaconf.utils import DynaconfDict


class AardvarkPlugin:
def __init__(self, alternative_config: Dynaconf = None):
def __init__(self, alternative_config: DynaconfDict | None = None):
if alternative_config:
self.config = alternative_config
else:
Expand Down
13 changes: 9 additions & 4 deletions aardvark/retrievers/access_advisor/retriever.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
from __future__ import annotations

import asyncio
import datetime
import logging
from typing import Any
from typing import TYPE_CHECKING, Any

from asgiref.sync import sync_to_async
from cloudaux.aws.sts import boto3_cached_conn
from dynaconf import Dynaconf

from aardvark.exceptions import AccessAdvisorError
from aardvark.retrievers import RetrieverPlugin

if TYPE_CHECKING:
import datetime

from dynaconf.utils import DynaconfDict

log = logging.getLogger("aardvark")


class AccessAdvisorRetriever(RetrieverPlugin):
def __init__(self, alternative_config: Dynaconf = None):
def __init__(self, alternative_config: DynaconfDict | None = None):
super().__init__("access_advisor", alternative_config=alternative_config)

async def _generate_service_last_accessed_details(self, iam_client, arn):
Expand Down
11 changes: 7 additions & 4 deletions aardvark/retrievers/plugin.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import logging
from typing import Any
from __future__ import annotations

from dynaconf import Dynaconf
import logging
from typing import TYPE_CHECKING, Any

from aardvark.plugins import AardvarkPlugin

if TYPE_CHECKING:
from dynaconf.utils import DynaconfDict

log = logging.getLogger("aardvark")


class RetrieverPlugin(AardvarkPlugin):
_name: str

def __init__(self, name: str, alternative_config: Dynaconf = None):
def __init__(self, name: str, alternative_config: DynaconfDict = None):
super().__init__(alternative_config=alternative_config)
self._name = name

Expand Down

0 comments on commit 026b16f

Please sign in to comment.