22
22
23
23
import os
24
24
import socket
25
+ import struct
25
26
import asyncio
26
27
import contextlib
27
28
import dataclasses
@@ -83,21 +84,30 @@ class HttpExposed:
83
84
method : str
84
85
path : str
85
86
auth_required : bool
87
+ allow_usc : bool
86
88
handler : Callable
87
89
88
90
89
91
_HTTP_EXPOSED = "_http_exposed"
90
92
_HTTP_METHOD = "_http_method"
91
93
_HTTP_PATH = "_http_path"
92
94
_HTTP_AUTH_REQUIRED = "_http_auth_required"
95
+ _HTTP_ALLOW_USC = "_http_allow_usc"
93
96
94
97
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
+
96
105
def set_attrs (handler : Callable ) -> Callable :
97
106
setattr (handler , _HTTP_EXPOSED , True )
98
107
setattr (handler , _HTTP_METHOD , http_method )
99
108
setattr (handler , _HTTP_PATH , path )
100
109
setattr (handler , _HTTP_AUTH_REQUIRED , auth_required )
110
+ setattr (handler , _HTTP_ALLOW_USC , allow_usc )
101
111
return handler
102
112
return set_attrs
103
113
@@ -108,6 +118,7 @@ def _get_exposed_http(obj: object) -> list[HttpExposed]:
108
118
method = getattr (handler , _HTTP_METHOD ),
109
119
path = getattr (handler , _HTTP_PATH ),
110
120
auth_required = getattr (handler , _HTTP_AUTH_REQUIRED ),
121
+ allow_usc = getattr (handler , _HTTP_ALLOW_USC ),
111
122
handler = handler ,
112
123
)
113
124
for handler in [getattr (obj , name ) for name in dir (obj )]
@@ -270,6 +281,23 @@ def set_request_auth_info(req: BaseRequest, info: str) -> None:
270
281
setattr (req , _REQUEST_AUTH_INFO , info )
271
282
272
283
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
+ data = sock .getsockopt (socket .SOL_SOCKET , socket .SO_PEERCRED , struct .calcsize ("iii" ))
298
+ return RequestUnixCredentials (* struct .unpack ("iii" , data ))
299
+
300
+
273
301
# =====
274
302
@dataclasses .dataclass (frozen = True )
275
303
class WsSession :
@@ -314,13 +342,14 @@ def run(
314
342
315
343
if unix_rm and os .path .exists (unix_path ):
316
344
os .remove (unix_path )
317
- server_socket = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
318
- server_socket .bind (unix_path )
345
+ sock = socket .socket (socket .AF_UNIX , socket .SOCK_STREAM )
346
+ sock .setsockopt (socket .SOL_SOCKET , socket .SO_PASSCRED , 1 )
347
+ sock .bind (unix_path )
319
348
if unix_mode :
320
349
os .chmod (unix_path , unix_mode )
321
350
322
351
run_app (
323
- sock = server_socket ,
352
+ sock = sock ,
324
353
app = self .__make_app (),
325
354
shutdown_timeout = 1 ,
326
355
access_log_format = access_log_format ,
0 commit comments