Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ciris_adapters/ciris_accord_metrics/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,17 @@ def get_status(self) -> RuntimeAdapterStatus:
# Consent Management API
# =========================================================================

def update_consent(self, consent_given: bool) -> None:
def update_consent(self, consent_given: bool, request_lens_deletion: bool = False) -> None:
"""Update consent state.

This is called by the setup wizard when consent is granted/revoked.
This is called by the setup wizard or DSAR self-service when consent is granted/revoked.

When consent is revoked and request_lens_deletion is True, a deletion request
is queued to be sent to CIRISLens on the next flush cycle.

Args:
consent_given: Whether user has consented
request_lens_deletion: If True and revoking consent, queue a lens deletion request
"""
self._consent_given = consent_given
self._consent_timestamp = datetime.now(timezone.utc).isoformat()
Expand All @@ -304,6 +308,8 @@ def update_consent(self, consent_given: bool) -> None:
logger.info(f"Consent GRANTED for accord metrics collection " f"at {self._consent_timestamp}")
else:
logger.info(f"Consent REVOKED for accord metrics collection " f"at {self._consent_timestamp}")
if request_lens_deletion:
self.metrics_service.queue_lens_deletion_on_revoke()

def is_consent_given(self) -> bool:
"""Check if consent has been given.
Expand Down
69 changes: 59 additions & 10 deletions ciris_adapters/ciris_accord_metrics/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,16 +597,8 @@ async def start(self) -> None:
logger.warning("=" * 70)
return

# Initialize HTTP session
self._session = aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=30),
headers={
"Content-Type": "application/json",
"User-Agent": "CIRIS-AccordMetrics/1.0",
},
)

# Start flush task
# Initialize HTTP session and start flush task
self._initialize_http_session()
self._flush_task = asyncio.create_task(self._periodic_flush())

logger.info("=" * 70)
Expand Down Expand Up @@ -1646,6 +1638,11 @@ async def record_pdma_decision(
def set_consent(self, consent_given: bool, timestamp: Optional[str] = None) -> None:
"""Update consent state.

When consent is granted and the HTTP session/flush task are not yet
initialized (adapter was started without consent), this method will
start them so collection begins immediately without requiring a
full adapter reload.

Args:
consent_given: Whether consent is given
timestamp: ISO timestamp when consent was given/revoked
Expand All @@ -1655,9 +1652,35 @@ def set_consent(self, consent_given: bool, timestamp: Optional[str] = None) -> N

if consent_given:
logger.info(f"Consent granted for accord metrics at {self._consent_timestamp}")
# If the service was started without consent, the HTTP session and
# flush task were never created. Initialize them now so collection
# begins immediately.
if self._session is None or (hasattr(self._session, "closed") and self._session.closed):
self._initialize_http_session()
if self._flush_task is None or self._flush_task.done():
self._flush_task = asyncio.create_task(self._periodic_flush())
logger.info("Started periodic flush task after late consent grant")
else:
logger.info(f"Consent revoked for accord metrics at {self._consent_timestamp}")

def _initialize_http_session(self) -> None:
"""Create the aiohttp session used to send events to CIRISLens.

Safe to call multiple times — will only create a session if one
does not already exist (or the existing one is closed).
"""
if self._session is not None and not getattr(self._session, "closed", True):
return

self._session = aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=30),
headers={
"Content-Type": "application/json",
"User-Agent": "CIRIS-AccordMetrics/1.0",
},
)
logger.info(f"HTTP session initialized for {self._endpoint_url}")

def set_agent_id(self, agent_id: str) -> None:
"""Set and anonymize the agent ID.

Expand Down Expand Up @@ -1694,4 +1717,30 @@ def get_metrics(self) -> Dict[str, Any]:
"traces_signed": self._traces_signed,
"signer_key_id": self._signer.key_id,
"has_signing_key": self._signer.has_signing_key,
"agent_id_hash": self._agent_id_hash,
}

def queue_lens_deletion_on_revoke(self) -> None:
"""Queue a deletion event to be sent to CIRISLens.

Called when consent is revoked to request removal of all traces
for this agent from the lens repository. Sends a disconnect event
with deletion_requested=True so the lens API knows to purge data.
"""
if not self._agent_id_hash:
logger.warning("Cannot queue lens deletion: no agent_id_hash set")
return

deletion_event: Dict[str, Any] = {
"event_type": "consent_revoked_deletion_requested",
"timestamp": datetime.now(timezone.utc).isoformat(),
"agent_id_hash": self._agent_id_hash,
"deletion_requested": True,
"reason": "User revoked accord metrics consent via DSAR self-service",
}

self._event_queue.append(deletion_event)
logger.info(
f"Queued lens deletion request for agent {self._agent_id_hash} "
f"(queue size: {len(self._event_queue)})"
)
2 changes: 2 additions & 0 deletions ciris_engine/logic/adapters/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
dsar_multi_source,
emergency,
memory,
my_data,
partnership,
scheduler,
setup,
Expand Down Expand Up @@ -271,6 +272,7 @@ async def _strip_trailing_slash(request: Request, call_next: Callable[[Request],
consent.router,
dsar.router,
dsar_multi_source.router,
my_data.router,
connectors.router,
tickets.router,
scheduler.router,
Expand Down
2 changes: 2 additions & 0 deletions ciris_engine/logic/adapters/api/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
dsar_multi_source,
emergency,
memory,
my_data,
partnership,
scheduler,
setup,
Expand Down Expand Up @@ -44,6 +45,7 @@
"dsar_multi_source",
"emergency",
"memory",
"my_data",
"partnership",
"scheduler",
"setup",
Expand Down
Loading
Loading