From bee998fdf9f95df1a460d42a453658c8130de8e6 Mon Sep 17 00:00:00 2001 From: Luigi Marini Date: Fri, 13 Sep 2024 14:42:19 -0500 Subject: [PATCH] Fixed listener query when checking if a user is in the user list. The query was not matching a user since it was an `equality` query on an `array` and not a `in` query. --- backend/app/routers/listeners.py | 17 +- openapi.json | 768 ++++++++++++++++++++++++++----- 2 files changed, 656 insertions(+), 129 deletions(-) diff --git a/backend/app/routers/listeners.py b/backend/app/routers/listeners.py index 933436f72..a1179fdc5 100644 --- a/backend/app/routers/listeners.py +++ b/backend/app/routers/listeners.py @@ -4,11 +4,6 @@ import string from typing import List, Optional -from beanie import PydanticObjectId -from beanie.operators import Or, RegEx, In, NE -from fastapi import APIRouter, Depends, HTTPException -from packaging import version - from app.config import settings from app.deps.authorization_deps import ListenerAuthorization from app.keycloak_auth import get_current_user, get_current_username, get_user @@ -27,6 +22,10 @@ from app.models.users import UserOut from app.routers.authentication import get_admin, get_admin_mode from app.routers.feeds import disassociate_listener_db +from beanie import PydanticObjectId +from beanie.operators import In, Or, RegEx +from fastapi import APIRouter, Depends, HTTPException +from packaging import version router = APIRouter() legacy_router = APIRouter() # for back-compatibilty with v1 extractors @@ -266,7 +265,7 @@ async def search_listeners( Or( EventListenerDB.access == None, EventListenerDB.access.owner == user_id, - EventListenerDB.access.users == user_id, + In(EventListenerDB.access.users, user_id), In(EventListenerDB.access.groups, user_groups), ) ) @@ -275,7 +274,7 @@ async def search_listeners( Or( EventListenerDB.access == None, EventListenerDB.access.owner == user_id, - EventListenerDB.access.users == user_id, + In(EventListenerDB.access.users, user_id), In(EventListenerDB.access.groups, user_groups), EventListenerDB.access.datasets == PydanticObjectId(dataset_id), ) @@ -423,7 +422,7 @@ async def get_listeners( Or( EventListenerDB.access == None, EventListenerDB.access.owner == user_id, - EventListenerDB.access.users == user_id, + In(EventListenerDB.access.users, user_id), In(EventListenerDB.access.groups, user_groups), ) ) @@ -432,7 +431,7 @@ async def get_listeners( Or( EventListenerDB.access == None, EventListenerDB.access.owner == user_id, - EventListenerDB.access.users == user_id, + In(EventListenerDB.access.users, user_id), In(EventListenerDB.access.groups, user_groups), EventListenerDB.access.datasets == PydanticObjectId(dataset_id), ) diff --git a/openapi.json b/openapi.json index 74418d8de..2f1283f74 100644 --- a/openapi.json +++ b/openapi.json @@ -7279,7 +7279,7 @@ "listeners" ], "summary": "Get Listeners", - "description": "Get a list of all Event Listeners in the db.\n\nArguments:\n skip -- number of initial records to skip (i.e. for pagination)\n limit -- restrict number of records to be returned (i.e. for pagination)\n heartbeat_interval -- number of seconds after which a listener is considered dead\n category -- filter by category has to be exact match\n label -- filter by label has to be exact match\n alive_only -- filter by alive status\n all -- boolean stating if we want to show all listeners irrespective of admin and admin_mode", + "description": "Get a list of all Event Listeners in the db.\n\nArguments:\n skip -- number of initial records to skip (i.e. for pagination)\n limit -- restrict number of records to be returned (i.e. for pagination)\n heartbeat_interval -- number of seconds after which a listener is considered dead\n category -- filter by category has to be exact match\n label -- filter by label has to be exact match\n alive_only -- filter by alive status\n process -- filter by file or dataset type (if specified)\n dataset_id -- restrict to listeners that run on the given dataset or a file within (if not otherwise permitted)\n all -- boolean stating if we want to show all listeners irrespective of admin and admin_mode", "operationId": "get_listeners_api_v2_listeners_get", "parameters": [ { @@ -7352,30 +7352,30 @@ { "required": false, "schema": { - "title": "All", - "type": "boolean", - "default": false + "title": "Dataset Id", + "type": "string" }, - "name": "all", + "name": "dataset_id", "in": "query" }, { "required": false, "schema": { - "title": "Enable Admin", + "title": "All", "type": "boolean", "default": false }, - "name": "enable_admin", + "name": "all", "in": "query" }, { "required": false, "schema": { - "title": "Dataset Id", - "type": "string" + "title": "Enable Admin", + "type": "boolean", + "default": false }, - "name": "dataset_id", + "name": "enable_admin", "in": "query" } ], @@ -7515,13 +7515,467 @@ "in": "query" }, { - "required": false, + "required": false, + "schema": { + "title": "Process", + "type": "string" + }, + "name": "process", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Dataset Id", + "type": "string" + }, + "name": "dataset_id", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Enable Admin", + "type": "boolean", + "default": false + }, + "name": "enable_admin", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Paged" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + } + }, + "/api/v2/listeners/categories": { + "get": { + "tags": [ + "listeners" + ], + "summary": "List Categories", + "description": "Get all the distinct categories of registered listeners in the db", + "operationId": "list_categories_api_v2_listeners_categories_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response List Categories Api V2 Listeners Categories Get", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + } + }, + "/api/v2/listeners/defaultLabels": { + "get": { + "tags": [ + "listeners" + ], + "summary": "List Default Labels", + "description": "Get all the distinct default labels of registered listeners in the db", + "operationId": "list_default_labels_api_v2_listeners_defaultLabels_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response List Default Labels Api V2 Listeners Defaultlabels Get", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + } + }, + "/api/v2/listeners/{listener_id}": { + "get": { + "tags": [ + "listeners" + ], + "summary": "Get Listener", + "description": "Return JSON information about an Event Listener if it exists.", + "operationId": "get_listener_api_v2_listeners__listener_id__get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Listener Id", + "type": "string" + }, + "name": "listener_id", + "in": "path" + }, + { + "required": false, + "schema": { + "title": "Enable Admin", + "type": "boolean", + "default": false + }, + "name": "enable_admin", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Dataset Id", + "type": "string" + }, + "name": "dataset_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventListenerOut" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + }, + "put": { + "tags": [ + "listeners" + ], + "summary": "Edit Listener", + "description": "Update the information about an existing Event Listener..\n\nArguments:\n listener_id -- UUID of the listener to be udpated\n listener_in -- JSON object including updated information", + "operationId": "edit_listener_api_v2_listeners__listener_id__put", + "parameters": [ + { + "required": true, + "schema": { + "title": "Listener Id", + "type": "string" + }, + "name": "listener_id", + "in": "path" + }, + { + "required": false, + "schema": { + "title": "Enable Admin", + "type": "boolean", + "default": false + }, + "name": "enable_admin", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Dataset Id", + "type": "string" + }, + "name": "dataset_id", + "in": "query" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventListenerIn" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventListenerOut" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + }, + "delete": { + "tags": [ + "listeners" + ], + "summary": "Delete Listener", + "description": "Remove an Event Listener from the database. Will not clear event history for the listener.", + "operationId": "delete_listener_api_v2_listeners__listener_id__delete", + "parameters": [ + { + "required": true, + "schema": { + "title": "Listener Id", + "type": "string" + }, + "name": "listener_id", + "in": "path" + }, + { + "required": false, + "schema": { + "title": "Enable Admin", + "type": "boolean", + "default": false + }, + "name": "enable_admin", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Dataset Id", + "type": "string" + }, + "name": "dataset_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + } + }, + "/api/v2/listeners/{listener_id}/status": { + "get": { + "tags": [ + "listeners" + ], + "summary": "Check Listener Livelihood", + "description": "Return JSON information about an Event Listener if it exists.", + "operationId": "check_listener_livelihood_api_v2_listeners__listener_id__status_get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Listener Id", + "type": "string" + }, + "name": "listener_id", + "in": "path" + }, + { + "required": false, + "schema": { + "title": "Heartbeat Interval", + "type": "integer", + "default": 300 + }, + "name": "heartbeat_interval", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Enable Admin", + "type": "boolean", + "default": false + }, + "name": "enable_admin", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Dataset Id", + "type": "string" + }, + "name": "dataset_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Check Listener Livelihood Api V2 Listeners Listener Id Status Get", + "type": "boolean" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + }, + { + "APIKeyHeader": [] + }, + { + "APIKeyCookie": [] + } + ] + } + }, + "/api/v2/listeners/{listener_id}/enable": { + "put": { + "tags": [ + "listeners" + ], + "summary": "Enable Listener", + "description": "Enable an Event Listener. Only admins can enable listeners.\n\nArguments:\n listener_id -- UUID of the listener to be enabled", + "operationId": "enable_listener_api_v2_listeners__listener_id__enable_put", + "parameters": [ + { + "required": true, "schema": { - "title": "Process", + "title": "Listener Id", "type": "string" }, - "name": "process", - "in": "query" + "name": "listener_id", + "in": "path" }, { "required": false, @@ -7549,7 +8003,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Paged" + "$ref": "#/components/schemas/EventListenerOut" } } } @@ -7578,62 +8032,61 @@ ] } }, - "/api/v2/listeners/categories": { - "get": { + "/api/v2/listeners/{listener_id}/disable": { + "put": { "tags": [ "listeners" ], - "summary": "List Categories", - "description": "Get all the distinct categories of registered listeners in the db", - "operationId": "list_categories_api_v2_listeners_categories_get", - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response List Categories Api V2 Listeners Categories Get", - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - }, - "security": [ + "summary": "Disable Listener", + "description": "Disable an Event Listener. Only admins can enable listeners.\n\nArguments:\n listener_id -- UUID of the listener to be enabled", + "operationId": "disable_listener_api_v2_listeners__listener_id__disable_put", + "parameters": [ { - "OAuth2AuthorizationCodeBearer": [] + "required": true, + "schema": { + "title": "Listener Id", + "type": "string" + }, + "name": "listener_id", + "in": "path" }, { - "APIKeyHeader": [] + "required": false, + "schema": { + "title": "Enable Admin", + "type": "boolean", + "default": false + }, + "name": "enable_admin", + "in": "query" }, { - "APIKeyCookie": [] + "required": false, + "schema": { + "title": "Dataset Id", + "type": "string" + }, + "name": "dataset_id", + "in": "query" } - ] - } - }, - "/api/v2/listeners/defaultLabels": { - "get": { - "tags": [ - "listeners" ], - "summary": "List Default Labels", - "description": "Get all the distinct default labels of registered listeners in the db", - "operationId": "list_default_labels_api_v2_listeners_defaultLabels_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { - "title": "Response List Default Labels Api V2 Listeners Defaultlabels Get", - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/components/schemas/EventListenerOut" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" } } } @@ -7652,14 +8105,13 @@ ] } }, - "/api/v2/listeners/{listener_id}": { - "get": { + "/api/v2/listeners/{listener_id}/users/{target_user}": { + "post": { "tags": [ "listeners" ], - "summary": "Get Listener", - "description": "Return JSON information about an Event Listener if it exists.", - "operationId": "get_listener_api_v2_listeners__listener_id__get", + "summary": "Add User Permission", + "operationId": "add_user_permission_api_v2_listeners__listener_id__users__target_user__post", "parameters": [ { "required": true, @@ -7670,6 +8122,15 @@ "name": "listener_id", "in": "path" }, + { + "required": true, + "schema": { + "title": "Target User", + "type": "string" + }, + "name": "target_user", + "in": "path" + }, { "required": false, "schema": { @@ -7695,9 +8156,7 @@ "description": "Successful Response", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/EventListenerOut" - } + "schema": {} } } }, @@ -7724,13 +8183,12 @@ } ] }, - "put": { + "delete": { "tags": [ "listeners" ], - "summary": "Edit Listener", - "description": "Update the information about an existing Event Listener..\n\nArguments:\n listener_id -- UUID of the listener to be udpated\n listener_in -- JSON object including updated information", - "operationId": "edit_listener_api_v2_listeners__listener_id__put", + "summary": "Remove User Permission", + "operationId": "remove_user_permission_api_v2_listeners__listener_id__users__target_user__delete", "parameters": [ { "required": true, @@ -7741,6 +8199,15 @@ "name": "listener_id", "in": "path" }, + { + "required": true, + "schema": { + "title": "Target User", + "type": "string" + }, + "name": "target_user", + "in": "path" + }, { "required": false, "schema": { @@ -7761,24 +8228,12 @@ "in": "query" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventListenerIn" - } - } - }, - "required": true - }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/EventListenerOut" - } + "schema": {} } } }, @@ -7804,14 +8259,15 @@ "APIKeyCookie": [] } ] - }, - "delete": { + } + }, + "/api/v2/listeners/{listener_id}/groups/{target_group}": { + "post": { "tags": [ "listeners" ], - "summary": "Delete Listener", - "description": "Remove an Event Listener from the database. Will not clear event history for the listener.", - "operationId": "delete_listener_api_v2_listeners__listener_id__delete", + "summary": "Add Group Permission", + "operationId": "add_group_permission_api_v2_listeners__listener_id__groups__target_group__post", "parameters": [ { "required": true, @@ -7822,6 +8278,15 @@ "name": "listener_id", "in": "path" }, + { + "required": true, + "schema": { + "title": "Target Group", + "type": "string" + }, + "name": "target_group", + "in": "path" + }, { "required": false, "schema": { @@ -7873,16 +8338,13 @@ "APIKeyCookie": [] } ] - } - }, - "/api/v2/listeners/{listener_id}/status": { - "get": { + }, + "delete": { "tags": [ "listeners" ], - "summary": "Check Listener Livelihood", - "description": "Return JSON information about an Event Listener if it exists.", - "operationId": "check_listener_livelihood_api_v2_listeners__listener_id__status_get", + "summary": "Remove Group Permission", + "operationId": "remove_group_permission_api_v2_listeners__listener_id__groups__target_group__delete", "parameters": [ { "required": true, @@ -7894,14 +8356,13 @@ "in": "path" }, { - "required": false, + "required": true, "schema": { - "title": "Heartbeat Interval", - "type": "integer", - "default": 300 + "title": "Target Group", + "type": "string" }, - "name": "heartbeat_interval", - "in": "query" + "name": "target_group", + "in": "path" }, { "required": false, @@ -7928,10 +8389,7 @@ "description": "Successful Response", "content": { "application/json": { - "schema": { - "title": "Response Check Listener Livelihood Api V2 Listeners Listener Id Status Get", - "type": "boolean" - } + "schema": {} } } }, @@ -7959,14 +8417,13 @@ ] } }, - "/api/v2/listeners/{listener_id}/enable": { - "put": { + "/api/v2/listeners/{listener_id}/datasets/{target_dataset}": { + "post": { "tags": [ "listeners" ], - "summary": "Enable Listener", - "description": "Enable an Event Listener. Only admins can enable listeners.\n\nArguments:\n listener_id -- UUID of the listener to be enabled", - "operationId": "enable_listener_api_v2_listeners__listener_id__enable_put", + "summary": "Add Dataset Permission", + "operationId": "add_dataset_permission_api_v2_listeners__listener_id__datasets__target_dataset__post", "parameters": [ { "required": true, @@ -7977,6 +8434,15 @@ "name": "listener_id", "in": "path" }, + { + "required": true, + "schema": { + "title": "Target Dataset", + "type": "string" + }, + "name": "target_dataset", + "in": "path" + }, { "required": false, "schema": { @@ -8002,9 +8468,7 @@ "description": "Successful Response", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/EventListenerOut" - } + "schema": {} } } }, @@ -8030,16 +8494,13 @@ "APIKeyCookie": [] } ] - } - }, - "/api/v2/listeners/{listener_id}/disable": { - "put": { + }, + "delete": { "tags": [ "listeners" ], - "summary": "Disable Listener", - "description": "Disable an Event Listener. Only admins can enable listeners.\n\nArguments:\n listener_id -- UUID of the listener to be enabled", - "operationId": "disable_listener_api_v2_listeners__listener_id__disable_put", + "summary": "Remove Dataset Permission", + "operationId": "remove_dataset_permission_api_v2_listeners__listener_id__datasets__target_dataset__delete", "parameters": [ { "required": true, @@ -8050,6 +8511,15 @@ "name": "listener_id", "in": "path" }, + { + "required": true, + "schema": { + "title": "Target Dataset", + "type": "string" + }, + "name": "target_dataset", + "in": "path" + }, { "required": false, "schema": { @@ -8075,9 +8545,7 @@ "description": "Successful Response", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/EventListenerOut" - } + "schema": {} } } }, @@ -11627,6 +12095,52 @@ }, "components": { "schemas": { + "AccessList": { + "title": "AccessList", + "required": [ + "owner" + ], + "type": "object", + "properties": { + "owner": { + "title": "Owner", + "type": "string" + }, + "users": { + "title": "Users", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "groups": { + "title": "Groups", + "type": "array", + "items": { + "type": "string", + "examples": [ + "5eb7cf5a86d9755df3a6c593", + "5eb7cfb05e32e07750a1756a" + ] + }, + "default": [] + }, + "datasets": { + "title": "Datasets", + "type": "array", + "items": { + "type": "string", + "examples": [ + "5eb7cf5a86d9755df3a6c593", + "5eb7cfb05e32e07750a1756a" + ] + }, + "default": [] + } + }, + "description": "Container object for lists of user emails/group IDs/dataset IDs that can submit to listener.\nThe singular owner is the primary who can modify other lists." + }, "AuthorizationBase": { "title": "AuthorizationBase", "required": [ @@ -12229,6 +12743,9 @@ "title": "Description", "type": "string", "default": "" + }, + "access": { + "$ref": "#/components/schemas/AccessList" } }, "description": "On submission, minimum info for a listener is name, version and description. Clowder will use name and version to locate queue." @@ -12420,6 +12937,9 @@ "type": "string", "default": "" }, + "access": { + "$ref": "#/components/schemas/AccessList" + }, "id": { "title": "Id", "type": "string", @@ -12565,6 +13085,10 @@ "title": "Version", "type": "string", "default": "1.0" + }, + "unique_key": { + "title": "Unique Key", + "type": "string" } }, "description": "Currently for extractor_info JSON from Clowder v1 extractors for use with to /api/extractors endpoint." @@ -13195,6 +13719,10 @@ "type": "string", "default": "1.0" }, + "unique_key": { + "title": "Unique Key", + "type": "string" + }, "description": { "title": "Description", "type": "string",