vgi-python 0.36.2__py3-none-any.whl → 0.37.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vgi/auth.py +12 -3
- vgi/serve.py +15 -7
- vgi/worker.py +75 -1
- {vgi_python-0.36.2.dist-info → vgi_python-0.37.0.dist-info}/METADATA +1 -1
- {vgi_python-0.36.2.dist-info → vgi_python-0.37.0.dist-info}/RECORD +8 -8
- {vgi_python-0.36.2.dist-info → vgi_python-0.37.0.dist-info}/WHEEL +0 -0
- {vgi_python-0.36.2.dist-info → vgi_python-0.37.0.dist-info}/entry_points.txt +0 -0
- {vgi_python-0.36.2.dist-info → vgi_python-0.37.0.dist-info}/licenses/LICENSE +0 -0
vgi/auth.py
CHANGED
|
@@ -9,8 +9,8 @@ HTTP auth factories (require ``vgi[http]``):
|
|
|
9
9
|
bearer_authenticate, bearer_authenticate_static, chain_authenticate,
|
|
10
10
|
OAuthResourceMetadata, AuthUnavailableError
|
|
11
11
|
|
|
12
|
-
Token introspection (always available):
|
|
13
|
-
TokenIdentity, TokenResolver
|
|
12
|
+
Token introspection and delegation (always available):
|
|
13
|
+
TokenIdentity, TokenResolver, IssuedGrant, GrantMinter
|
|
14
14
|
|
|
15
15
|
JWT auth (requires ``vgi[oauth]``):
|
|
16
16
|
jwt_authenticate
|
|
@@ -30,16 +30,25 @@ from vgi_rpc.rpc import AuthContext, CallContext
|
|
|
30
30
|
# on the ``http`` extra. Still not re-exported by ``vgi_rpc.rpc`` itself, so
|
|
31
31
|
# the private module remains the only import path; re-exported here so a worker
|
|
32
32
|
# that implements ``resolve_token`` never has to name one.
|
|
33
|
-
from vgi_rpc.rpc._token_identity import TokenIdentity
|
|
33
|
+
from vgi_rpc.rpc._token_identity import IssuedGrant, TokenIdentity
|
|
34
34
|
|
|
35
35
|
#: What :meth:`vgi.worker.Worker.resolve_token` is. Upstream dropped its own
|
|
36
36
|
#: alias when identity moved to the RPC layer; it is spelled out here so the
|
|
37
37
|
#: name ``vgi.auth.TokenResolver`` keeps working.
|
|
38
38
|
TokenResolver = Callable[[str], "TokenIdentity | None"]
|
|
39
39
|
|
|
40
|
+
#: What :meth:`vgi.worker.Worker.mint_grant` is: ``(principal, purpose, scopes,
|
|
41
|
+
#: ttl_seconds) -> IssuedGrant``. The subject is a *parameter of the call the
|
|
42
|
+
#: framework makes*, never of the wire method — ``issue_grant`` has no subject
|
|
43
|
+
#: field, so a worker is handed the caller's own principal and cannot be asked
|
|
44
|
+
#: to mint for anybody else.
|
|
45
|
+
GrantMinter = Callable[[str, str, list[str], int], "IssuedGrant"]
|
|
46
|
+
|
|
40
47
|
__all__ = [
|
|
41
48
|
"AuthContext",
|
|
42
49
|
"CallContext",
|
|
50
|
+
"GrantMinter",
|
|
51
|
+
"IssuedGrant",
|
|
43
52
|
"TokenIdentity",
|
|
44
53
|
"TokenResolver",
|
|
45
54
|
]
|
vgi/serve.py
CHANGED
|
@@ -791,9 +791,12 @@ def _build_identity(
|
|
|
791
791
|
) -> IdentityImpl | None:
|
|
792
792
|
"""Build the ``vgi_rpc.Identity.v1`` implementation, or ``None``.
|
|
793
793
|
|
|
794
|
-
``None`` unless the worker class
|
|
795
|
-
is the point: ``RpcServer`` then does not host the
|
|
796
|
-
than hosting it and refusing every call.
|
|
794
|
+
``None`` unless the worker class implements at least one of the two
|
|
795
|
+
methods, and that is the point: ``RpcServer`` then does not host the
|
|
796
|
+
protocol at all, rather than hosting it and refusing every call. The two
|
|
797
|
+
are independent — a worker may resolve credentials without minting them,
|
|
798
|
+
mint without resolving, or do both, and ``offered_methods()`` reports
|
|
799
|
+
exactly what it wrote. Absent beats routed-and-refusing —
|
|
797
800
|
it is what keeps a dependency upgrade from growing a
|
|
798
801
|
credential-to-identity oracle on every existing worker.
|
|
799
802
|
|
|
@@ -804,8 +807,8 @@ def _build_identity(
|
|
|
804
807
|
calling and reading an error.
|
|
805
808
|
|
|
806
809
|
Args:
|
|
807
|
-
worker_cls: The worker class, consulted for
|
|
808
|
-
|
|
810
|
+
worker_cls: The worker class, consulted for ``resolve_token`` and
|
|
811
|
+
``mint_grant`` overrides.
|
|
809
812
|
introspect_principals: Principals permitted to introspect, or ``None``
|
|
810
813
|
to read the environment.
|
|
811
814
|
|
|
@@ -815,7 +818,8 @@ def _build_identity(
|
|
|
815
818
|
|
|
816
819
|
"""
|
|
817
820
|
resolver = worker_cls._introspect_resolver()
|
|
818
|
-
|
|
821
|
+
minter = worker_cls._grant_minter()
|
|
822
|
+
if resolver is None and minter is None:
|
|
819
823
|
return None
|
|
820
824
|
|
|
821
825
|
# Not re-exported by ``vgi_rpc.rpc``, so the private module is the only
|
|
@@ -824,7 +828,11 @@ def _build_identity(
|
|
|
824
828
|
|
|
825
829
|
return IdentityImpl(
|
|
826
830
|
resolve_token=resolver,
|
|
827
|
-
|
|
831
|
+
mint_grant=minter,
|
|
832
|
+
# Only meaningful for ``introspect_token``, and ``IdentityImpl``
|
|
833
|
+
# validates it only when a resolver is supplied — a worker that mints
|
|
834
|
+
# grants but resolves nothing is not an oracle and needs no allowlist.
|
|
835
|
+
introspect_principals=(_resolve_introspect_principals(introspect_principals) if resolver is not None else None),
|
|
828
836
|
)
|
|
829
837
|
|
|
830
838
|
|
vgi/worker.py
CHANGED
|
@@ -173,7 +173,7 @@ if TYPE_CHECKING:
|
|
|
173
173
|
from ssl import SSLContext
|
|
174
174
|
|
|
175
175
|
from vgi_rpc.rpc import PeerAuthenticationPolicy, PeerIdentityProvider
|
|
176
|
-
from vgi_rpc.rpc._token_identity import TokenIdentity
|
|
176
|
+
from vgi_rpc.rpc._token_identity import IssuedGrant, TokenIdentity
|
|
177
177
|
|
|
178
178
|
from vgi.catalog.descriptors import Catalog
|
|
179
179
|
from vgi.protocol import (
|
|
@@ -1478,6 +1478,80 @@ class Worker:
|
|
|
1478
1478
|
return None
|
|
1479
1479
|
return cls.resolve_token
|
|
1480
1480
|
|
|
1481
|
+
@classmethod
|
|
1482
|
+
def mint_grant(cls, principal: str, purpose: str, scopes: list[str], ttl_seconds: int) -> IssuedGrant:
|
|
1483
|
+
"""Mint a standing delegation credential for *principal*.
|
|
1484
|
+
|
|
1485
|
+
Override to host ``vgi_rpc.Identity.v1``'s ``issue_grant``. Like
|
|
1486
|
+
``resolve_token``, the method is absent until it is overridden rather
|
|
1487
|
+
than hosted-and-refusing, so no worker grows a credential *issuer* by
|
|
1488
|
+
upgrading a dependency.
|
|
1489
|
+
|
|
1490
|
+
OAuth cannot express durable delegation: it fuses the grant, the
|
|
1491
|
+
credential and the session into one refresh token, so an IdP shortening
|
|
1492
|
+
session lifetime shortens the grant. This mints the durable record —
|
|
1493
|
+
created while the user is present, presented later by unattended
|
|
1494
|
+
automation as an ordinary bearer.
|
|
1495
|
+
|
|
1496
|
+
**There is no subject on the wire.** ``issue_grant`` takes no subject
|
|
1497
|
+
field; the framework passes the *caller's own* authenticated principal,
|
|
1498
|
+
so cross-subject minting is closed by construction rather than by a
|
|
1499
|
+
check that could be forgotten. That is also why this needs no allowlist
|
|
1500
|
+
while ``introspect_token`` has one — it is not an oracle about anybody
|
|
1501
|
+
else. The framework additionally refuses a caller whose authentication
|
|
1502
|
+
is stale (``max_auth_age``, 15 minutes by default), because "minted
|
|
1503
|
+
while the user is present" is the property that makes a grant
|
|
1504
|
+
accountable to them.
|
|
1505
|
+
|
|
1506
|
+
``IssuedGrant.token`` is **opaque to the framework** — a sealed
|
|
1507
|
+
envelope, a database row, or a credential brokered from the IdP are all
|
|
1508
|
+
equally valid and equally invisible here. It is never parsed and never
|
|
1509
|
+
logged. Note that a self-contained sealed token cannot be revoked
|
|
1510
|
+
individually: with no server-side record there is nothing to delete,
|
|
1511
|
+
and rotating the sealing key invalidates every grant at once. If
|
|
1512
|
+
per-grant revocation matters, the token has to name something the
|
|
1513
|
+
worker can look up and remove.
|
|
1514
|
+
|
|
1515
|
+
Args:
|
|
1516
|
+
principal: The caller's authenticated principal, in the form this
|
|
1517
|
+
worker derives itself. Supplied by the framework, never by the
|
|
1518
|
+
caller.
|
|
1519
|
+
purpose: Free-text reason recorded with the grant, for the audit
|
|
1520
|
+
trail.
|
|
1521
|
+
scopes: Scopes the grant is limited to. A worker that ignores this
|
|
1522
|
+
is issuing a credential broader than the caller asked for.
|
|
1523
|
+
ttl_seconds: Lifetime the caller requested. A worker may issue a
|
|
1524
|
+
shorter one — and should say so in ``expires_at`` — but must
|
|
1525
|
+
never issue a longer one.
|
|
1526
|
+
|
|
1527
|
+
Returns:
|
|
1528
|
+
The minted ``IssuedGrant``. ``expires_at`` is a declaration rather
|
|
1529
|
+
than an enforcement: the real lifetime lives inside the opaque
|
|
1530
|
+
token, so a worker that states one has thought about one.
|
|
1531
|
+
|
|
1532
|
+
Raises:
|
|
1533
|
+
GrantRefusedError: When this caller may not mint this grant —
|
|
1534
|
+
an unsupported scope, a ttl beyond policy, a principal the
|
|
1535
|
+
worker declines to delegate for.
|
|
1536
|
+
|
|
1537
|
+
"""
|
|
1538
|
+
from vgi_rpc.rpc._token_identity import GrantRefusedError
|
|
1539
|
+
|
|
1540
|
+
raise GrantRefusedError("this worker does not mint grants")
|
|
1541
|
+
|
|
1542
|
+
@classmethod
|
|
1543
|
+
def _grant_minter(cls) -> Callable[[str, str, list[str], int], IssuedGrant] | None:
|
|
1544
|
+
"""Return this worker's grant minter, or ``None`` when it has none.
|
|
1545
|
+
|
|
1546
|
+
Identity comparison against the base implementation, for the same
|
|
1547
|
+
reason ``_introspect_resolver`` uses one: the method must be absent
|
|
1548
|
+
unless a worker actually wrote the minting, and a "supports grants"
|
|
1549
|
+
flag can be set without one.
|
|
1550
|
+
"""
|
|
1551
|
+
if cls.mint_grant.__func__ is Worker.mint_grant.__func__: # type: ignore[attr-defined]
|
|
1552
|
+
return None
|
|
1553
|
+
return cls.mint_grant
|
|
1554
|
+
|
|
1481
1555
|
@final
|
|
1482
1556
|
@classmethod
|
|
1483
1557
|
def main(cls) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.0
|
|
4
4
|
Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
|
|
5
5
|
Project-URL: Homepage, https://query.farm
|
|
6
6
|
Project-URL: Repository, https://github.com/Query-farm/vgi-python
|
|
@@ -5,7 +5,7 @@ vgi/aggregate_function.py,sha256=NYxudbvQ9ZLkP2w5hRdd4YgsGUZ_0cVfqqXnlmJPfls,273
|
|
|
5
5
|
vgi/argument_spec.py,sha256=SADH97VlFu3asI8CWZ_I7XTuSxwqFciFgdupdAf-tkY,34231
|
|
6
6
|
vgi/arguments.py,sha256=ncnC1CS7KN-2rDjSw9vdf1uHqV5ozebet_eZtcq_ujw,72300
|
|
7
7
|
vgi/attach_header.py,sha256=sxpzCOVa5fl_-kQSCZgeUxS5I9Kuv8VCUjAOF5s1pr0,3782
|
|
8
|
-
vgi/auth.py,sha256=
|
|
8
|
+
vgi/auth.py,sha256=qMhaLlQfn6F_ovMg4vwD8fl55THalWrSQ9A_V8arGDU,5195
|
|
9
9
|
vgi/cache_control.py,sha256=IyBp9gZTCc4Y-4V7Rs05_o5lSEjbR2jt6cddXPU0dKw,7812
|
|
10
10
|
vgi/copy_from_function.py,sha256=oroQe5qYzZTJ-ppzQTQ-OruEF1ZlwTBNLBoXYfN-P2o,7133
|
|
11
11
|
vgi/copy_to_function.py,sha256=8l0v_DyEF1EnFwinStrZDQ-yNFfGJsv3Mr5o2jG30f0,9279
|
|
@@ -30,13 +30,13 @@ vgi/schema_path.py,sha256=GVlkZlCUj7GbVhgiocSMC6Ff5iTJhKqiYE62hMIEKiI,2403
|
|
|
30
30
|
vgi/schema_utils.py,sha256=zQCliUgIpO2u77Mhtgg3OFvOEDNgJopu6kj2fw8YP1I,7945
|
|
31
31
|
vgi/secret_protocol.py,sha256=iCDl7nF7Nb4cJV4vpfbldunTDqDJWzCzS7uCmOACf7U,7151
|
|
32
32
|
vgi/secret_service.py,sha256=r7qCzfnOMirf9wSCUe-mgFe3z6v9_cDRzixuSAZG_bs,8973
|
|
33
|
-
vgi/serve.py,sha256=
|
|
33
|
+
vgi/serve.py,sha256=DfHSp6B30v_EgWoswsqtmfQ4Fwz1PrnJqvUo1GwM2hA,64416
|
|
34
34
|
vgi/split_token.py,sha256=tutcksj-hgWrLY7UW3sSqkyoaD-cMqNTOeff40MGl_g,10850
|
|
35
35
|
vgi/table_buffering_function.py,sha256=O5TcIdqk8-YViCQCkb8bOevO-zhe3IoyzLgwXDO3Cjc,19531
|
|
36
36
|
vgi/table_filter_pushdown.py,sha256=4Qo4OAOLjTYQ5pinNTOjLCDnb96OHqpcxJlbSJo3cJU,67144
|
|
37
37
|
vgi/table_function.py,sha256=8Pti3uFnsRaPCVG-CGYl9r3GHJF146Fp_62oXXwHKKw,73502
|
|
38
38
|
vgi/table_in_out_function.py,sha256=F2QEtsbGEYHDVyQ4Cf9OCW7HlWXeqekAyx607foB-bs,19393
|
|
39
|
-
vgi/worker.py,sha256=
|
|
39
|
+
vgi/worker.py,sha256=0ez46DE5tc1-78ll29lBP7KRKdDJnkOv89gdr4RjLrE,253453
|
|
40
40
|
vgi/write_results.py,sha256=-OrUcz6E9ahhC_pnKYAwIMR2LpapwQafLC4IOnPTmec,2851
|
|
41
41
|
vgi/_test_fixtures/__init__.py,sha256=xQq-QQLk8USQ6TZHsPiPcZldNNUdcJ2gToXMPGzScLI,444
|
|
42
42
|
vgi/_test_fixtures/attach_options.py,sha256=ZNgNTNF1RymSfnU-uVIDTLvCpc283h6O8tZ1UuEa3Lc,12516
|
|
@@ -152,8 +152,8 @@ vgi/transactor/_duckdb_compat.py,sha256=sXVZ9JLKAQyGR1BjWczSwdQEavtr-TcZPoVZZnTr
|
|
|
152
152
|
vgi/transactor/client.py,sha256=7DTeMksogsw6ANjQjGOPpKYrV76rg4_kGjktMJf54jg,4486
|
|
153
153
|
vgi/transactor/protocol.py,sha256=v59IvrKnuvwOXvn_HEcBAbCzDVcx0akgKN0R1mChXg0,5034
|
|
154
154
|
vgi/transactor/server.py,sha256=nzsZQxJZdgappdYX8okrFIrjFtUbdM_Hhskl1fZ2nDY,32553
|
|
155
|
-
vgi_python-0.
|
|
156
|
-
vgi_python-0.
|
|
157
|
-
vgi_python-0.
|
|
158
|
-
vgi_python-0.
|
|
159
|
-
vgi_python-0.
|
|
155
|
+
vgi_python-0.37.0.dist-info/METADATA,sha256=aYu1DnoElkICwDeYXEoJ_qTIR962ftJnNxkqMvYet5Y,25617
|
|
156
|
+
vgi_python-0.37.0.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
|
|
157
|
+
vgi_python-0.37.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
|
|
158
|
+
vgi_python-0.37.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
|
|
159
|
+
vgi_python-0.37.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|