@@ -1333,17 +1333,41 @@ async def stream_response():
13331333
13341334
13351335@method_decorator (csrf_exempt , name = 'dispatch' )
1336- class QuickwitProxyView (View ):
1336+ class BaseUserProxyView (View ):
13371337 timeout = aiohttp .ClientTimeout (total = 30 , connect = 10 , sock_read = 15 )
13381338 permission = permissions .IsServiceToken ()
13391339 authentication = authentication .DryccAuthentication ()
13401340 authentication .ignore_authentication_failed = True
1341+
1342+ async def authenticate (self , request , username ):
1343+ """
1344+ Authenticate the user based on the provided request and username.
1345+ Returns the user ID on success; returns None and an error message on failure.
1346+ """
1347+ if self .permission .has_permission (request , None ):
1348+ if username != "drycc" :
1349+ return None , {'error' : 'Access denied' , "status_code" : 403 }
1350+ return - 1 , None
1351+ auth = await database_sync_to_async (self .authentication .authenticate )(request )
1352+ if not auth or len (auth ) != 2 :
1353+ return None , {'error' : 'Unauthorized' , "status_code" : 401 }
1354+ if auth [0 ].username != username :
1355+ return None , {'error' : 'Access denied' , "status_code" : 403 }
1356+ return auth [0 ].id , None
1357+
1358+
1359+ @method_decorator (csrf_exempt , name = 'dispatch' )
1360+ class QuickwitProxyView (BaseUserProxyView ):
13411361 index_url_match = re .compile (r"^indexes/?$" ).match
13421362 search_url_match = re .compile (r"^(?P<index>[a-zA-Z*][\w.*-,]{0,})/search/?$" ).match
13431363 msearch_url_match = re .compile (r"^_elastic/_msearch/?$" ).match
1344- field_caps_url_match = re .compile (r"_elastic/(?P<index>[a-zA-Z*][\w.*-,]{0,})/_field_caps/?$" ).match
1364+ field_caps_url_match = re .compile (
1365+ r"_elastic/(?P<index>[a-zA-Z*][\w.*-,]{0,})/_field_caps/?$" ).match
13451366
13461367 async def proxy (self , request , username , path ):
1368+ user_id , message = await self .authenticate (request , username )
1369+ if user_id is None and message is not None :
1370+ return JsonResponse (message , status = message ["status_code" ])
13471371 kwargs = {"request" : request , "username" : username }
13481372 if self .index_url_match (path ):
13491373 func , kwargs ["index" ] = self .index , request .GET .get ("index_id_patterns" , "*" )
@@ -1354,17 +1378,7 @@ async def proxy(self, request, username, path):
13541378 elif match := self .field_caps_url_match (path ):
13551379 func , kwargs ["index" ] = self .field_caps , match .group ("index" )
13561380 else :
1357- raise JsonResponse ({'error' : 'Not Found' }, status = 404 )
1358-
1359- if self .permission .has_permission (request , None ):
1360- if username != "drycc" :
1361- return JsonResponse ({'error' : 'Access denied' }, status = 403 )
1362- else :
1363- auth = await database_sync_to_async (self .authentication .authenticate )(request )
1364- if not auth or len (auth ) != 2 :
1365- return JsonResponse ({'error' : 'Unauthorized' }, status = 401 )
1366- if auth [0 ].username != username :
1367- return JsonResponse ({'error' : 'Access denied' }, status = 403 )
1381+ return JsonResponse ({'error' : 'Not Found' }, status = 404 )
13681382 return await func (** kwargs )
13691383
13701384 async def index (self , request , username , index ):
@@ -1403,7 +1417,7 @@ async def msearch(self, request, username):
14031417 ).split ("," )
14041418 json_lines [i ] = json .dumps (request_header )
14051419 url , params = urljoin (
1406- base_url , f "/api/v1/_elastic/_msearch" ), dict (request .GET )
1420+ base_url , "/api/v1/_elastic/_msearch" ), dict (request .GET )
14071421 try :
14081422 async with aiohttp .ClientSession () as session :
14091423 async with session .post (
@@ -1451,30 +1465,23 @@ async def get_app_indexes(self, username, index):
14511465
14521466
14531467@method_decorator (csrf_exempt , name = 'dispatch' )
1454- class PrometheusProxyView (View ):
1455- timeout = aiohttp .ClientTimeout (total = 30 , connect = 10 , sock_read = 15 )
1456- permission = permissions .IsServiceToken ()
1457- authentication = authentication .DryccAuthentication ()
1458- authentication .ignore_authentication_failed = True
1459-
1468+ class PrometheusProxyView (BaseUserProxyView ):
14601469 async def proxy (self , request , username , path ):
1461- if self .permission .has_permission (request , None ):
1462- if username != "drycc" :
1463- return JsonResponse ({'error' : 'Access denied' }, status = 403 )
1470+ user_id , message = await self .authenticate (request , username )
1471+ if user_id is None and message is not None :
1472+ return JsonResponse (message , status = message ["status_code" ])
1473+ if username == "drycc" :
14641474 path = f"/select/0/prometheus/{ path } "
14651475 else :
1466- auth = await database_sync_to_async (self .authentication .authenticate )(request )
1467- if not auth or len (auth ) != 2 :
1468- return JsonResponse ({'error' : 'Unauthorized' }, status = 401 )
1469- if auth [0 ].username != username :
1470- return JsonResponse ({'error' : 'Access denied' }, status = 403 )
1471- else :
1472- path = f"/select/{ auth [0 ].id } /prometheus/{ path } "
1473- url = urljoin (settings .DRYCC_VICTORIAMETRICS_URL , path )
1474- params = dict (request .GET ) if request .method == "GET" else dict (request .POST )
1476+ path = f"/select/{ user_id } /prometheus/{ path } "
14751477 try :
14761478 async with aiohttp .ClientSession () as session :
1477- async with session .get (url , params = params , timeout = self .timeout ) as response :
1479+ async with session .post (
1480+ urljoin (settings .DRYCC_VICTORIAMETRICS_URL , path ),
1481+ data = dict (request .GET ) if request .method == "GET" else dict (request .POST ),
1482+ headers = {"Content-Type" : "application/x-www-form-urlencoded" },
1483+ timeout = self .timeout
1484+ ) as response :
14781485 data , status = await response .json (), response .status
14791486 except aiohttp .ClientError as e :
14801487 data , status = {'error' : f'victoriametrics connection failed: { str (e )} ' }, 502
0 commit comments