Skip to content
Open
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
28 changes: 28 additions & 0 deletions UPGRADING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ reverted. This is all fairly informal and loosely defined. Hopefully we won't
have too many entries in this file.


#8059 APATs have non-standard signature
=======================================

Everyone
--------

Truncate the DynamoDB user table in each of your personal deployments by
tainting the Terraform resource and reapplying::

make deploy
cd terraform
terraform apply -replace=aws_dynamodb_table.users

Note that this will invalidate all APATs in that deployment.

Operator
--------

Follow the steps above for all shared deployments. Cancel the
``integration_test`` job of the GitLab pipeline. When the ``deploy`` job has
completed, run the above commands locally, with the target deployment selected
and the merge commit (or, for sandbox deployments, the PR branch) checked out.
The ``make deploy`` should yield an empty plan. We only use it to ensure that
all dependencies (config files, lambda layer, ...) are present before invoking
``terraform``. After the ``terraform apply``, resume the pipeline by starting
the ``integration_test`` job.


#8033 Support per-user APAT revocation
======================================

Expand Down
Binary file removed bin/wheels/runtime/idna-3.16-py3-none-any.whl
Binary file not shown.
Binary file added bin/wheels/runtime/idna-3.17-py3-none-any.whl
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
8 changes: 4 additions & 4 deletions requirements.all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ grpcio-status==1.80.0
http-message-signatures==2.0.1
http_sfv==0.9.9
httplib2==0.31.2
idna==3.16
idna==3.17
inquirer==3.4.1
isort==8.0.1
jinxed==2.0.4
Expand All @@ -68,7 +68,7 @@ mypy-boto3-ecr==1.43.0
mypy-boto3-iam==1.43.2
mypy-boto3-kms==1.43.12
mypy-boto3-lambda==1.43.0
mypy-boto3-opensearch==1.43.7
mypy-boto3-opensearch==1.43.16
mypy-boto3-s3==1.43.14
mypy-boto3-secretsmanager==1.43.0
mypy-boto3-securityhub==1.43.5
Expand Down Expand Up @@ -113,9 +113,9 @@ requirements-parser==0.13.0+6
responses==0.26.0
resumablehash==1.5
rfc3339-validator==0.1.4
rpds-py==0.30.0
rpds-py==2026.5.1
runs==1.3.0
s3transfer==0.17.0
s3transfer==0.17.1
setuptools==82.0.1
six==1.17.0
smmap==5.0.3
Expand Down
2 changes: 1 addition & 1 deletion requirements.dev.trans.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mypy-boto3-ecr==1.43.0
mypy-boto3-iam==1.43.2
mypy-boto3-kms==1.43.12
mypy-boto3-lambda==1.43.0
mypy-boto3-opensearch==1.43.7
mypy-boto3-opensearch==1.43.16
mypy-boto3-s3==1.43.14
mypy-boto3-secretsmanager==1.43.0
mypy-boto3-securityhub==1.43.5
Expand Down
7 changes: 3 additions & 4 deletions requirements.trans.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
certifi==2026.5.20
cffi==2.0.0
charset-normalizer==3.4.7
cryptography==48.0.0
events==0.5
google-cloud-core==2.6.0
google-crc32c==1.8.0
google-resumable-media==2.9.0
googleapis-common-protos==1.75.0
grpcio==1.80.0
grpcio-status==1.80.0
idna==3.16
idna==3.17
jsonschema-specifications==2025.9.1
markupsafe==3.0.3
orderedmultidict==1.0.2
Expand All @@ -21,7 +20,7 @@ pyasn1_modules==0.4.2
pycparser==3.0
pyopenssl==26.2.0
python-dateutil==2.9.0.post0
rpds-py==0.30.0
s3transfer==0.17.0
rpds-py==2026.5.1
s3transfer==0.17.1
six==1.17.0
typing_extensions==4.15.0
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ attrs==26.1.0
boto3==1.43.10 # Match version of the `boto3-stubs` dev dependency
botocore==1.43.10
chevron==0.14.0 # Match with types-chevron in requirements.dev.txt
cryptography==48.0.0
fastavro==1.12.2
furl==2.1.4
google-api-core==2.30.3
Expand Down
60 changes: 59 additions & 1 deletion scripts/pull_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ def main(argv):
choices=['upgrade', 'promotion'],
help='Type of PR to create. '
'If omitted, a regular PR is created.')
parser.add_argument('--no-partial',
action='store_true', default=False,
help='Remove partial label and check partiality tasks.')
parser.add_argument('--no-mirror',
action='store_true', default=False,
help='Remove mirror labels and check mirror tasks.')
parser.add_argument('--no-reindex',
action='store_true', default=False,
help='Remove reindex labels and check reindex tasks.')
fix_group = parser.add_mutually_exclusive_group()
fix_group.add_argument('--fix',
action='store_true', default=None,
Expand All @@ -67,6 +76,12 @@ def main(argv):
args = parser.parse_args(argv)
if args.type is not None and args.fix is not None:
parser.error('--fix/--no-fix cannot be used with --type')
if args.type is not None and args.no_partial:
parser.error('--no-partial cannot be used with --type')
if args.type is not None and args.no_mirror:
parser.error('--no-mirror cannot be used with --type')
if args.type is not None and args.no_reindex:
parser.error('--no-reindex cannot be used with --type')

branch = _current_branch()
_check_working_copy()
Expand Down Expand Up @@ -133,10 +148,30 @@ def main(argv):
log.warning('Target branch is %r, expected %r', base, target_branch)
body = _check_task(body, target_branch_task, checked=False)

if args.no_partial:
assert not _has_commit_tag(target_branch, 'p'), R(
'--no-partial cannot be used when a commit is tagged `p`')
body = _check_task(body, r'Added `p` tag to titles of partial commits.*')
body = _check_task(body, r'This PR is labeled `partial`.*')
body = _check_task(body, r'This PR partially resolves .*')

has_u_tag = _has_commit_tag(target_branch, 'u')
body = _check_task(body, r'Added `u` tag to commit title.*', checked=has_u_tag)
body = _check_task(body, r'This PR is labeled `upgrade`.*', checked=has_u_tag)

if args.no_reindex:
assert not _has_commit_tag(target_branch, 'r'), R(
'--no-reindex cannot be used when a commit is tagged `r`')
body = _check_task(body, r'Added `r` tag to commit title.*')
reindex_labels = _reindex_labels(body)
for label in reindex_labels:
body = _check_task(body, r'This PR is labeled `' + re.escape(label) + '`.*')

if args.no_mirror:
mirror_labels = _mirror_labels(body)
for label in mirror_labels:
body = _check_task(body, r'This PR is labeled `' + re.escape(label) + '`.*')

body = _check_task(body, 'PR is assigned to the author')
body = _check_task(body, r'Status of PR is \*In progress\*')
body = _check_task(body, 'Name of PR branch matches .*')
Expand Down Expand Up @@ -183,6 +218,17 @@ def main(argv):

_label(pr_url, 'upgrade', mode='add' if has_u_tag else 'remove')

if args.no_partial:
_label(pr_url, 'partial', mode='remove')

if args.no_reindex:
for label in reindex_labels:
_label(pr_url, label, mode='remove')

if args.no_mirror:
for label in mirror_labels:
_label(pr_url, label, mode='remove')

log.info('Setting PR status …')
pr_node_id = _node_id(pr_url)
_set_status(pr_node_id, 'In Progress')
Expand Down Expand Up @@ -353,6 +399,16 @@ def _reference_issue_in_body(body: str, issue_number: int) -> str:
return body


def _reindex_labels(body: str) -> list[str]:
return re.findall(r'^- \[[ x]] This PR is labeled `(reindex:\w+)`',
body, flags=re.MULTILINE)


def _mirror_labels(body: str) -> list[str]:
return re.findall(r'^- \[[ x]] This PR is labeled `(mirror:\w+)`',
body, flags=re.MULTILINE)


def _check_task(body: str, task: str, checked: bool = True) -> str:
mark = 'x' if checked else ' '
body, n = re.subn(r'^- \[[ x]] (' + task + ')$',
Expand Down Expand Up @@ -399,7 +455,9 @@ def _node_id(url: str) -> str:
def _label(item_url: str, label: str, *, mode: LabelMode) -> None:
assert check_type(LabelMode, mode)
item_type = _gh_item_type(item_url)
log.info('%s label %r to %r …', mode.title() + 'ing', label, item_url)
verb = {'add': 'Adding', 'remove': 'Removing'}[mode]
preposition = {'add': 'to', 'remove': 'from'}[mode]
log.info('%s label %r %s %r …', verb, label, preposition, item_url)
subprocess.run(
['gh', item_type, 'edit', item_url, f'--{mode}-label', label],
capture_output=True, text=True
Expand Down
25 changes: 23 additions & 2 deletions src/azul/service/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
)

import attrs
from cryptography.hazmat.primitives.asymmetric.ec import (
SECP256R1,
)
from cryptography.hazmat.primitives.asymmetric.utils import (
decode_dss_signature,
encode_dss_signature,
)
import jwt.algorithms
from jwt.api_jwt import (
PyJWT,
Expand Down Expand Up @@ -498,23 +505,37 @@ def from_jwk(jwk):
def to_jwk(key, as_dict=False):
raise NotImplementedError

# ES256 uses the P-256 curve (RFC 7518 Section 3.4), a.k.a. SECP256R1
_sig_component_size = SECP256R1.key_size // 8

def sign(self, msg: bytes, key: str) -> bytes:
log.debug('Signing %d bytes with key %r', len(msg), key)
response = aws.kms.sign(KeyId=key,
Message=msg,
MessageType='RAW',
SigningAlgorithm='ECDSA_SHA_256')
return response['Signature']
# KMS returns DER-encoded signatures, but JWS requires raw r||s
return self._der_to_raw(response['Signature'])

def verify(self, msg: bytes, key: str, sig: bytes) -> bool:
log.debug('Verifying %d bytes with key %r', len(msg), key)
# KMS expects DER-encoded signatures, but JWS uses raw r||s
try:
response = aws.kms.verify(KeyId=key,
Message=msg,
MessageType='RAW',
Signature=sig,
Signature=self._raw_to_der(sig),
SigningAlgorithm='ECDSA_SHA_256')
except aws.kms.exceptions.KMSInvalidSignatureException:
return False
else:
return response['SignatureValid']

def _der_to_raw(self, der_sig: bytes) -> bytes:
r, s = decode_dss_signature(der_sig)
return r.to_bytes(self._sig_component_size, 'big') + s.to_bytes(self._sig_component_size, 'big')

def _raw_to_der(self, raw_sig: bytes) -> bytes:
r = int.from_bytes(raw_sig[:self._sig_component_size], 'big')
s = int.from_bytes(raw_sig[self._sig_component_size:], 'big')
return encode_dss_signature(r, s)
Loading