2222
2323import os
2424import socket
25+ import struct
2526import asyncio
2627import contextlib
2728import dataclasses
@@ -83,21 +84,30 @@ class HttpExposed:
8384 method : str
8485 path : str
8586 auth_required : bool
87+ allow_usc : bool
8688 handler : Callable
8789
8890
8991_HTTP_EXPOSED = "_http_exposed"
9092_HTTP_METHOD = "_http_method"
9193_HTTP_PATH = "_http_path"
9294_HTTP_AUTH_REQUIRED = "_http_auth_required"
95+ _HTTP_ALLOW_USC = "_http_allow_usc"
9396
9497
95- def exposed_http (http_method : str , path : str , auth_required : bool = True ) -> Callable :
98+ def exposed_http (
99+ http_method : str ,
100+ path : str ,
101+ auth_required : bool = True ,
102+ allow_usc : bool = True ,
103+ ) -> Callable :
104+
96105 def set_attrs (handler : Callable ) -> Callable :
97106 setattr (handler , _HTTP_EXPOSED , True )
98107 setattr (handler , _HTTP_METHOD , http_method )
99108 setattr (handler , _HTTP_PATH , path )
100109 setattr (handler , _HTTP_AUTH_REQUIRED , auth_required )
110+ setattr (handler , _HTTP_ALLOW_USC , allow_usc )
101111 return handler
102112 return set_attrs
103113
@@ -108,6 +118,7 @@ def _get_exposed_http(obj: object) -> list[HttpExposed]:
108118 method = getattr (handler , _HTTP_METHOD ),
109119 path = getattr (handler , _HTTP_PATH ),
110120 auth_required = getattr (handler , _HTTP_AUTH_REQUIRED ),
121+ allow_usc = getattr (handler , _HTTP_ALLOW_USC ),
111122 handler = handler ,
112123 )
113124 for handler in [getattr (obj , name ) for name in dir (obj )]
@@ -270,6 +281,29 @@ def set_request_auth_info(req: BaseRequest, info: str) -> None:
270281 setattr (req , _REQUEST_AUTH_INFO , info )
271282
272283
284+ @dataclasses .dataclass (frozen = True )
285+ class RequestUnixCredentials :
286+ pid : int
287+ uid : int
288+ gid : int
289+
290+
291+ def get_request_unix_credentials (req : BaseRequest ) -> (RequestUnixCredentials | None ):
292+ if req .transport is None :
293+ return None
294+ sock = req .transport .get_extra_info ("socket" )
295+ if sock is None :
296+ return None
297+ try :
298+ data = sock .getsockopt (socket .SOL_SOCKET , socket .SO_PEERCRED , struct .calcsize ("iii" ))
299+ except Exception :
300+ return None
301+ (pid , uid , gid ) = struct .unpack ("iii" , data )
302+ if pid <= 0 or uid < 0 or gid < 0 :
303+ return None
304+ return RequestUnixCredentials (pid = pid , uid = uid , gid = gid )
305+
306+
273307# =====
274308@dataclasses .dataclass (frozen = True )
275309class WsSession :
@@ -314,13 +348,14 @@ def run(
314348
315349 if unix_rm and os .path .exists (unix_path ):
316350 os .remove (unix_path )
317- server_socket = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
318- server_socket .bind (unix_path )
351+ sock = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
352+ sock .setsockopt (socket .SOL_SOCKET , socket .SO_PASSCRED , 1 )
353+ sock .bind (unix_path )
319354 if unix_mode :
320355 os .chmod (unix_path , unix_mode )
321356
322357 run_app (
323- sock = server_socket ,
358+ sock = sock ,
324359 app = self .__make_app (),
325360 shutdown_timeout = 1 ,
326361 access_log_format = access_log_format ,
0 commit comments