Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ modules =
azul.service.user_service,
scripts.pull_request,
scripts.claude_mv,
service.test_user_controller,


packages =
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 7 additions & 5 deletions deployments/prod/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
source('bigquery', 'datarepo-bc83ab27', 'hca_prod_a2a2f324cf24409ea859deaee871269c__20220330_dcp2_20220607_dcp17'),
source('bigquery', 'datarepo-10a33a05', 'hca_prod_a62dae2ecd694d5cb5f84f7e8abdbafa__20220606_dcp2_20220607_dcp17'),
source('bigquery', 'datarepo-e3d0317e', 'hca_prod_a9f5323ace71471c9caf04cc118fd1d7__20220606_dcp2_20220607_dcp17'),
source('bigquery', 'datarepo-cd2ab73f', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20220607_dcp17'),
source('bigquery', 'datarepo-cd2ab73f', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20220607_dcp17', no_ma_mirror), # noqa: E501
source('bigquery', 'datarepo-dcd2f9cf', 'hca_prod_aff9c3cd6b844fc2abf2b9c0b3038277__20220330_dcp2_20220607_dcp17'),
source('bigquery', 'datarepo-c9b6cc1c', 'hca_prod_b9484e4edc404e389b854cecf5b8c068__20220118_dcp2_20220607_dcp17'),
source('bigquery', 'datarepo-49083689', 'hca_prod_bd7104c9a950490e94727d41c6b11c62__20220118_dcp2_20220607_dcp17'),
Expand Down Expand Up @@ -700,7 +700,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
source('bigquery', 'datarepo-d962a1b4', 'hca_prod_a991ef154d4a4b80a93ec538b4b54127__20220118_dcp2_20230314_dcp25'),
source('bigquery', 'datarepo-60416b5f', 'hca_prod_a9f5323ace71471c9caf04cc118fd1d7__20220606_dcp2_20230314_dcp25'),
source('bigquery', 'datarepo-f2f57a7c', 'hca_prod_ac289b77fb124a6bad43c0721c698e70__20220906_dcp2_20230314_dcp25'),
source('bigquery', 'datarepo-bbe8303d', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20230314_dcp25'),
source('bigquery', 'datarepo-bbe8303d', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20230314_dcp25', no_ma_mirror), # noqa: E501
source('bigquery', 'datarepo-d67d5486', 'hca_prod_ae62bb3155ca4127b0fbb1771a604645__20230313_dcp2_20230314_dcp25'),
source('bigquery', 'datarepo-2a19065b', 'hca_prod_aefb919243fc46d7a4c129597f7ef61b__20220330_dcp2_20230314_dcp25'),
source('bigquery', 'datarepo-ed809ac3', 'hca_prod_b7259878436c4274bfffca76f4cb7892__20220118_dcp2_20230314_dcp25'),
Expand Down Expand Up @@ -1077,6 +1077,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
]))

dcp40_sources = union(dcp39_sources, 458, delta([
# @formatter:off
source('bigquery', 'datarepo-7ff6ae27', 'hca_prod_005d611a14d54fbf846e571a1f874f70__20220111_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-083a593d', 'hca_prod_027c51c60719469fa7f5640fe57cbece__20220110_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-6e878a15', 'hca_prod_065e6c13ad6b46a38075c3137eb03068__20220213_dcp2_20240711_dcp40'),
Expand All @@ -1099,7 +1100,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
source('bigquery', 'datarepo-1a5200cb', 'hca_prod_86fd2521c5014e41841c06d79277bb7c__20240708_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-436c5a47', 'hca_prod_99101928d9b14aafb759e97958ac7403__20220118_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-e10ecf5f', 'hca_prod_a83b7f45bfb14c6a97e98e3370065cc1__20240708_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-028b06ac', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-028b06ac', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20240711_dcp40', no_ma_mirror), # noqa: E501
source('bigquery', 'datarepo-7c60076d', 'hca_prod_ae71be1dddd84feb9bed24c3ddb6e1ad__20220118_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-27cbfba4', 'hca_prod_b963bd4b4bc14404842569d74bc636b8__20220118_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-7345f02d', 'hca_prod_c16a754f5da346ed8c1e6426af2ef625__20220519_dcp2_20240711_dcp40'),
Expand All @@ -1111,6 +1112,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
source('bigquery', 'datarepo-812cbdeb', 'hca_prod_cddab57b68684be4806f395ed9dd635a__20220118_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-d8cb1e24', 'hca_prod_d3446f0c30f34a12b7c36af877c7bb2d__20220119_dcp2_20240711_dcp40'),
source('bigquery', 'datarepo-bde87024', 'hca_prod_dc0b65b0771346f0a3390b03ea786046__20230427_dcp2_20240711_dcp40')
# @formatter:on
]))

dcp41_sources = union(dcp40_sources, 462, delta([
Expand Down Expand Up @@ -1251,7 +1253,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
source('bigquery', 'datarepo-47e132f5', 'hca_prod_9bef1e81e5d94ece81cbab7449232021__20241104_dcp2_20241107_dcp44'),
source('bigquery', 'datarepo-8e2c06ed', 'hca_prod_9dd91b6e7c6249d3a3d474f603deffdb__20240903_dcp2_20241107_dcp44'),
source('bigquery', 'datarepo-b45fddfb', 'hca_prod_a4f154f85cc940b5b8d7af90afce8a8f__20230526_dcp2_20241107_dcp44'),
source('bigquery', 'datarepo-18b2a11c', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20241107_dcp44'),
source('bigquery', 'datarepo-18b2a11c', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20241107_dcp44', no_ma_mirror), # noqa: E501
source('bigquery', 'datarepo-950c161b', 'hca_prod_ae62bb3155ca4127b0fbb1771a604645__20230313_dcp2_20241107_dcp44'),
source('bigquery', 'datarepo-ca6f869a', 'hca_prod_ae71be1dddd84feb9bed24c3ddb6e1ad__20220118_dcp2_20241107_dcp44'),
source('bigquery', 'datarepo-168f955e', 'hca_prod_aebc99a33151482a9709da6802617763__20240201_dcp2_20241107_dcp44'),
Expand Down Expand Up @@ -1784,7 +1786,7 @@ def union(previous_catalog: dict[DatasetName, SourceItem | None],
dcp58_sources = union(dcp57_sources, 530, delta([
# @formatter:off
source('bigquery', 'datarepo-74756a12', 'hca_prod_984ce0a2682d47a3b80e1354dfe51ca3__20260304_dcp2_20260304_dcp58'),
source('bigquery', 'datarepo-90320986', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20260304_dcp58'),
source('bigquery', 'datarepo-90320986', 'hca_prod_ad04c8e79b7d4cceb8e901e31da10b94__20220118_dcp2_20260304_dcp58', no_ma_mirror), # noqa: E501
source('bigquery', 'datarepo-04e4ece0', 'hca_prod_c8503de8d02d4bdaad064c851b37fa97__20260304_dcp2_20260304_dcp58', no_ma_mirror), # noqa: E501
# @formatter:on
]))
Expand Down
22 changes: 6 additions & 16 deletions environment
Original file line number Diff line number Diff line change
Expand Up @@ -386,36 +386,26 @@ _hibernate() {
# Launch Claude Code in a loop, reloading the environment between invocations.
# Each invocation will pick up where the previous one left off.
#
# Usage: _claude [session_id] [claude_args...]
# Usage: _claude [session_id]
#
# To exit the loop, exit Claude and then hit any key within 3 seconds.
#
_claude() {
local session_id resume
local session_id flag
if [ -n "$1" ] ; then
session_id="$1"
shift
resume=true
flag=--resume
else
session_id="$(python -c 'import uuid; print(uuid.uuid4())')"
resume=false
flag=--session-id
fi
while true ; do
local flag
if $resume ; then
flag=--resume
else
flag=--session-id
resume=true
fi
if ! claude "$flag" "$session_id" "$@" ; then
return 1
fi
while claude "$flag" "$session_id" ; do
echo >&2 'Reloading environment in 3 seconds. Press any key to exit …'
if read -r -t 3 -n 1 ; then
break
fi
_refresh
flag=--resume
done
}

Expand Down
4 changes: 2 additions & 2 deletions lambdas/service/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -13948,7 +13948,7 @@
}
},
"summary": "Obtain an OAuth 2.0 access token in exchange for an authorization code",
"description": "\nInvoke this endpoint as part of the authorization code flow\nfrom a single-page app.\n\nNote that while this endpoint is part of the authorization\ncode flow, which typically yields a refresh token along with\nthe access token, this endpoint returns only the access\ntoken. Doing so is an aspect of the commonly adopted\nsecurity best practice known as [Backend For Frontend](\nhttps://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#section-6.1).\n\n**When initiating the authorization code flow, be\nsure to request the `openid` scope.**\n",
"description": "\nInvoke this endpoint as part of the authorization code flow\nfrom a single-page app.\n\nNote that while this endpoint is part of the authorization\ncode flow, which typically yields a refresh token along with\nthe access token, this endpoint returns only the access\ntoken. Doing so is an aspect of the commonly adopted\nsecurity best practice known as [Backend For Frontend](\nhttps://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#section-6.1).\n\n**When initiating the authorization code flow, be\nsure to request the `email` and `openid` scopes.**\n",
"requestBody": {
"description": "\nJSON conforming to the Google Sign-In [CodeResponse](\nhttps://developers.google.com/identity/oauth2/web/reference/js-reference#CodeResponse),\nexcept for the error-related parts. Those should be\nhandled client-side.\n",
"required": true,
Expand All @@ -13963,7 +13963,7 @@
},
"scope": {
"type": "string",
"description": "\nA space-delimited list of scopes that are\napproved by the user. Must contain `openid`.\n"
"description": "\nA space-delimited list of scopes that are\napproved by the user. Must contain\n`email` and `openid`.\n"
},
"state": {
"type": "string",
Expand Down
10 changes: 5 additions & 5 deletions requirements.all.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
atomicwrites==1.4.1
attrs==26.1.0
aws-requests-auth==0.4.3
blessed==1.38.0
blessed==1.39.0
boto3==1.42.97
boto3-stubs-lite==1.42.97
botocore==1.42.97
Expand All @@ -13,7 +13,7 @@ charset-normalizer==3.4.7
chevron==0.14.0
click==8.3.3
coverage==7.13.5
cryptography==47.0.0
cryptography==48.0.0
docker==7.1.0
editor==1.7.0
et_xmlfile==2.0.0
Expand Down Expand Up @@ -52,7 +52,7 @@ jsonschema==4.26.0
jsonschema-path==0.3.4
jsonschema-specifications==2025.9.1
lazy-object-proxy==1.12.0
librt==0.9.0
librt==0.10.0
markupsafe==3.0.3
mccabe==0.7.0
more-itertools==11.0.2
Expand Down Expand Up @@ -99,7 +99,7 @@ pyflakes==3.4.0
pygithub==2.9.1
pyjwt==2.12.1
pynacl==1.6.2
pyopenssl==26.1.0
pyopenssl==26.2.0
pyparsing==3.3.2
python-dateutil==2.9.0.post0
python-dxf==12.1.1
Expand Down Expand Up @@ -129,7 +129,7 @@ typing_extensions==4.15.0
uritemplate==4.2.0
urllib3==2.6.3
watchdog==6.0.0
wcwidth==0.6.0
wcwidth==0.7.0
werkzeug==3.1.8
wheel==0.46.3
www-authenticate==0.9.2
Expand Down
7 changes: 3 additions & 4 deletions requirements.dev.trans.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
blessed==1.38.0
blessed==1.39.0
botocore-stubs==1.42.41
click==8.3.3
editor==1.7.0
Expand All @@ -12,7 +12,7 @@ inquirer==3.4.1
jinja2==3.1.6
jsonschema-path==0.3.4
lazy-object-proxy==1.12.0
librt==0.9.0
librt==0.10.0
mccabe==0.7.0
mypy-boto3-apigateway==1.42.68
mypy-boto3-cloudwatch==1.42.95
Expand All @@ -38,7 +38,6 @@ pathspec==1.1.1
py-partiql-parser==0.6.3
pycodestyle==2.14.0
pyflakes==3.4.0
pyjwt==2.12.1
pynacl==1.6.2
pyparsing==3.3.2
readchar==4.2.2
Expand All @@ -50,7 +49,7 @@ tqdm==4.67.3
types-awscrt==0.31.3
types-s3transfer==0.16.0
uritemplate==4.2.0
wcwidth==0.6.0
wcwidth==0.7.0
www-authenticate==0.9.2
xmltodict==1.0.4
xmod==1.9.0
Expand Down
4 changes: 2 additions & 2 deletions requirements.trans.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
certifi==2026.4.22
cffi==2.0.0
charset-normalizer==3.4.7
cryptography==47.0.0
cryptography==48.0.0
events==0.5
google-cloud-core==2.5.1
google-crc32c==1.8.0
Expand All @@ -19,7 +19,7 @@ protobuf==6.33.6
pyasn1==0.6.3
pyasn1_modules==0.4.2
pycparser==3.0
pyopenssl==26.1.0
pyopenssl==26.2.0
python-dateutil==2.9.0.post0
rpds-py==0.30.0
s3transfer==0.16.1
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jsonschema==4.26.0
more-itertools==11.0.2
msgpack==1.1.2 # versioned independently from the type stub dependency but when updating one, the other should be updated, too, if posssible
opensearch-py==2.8.0 # < 3 to match server
pyjwt==2.12.1
referencing==0.36.2 # < 0.37.0, see https://github.com/DataBiosphere/azul/issues/7832
requests==2.33.1
resumablehash==1.5 # match version with --find-links above
Expand Down
8 changes: 4 additions & 4 deletions src/azul/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1605,14 +1605,14 @@ def gitlab_data_volume_id(self) -> str | None:
def lambda_layer_key(self) -> str:
return 'lambda_layers'

@property
def dynamo_object_version_table_name(self) -> str:
return self.qualified_resource_name('object_versions')

@property
def dynamo_sources_cache_table_name(self) -> str:
return self.qualified_resource_name('sources_cache_by_auth')

@property
def dynamo_users_table_name(self) -> str:
return self.qualified_resource_name('users')

@property
def current_sources(self) -> list[str] | None:
try:
Expand Down
4 changes: 2 additions & 2 deletions src/azul/service/lambda_iam_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@
'Resource': [
f'arn:aws:dynamodb:{aws.region_name}:{aws.account}:table/{table_name}'
for table_name in (
config.dynamo_object_version_table_name,
config.dynamo_sources_cache_table_name
config.dynamo_sources_cache_table_name,
config.dynamo_users_table_name
)
]
},
Expand Down
17 changes: 13 additions & 4 deletions src/azul/service/user_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
cached_property,
)
from azul.lib.strings import (
back_quote,
format_and_dedent as fd,
join_grammatically,
)
from azul.lib.types import (
JSON,
Expand Down Expand Up @@ -70,8 +72,8 @@ def handlers(self) -> dict[str, Any]:
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#section-6.1).

**When initiating the authorization code flow, be
sure to request the `openid` scope.**
'''),
sure to request the {required_scopes} scopes.**
''', required_scopes=self._required_scopes),
'requestBody': {
'description': fd('''
JSON conforming to the Google Sign-In [CodeResponse](
Expand All @@ -88,8 +90,9 @@ def handlers(self) -> dict[str, Any]:
''')),
scope=describe(str, fd('''
A space-delimited list of scopes that are
approved by the user. Must contain `openid`.
''')),
approved by the user. Must contain
{required_scopes}.
''', required_scopes=self._required_scopes)),
state=optional(describe(str, fd('''
The string value that your application uses to
maintain state between your authorization
Expand Down Expand Up @@ -133,6 +136,12 @@ def authorize():

return locals()

@cached_property
def _required_scopes(self) -> str:
scopes = sorted(self._service.required_scopes)
scopes = list(map(back_quote, scopes))
return join_grammatically(scopes)

def _authorize(self) -> JSON:
try:
request: JSON = json_mapping(self.current_request.json_body)
Expand Down
Loading
Loading