From f3edf9603008d0ebdfac06458d367682a8ea1696 Mon Sep 17 00:00:00 2001 From: Syed Ahmed Date: Tue, 29 Apr 2025 09:33:49 -0400 Subject: [PATCH] config: Add feature flag for user events (PROJQUAY-8839) (#3830) * config: Add feature flag for user events (PROJQUAY-8839) --- config.py | 3 +++ endpoints/v1/index.py | 13 ++++++++----- endpoints/v2/v2auth.py | 6 ++++-- util/audit.py | 8 ++++++-- util/config/schema.py | 5 +++++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/config.py b/config.py index 13144d53b..84ab0ca36 100644 --- a/config.py +++ b/config.py @@ -366,6 +366,9 @@ class DefaultConfig(ImmutableConfig): # Feature Flag: Whether the v2/ endpoint is visible FEATURE_ADVERTISE_V2 = True + # Allow realtime user events to be sent to redis + FEATURE_USER_EVENTS = True + # Semver spec for which Docker versions we will blacklist # Documentation: http://pythonhosted.org/semantic_version/reference.html#semantic_version.Spec BLACKLIST_V2_SPEC = "<1.6.0" diff --git a/endpoints/v1/index.py b/endpoints/v1/index.py index 1abc19585..f4d354a27 100644 --- a/endpoints/v1/index.py +++ b/endpoints/v1/index.py @@ -129,8 +129,9 @@ def create_user(): if kind == CredentialKind.user: # Mark that the login failed. - event = userevents.get_event(username) - event.publish_event_data("docker-cli", {"action": "loginfailure"}) + if app.config.get("FEATURE_USER_EVENTS", True): + event = userevents.get_event(username) + event.publish_event_data("docker-cli", {"action": "loginfailure"}) abort(400, result.error_message, issue="login-failure") # Default case: Just fail. @@ -144,8 +145,10 @@ def create_user(): result.authed_user.username, {"type": "quayauth", "useragent": request.user_agent.string}, ) - event = userevents.get_event(username) - event.publish_event_data("docker-cli", {"action": "login"}) + + if app.config.get("FEATURE_USER_EVENTS", True): + event = userevents.get_event(username) + event.publish_event_data("docker-cli", {"action": "login"}) return success @@ -254,7 +257,7 @@ def create_repository(namespace_name, repo_name): namespace_name, repo_name, get_authenticated_user() ) - if get_authenticated_user(): + if get_authenticated_user() and app.config.get("FEATURE_USER_EVENTS", True): user_event_data = { "action": "push_start", "repository": repo_name, diff --git a/endpoints/v2/v2auth.py b/endpoints/v2/v2auth.py index 10fe1096f..e8fb167aa 100644 --- a/endpoints/v2/v2auth.py +++ b/endpoints/v2/v2auth.py @@ -158,8 +158,10 @@ def generate_registry_jwt(auth_result): user.username if not user.robot else parse_robot_username(user.username)[0], metadata=metadata, ) - event = userevents.get_event(user.username) - event.publish_event_data("docker-cli", user_event_data) + + if app.config.get("FEATURE_USER_EVENTS", False): + event = userevents.get_event(user.username) + event.publish_event_data("docker-cli", user_event_data) # Build the signed JWT. tuf_roots = { diff --git a/util/audit.py b/util/audit.py index aeca39064..30838224f 100644 --- a/util/audit.py +++ b/util/audit.py @@ -5,7 +5,7 @@ from urllib.parse import urlparse from flask import request -from app import analytics, ip_resolver, userevents +from app import analytics, app, ip_resolver, userevents from auth.auth_context import get_authenticated_context, get_authenticated_user from data.logs_model import logs_model from data.readreplica import ReadOnlyModeException @@ -48,7 +48,11 @@ def track_and_log(event_name, repo_obj, analytics_name=None, analytics_sample=1, # Publish the user event (if applicable) logger.debug("Checking publishing %s to the user events system", event_name) - if auth_context and auth_context.has_nonrobot_user: + if ( + auth_context + and auth_context.has_nonrobot_user + and app.config.get("FEATURE_USER_EVENTS", True) + ): logger.debug("Publishing %s to the user events system", event_name) user_event_data = { "action": event_name, diff --git a/util/config/schema.py b/util/config/schema.py index 42f2ed57c..effae7584 100644 --- a/util/config/schema.py +++ b/util/config/schema.py @@ -428,6 +428,11 @@ CONFIG_SCHEMA = { }, }, }, + "FEATURE_USER_EVENTS": { + "type": "boolean", + "description": "Whether user events are enabled. Defaults to True", + "x-example": True, + }, "USER_EVENTS_REDIS": { "type": "object", "description": "Connection information for Redis for user event handling",