This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git


The following commit(s) were added to refs/heads/main by this push:
     new e75099e  Remove unused code for adding keys
e75099e is described below

commit e75099e14c9d9501b6f3ca43460a89a8308f59e3
Author: Sean B. Palmer <[email protected]>
AuthorDate: Thu Jul 24 17:18:38 2025 +0100

    Remove unused code for adding keys
---
 atr/db/interaction.py | 327 --------------------------------------------------
 1 file changed, 327 deletions(-)

diff --git a/atr/db/interaction.py b/atr/db/interaction.py
index 1084120..49bb188 100644
--- a/atr/db/interaction.py
+++ b/atr/db/interaction.py
@@ -15,11 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import asyncio
 import contextlib
-import datetime
 import pathlib
-import pprint
 import re
 from collections.abc import AsyncGenerator, Sequence
 
@@ -84,170 +81,6 @@ async def has_failing_checks(release: sql.Release, 
revision_number: str, caller_
         return result.scalar_one() > 0
 
 
-async def key_user_add(
-    session_asf_uid: str | None,
-    public_key: str,
-    selected_committees: list[str],
-    ldap_data: dict[str, str] | None = None,
-    update_existing: bool = False,
-) -> list[dict]:
-    session_asf_uid = session_asf_uid.lower() if session_asf_uid else None
-    if not public_key:
-        raise PublicKeyError("Public key is required")
-
-    # Validate the key using GPG and get its properties
-    # This does not add it to the database, only validates and gets its 
properties
-    keys = await _key_user_add_validate_key_properties(public_key)
-
-    added_keys = []
-    for key in keys:
-        uids = key.get("uids", [])
-        asf_uid = await util.asf_uid_from_uids(uids, ldap_data=ldap_data)
-        test_key_uids = ["Apache Tooling (For test use only) 
<[email protected]>"]
-        is_admin = user.is_admin(session_asf_uid)
-        if (uids == test_key_uids) and is_admin:
-            # Allow the test key
-            # TODO: We should fix the test key, not add an exception for it
-            # But the admin check probably makes this safe enough
-            asf_uid = session_asf_uid
-        elif session_asf_uid and (asf_uid != session_asf_uid):
-            # TODO: Give a more detailed error message about why and what to do
-            raise InteractionError(f"Key {key.get('fingerprint', '').upper()} 
is not associated with your ASF account")
-        async with db.session() as data:
-            # Store the key in the database
-            added = await key_user_session_add(
-                asf_uid, public_key, key, selected_committees, data, 
update_existing=update_existing
-            )
-            if added:
-                added_keys.append(added)
-            else:
-                log.warning(f"Failed to add key {key} to user {asf_uid}")
-    return added_keys
-
-
-async def key_user_session_add(
-    asf_uid: str | None,
-    public_key: str,
-    key: dict,
-    selected_committees: list[str],
-    data: db.Session,
-    update_existing: bool = False,
-) -> dict | None:
-    # TODO: Check if key already exists
-    # psk_statement = 
select(PublicSigningKey).where(PublicSigningKey.apache_uid == session.uid)
-
-    # # If uncommented, this will prevent a user from adding a second key
-    # existing_key = (await db_session.execute(statement)).scalar_one_or_none()
-    # if existing_key:
-    #     return ("You already have a key registered", None)
-
-    fingerprint = key.get("fingerprint")
-    # for subkey in key.get("subkeys", []):
-    #     if subkey[1] == "s":
-    #         # It's a signing key, so use its fingerprint instead
-    #         # TODO: Not sure that we should do this
-    #         # TODO: Check for multiple signing subkeys
-    #         fingerprint = subkey[2]
-    #         break
-    if not isinstance(fingerprint, str):
-        raise RuntimeError("Invalid key fingerprint")
-    fingerprint = fingerprint.lower()
-    uids = key.get("uids", [])
-    key_record: sql.PublicSigningKey | None = None
-
-    latest_self_signature = _key_latest_self_signature(key)
-    created = datetime.datetime.fromtimestamp(int(key["date"]), 
tz=datetime.UTC)
-    expires = datetime.datetime.fromtimestamp(int(key["expires"]), 
tz=datetime.UTC) if key.get("expires") else None
-
-    async with data.begin():
-        existing = await data.public_signing_key(fingerprint=fingerprint).get()
-        # TODO: This can race
-        if existing:
-            update = update_existing
-            # If the new key has a latest self signature
-            if latest_self_signature is not None:
-                # And the self signature is newer, update it
-                if (existing.latest_self_signature is None) or 
(existing.latest_self_signature < latest_self_signature):
-                    update = True
-            if update:
-                existing.fingerprint = fingerprint
-                existing.algorithm = int(key["algo"])
-                existing.length = int(key.get("length", "0"))
-                existing.created = created
-                existing.latest_self_signature = latest_self_signature
-                existing.expires = expires
-                existing.primary_declared_uid = uids[0] if uids else None
-                existing.secondary_declared_uids = uids[1:]
-                existing.apache_uid = asf_uid.lower() if asf_uid else None
-                existing.ascii_armored_key = (
-                    public_key.decode("utf-8", errors="replace") if 
isinstance(public_key, bytes) else public_key
-                )
-                log.info(f"Found existing key {fingerprint.upper()}, updating 
associations")
-            else:
-                log.info(f"Found existing key {fingerprint.upper()}, no update 
needed")
-            key_record = existing
-        else:
-            # Key doesn't exist, create it
-            log.info(f"Adding new key {fingerprint.upper()}")
-
-            key_record = sql.PublicSigningKey(
-                fingerprint=fingerprint,
-                algorithm=int(key["algo"]),
-                length=int(key.get("length", "0")),
-                created=created,
-                latest_self_signature=latest_self_signature,
-                expires=expires,
-                primary_declared_uid=uids[0] if uids else None,
-                secondary_declared_uids=uids[1:],
-                apache_uid=asf_uid.lower() if asf_uid else None,
-                ascii_armored_key=public_key,
-            )
-            data.add(key_record)
-            await data.flush()
-            await data.refresh(key_record)
-
-        # Link key to selected PMCs and track status for each
-        committee_statuses: dict[str, str] = {}
-        for committee_name in selected_committees:
-            committee = await data.committee(name=committee_name).get()
-            if committee and committee.name:
-                # Check whether the link already exists
-                link_exists = await data.execute(
-                    sqlmodel.select(sql.KeyLink).where(
-                        sql.KeyLink.committee_name == committee.name,
-                        sql.KeyLink.key_fingerprint == key_record.fingerprint,
-                    )
-                )
-                if link_exists.scalar_one_or_none() is None:
-                    committee_statuses[committee_name] = "newly_linked"
-                    # Link doesn't exist, create it
-                    log.debug(f"Linking key {fingerprint.upper()} to committee 
{committee_name}")
-                    link = sql.KeyLink(committee_name=committee.name, 
key_fingerprint=key_record.fingerprint)
-                    data.add(link)
-                else:
-                    committee_statuses[committee_name] = "already_linked"
-                    log.debug(f"Link already exists for key 
{fingerprint.upper()} and committee {committee_name}")
-            else:
-                log.warning(f"Could not find committee {committee_name} to 
link key {fingerprint.upper()}")
-                continue
-
-    # TODO: What if there is no email?
-    user_id_str = key_record.primary_declared_uid or ""
-    email = util.email_from_uid(user_id_str) or ""
-
-    return {
-        "key_id": key_record.fingerprint[-16:],
-        "fingerprint": key_record.fingerprint,
-        "user_id": user_id_str,
-        "email": email,
-        "creation_date": key_record.created,
-        "expiration_date": key_record.expires,
-        "data": pprint.pformat(key),
-        "committee_statuses": committee_statuses,
-        "status": "success",
-    }
-
-
 async def latest_revision(release: sql.Release) -> sql.Revision | None:
     if release.latest_revision_number is None:
         return None
@@ -393,38 +226,6 @@ async def unfinished_releases(asfuid: str) -> dict[str, 
list[sql.Release]]:
     return releases
 
 
-async def upload_keys_bytes(
-    user_committees: list[str],
-    keys_bytes: bytes,
-    selected_committees: list[str],
-    ldap_data: dict[str, str] | None = None,
-    update_existing: bool = False,
-) -> tuple[list[dict], int, int, list[str]]:
-    key_blocks = util.parse_key_blocks_bytes(keys_bytes)
-    if not key_blocks:
-        raise InteractionError("No valid OpenPGP keys found in the uploaded 
file")
-
-    # Ensure that the selected committees are ones of which the user is 
actually a member
-    invalid_committees = [committee for committee in selected_committees if 
(committee not in user_committees)]
-    if invalid_committees:
-        raise InteractionError(f"Invalid committee selection: {', 
'.join(invalid_committees)}")
-
-    # TODO: Do we modify this? Store a copy just in case, for the template to 
use
-    submitted_committees = selected_committees[:]
-
-    # Process each key block
-    results = await _upload_process_key_blocks(
-        key_blocks, selected_committees, ldap_data=ldap_data, 
update_existing=update_existing
-    )
-    # if not results:
-    #     raise InteractionError("No keys were added")
-
-    success_count = sum(1 for result in results if result["status"] == 
"success")
-    error_count = len(results) - success_count
-
-    return results, success_count, error_count, submitted_committees
-
-
 # This function cannot go in user.py because it causes a circular import
 async def user_committees_committer(asf_uid: str, caller_data: db.Session | 
None = None) -> Sequence[sql.Committee]:
     async with db.ensure_session(caller_data) as data:
@@ -487,66 +288,6 @@ async def _delete_release_data_filesystem(release_dir: 
pathlib.Path, release_nam
         )
 
 
-def _key_latest_self_signature(key: dict) -> datetime.datetime | None:
-    fingerprint = key["fingerprint"]
-    # TODO: Only 64 bits, which is not at all secure
-    fingerprint_suffix = fingerprint[-16:]
-    sig_lists = [key.get("sigs", [])] + [sub.get("sigs", []) for sub in 
key.get("subkey_info", {}).values()]
-    latest_sig_date = None
-    for sig_list in sig_lists:
-        for sig in sig_list:
-            if sig[0] in {fingerprint_suffix, fingerprint}:
-                if latest_sig_date is None:
-                    latest_sig_date = sig[3]
-                else:
-                    latest_sig_date = max(latest_sig_date, sig[3])
-    return datetime.datetime.fromtimestamp(latest_sig_date, tz=datetime.UTC) 
if latest_sig_date else None
-
-
-async def _key_user_add_validate_key_properties(public_key: str) -> list[dict]:
-    """Validate OpenPGP key string, import it, and return its properties and 
fingerprint."""
-    # import atr.gpgpatch as gpgpatch
-    # gnupg = gpgpatch.patch_gnupg()
-    import gnupg
-
-    def _sig_with_timestamp(self, args):
-        self.curkey["sigs"].append((args[4], args[9], args[10], int(args[5])))
-
-    gnupg.ListKeys.sig = _sig_with_timestamp
-
-    async with ephemeral_gpg_home() as gpg_home:
-        gpg = gnupg.GPG(gnupghome=gpg_home)
-        import_result = await asyncio.to_thread(gpg.import_keys, public_key)
-
-        if not import_result.fingerprints:
-            raise PublicKeyError("Invalid public key format or failed import")
-
-        # List keys to get details
-        keys = await asyncio.to_thread(gpg.list_keys, 
keys=import_result.fingerprints, sigs=True)
-        for key in keys:
-            if key.get("fingerprint") is None:
-                continue
-    if not keys:
-        log.warning(f"No keys found in {public_key}")
-        return []
-
-    # Find the specific key details from the list using the fingerprint
-    results = []
-    for key in keys:
-        if key.get("fingerprint") is None:
-            log.warning(f"Key {key} has no fingerprint")
-            continue
-
-        # Validate key algorithm and length
-        # https://infra.apache.org/release-signing.html#note
-        # Says that keys must be at least 2048 bits
-        if (key.get("algo") == "1") and (int(key.get("length", "0")) < 2048):
-            raise PublicKeyError("RSA Key is not long enough; must be at least 
2048 bits")
-        results.append(key)
-
-    return results
-
-
 async def _successes_errors_warnings(
     data: db.Session, release: sql.Release, latest_revision_number: str, info: 
PathInfo
 ) -> None:
@@ -582,74 +323,6 @@ async def _successes_errors_warnings(
             info.errors.setdefault(pathlib.Path(primary_rel_path), 
[]).append(error)
 
 
-async def _upload_process_key_blocks(
-    key_blocks: list[str],
-    selected_committees: list[str],
-    ldap_data: dict[str, str] | None = None,
-    update_existing: bool = False,
-) -> list[dict]:
-    """Process OpenPGP key blocks and add them to the user's account."""
-    results: list[dict] = []
-
-    # Process each key block
-    for i, key_block in enumerate(key_blocks):
-        try:
-            added_keys = await key_user_add(
-                None, key_block, selected_committees, ldap_data=ldap_data, 
update_existing=update_existing
-            )
-            for key_info in added_keys:
-                key_info["status"] = key_info.get("status", "success")
-                key_info["email"] = key_info.get("email", "Unknown")
-                key_info["committee_statuses"] = 
key_info.get("committee_statuses", {})
-                results.append(key_info)
-            if not added_keys:
-                results.append(
-                    {
-                        "status": "error",
-                        "message": "Failed to process key (key_user_add 
returned None)",
-                        "key_id": f"Key #{i + 1}",
-                        "fingerprint": "Unknown",
-                        "user_id": "Unknown",
-                        "email": "Unknown",
-                        "committee_statuses": {},
-                    }
-                )
-        except (InteractionError, PublicKeyError) as e:
-            results.append(
-                {
-                    "status": "error",
-                    "message": f"Validation Error: {e}",
-                    "key_id": f"Key #{i + 1}",
-                    "fingerprint": "Invalid",
-                    "user_id": "Unknown",
-                    "email": "Unknown",
-                    "committee_statuses": {},
-                }
-            )
-        except Exception as e:
-            log.exception(f"Exception processing key #{i + 1}:")
-            fingerprint, user_id = "Unknown", "None"
-            if isinstance(e, ApacheUserMissingError):
-                fingerprint = e.fingerprint or "Unknown"
-                user_id = e.primary_uid or "None"
-            results.append(
-                {
-                    "status": "error",
-                    "message": f"Internal Exception: {e}",
-                    "key_id": f"Key #{i + 1}",
-                    "fingerprint": fingerprint,
-                    "user_id": user_id,
-                    "email": user_id,
-                    "committee_statuses": {},
-                }
-            )
-
-    # Primary key is email, secondary key is fingerprint
-    results_sorted = sorted(results, key=lambda x: (x.get("email", 
"").lower(), x.get("fingerprint", "")))
-
-    return results_sorted
-
-
 async def releases_by_phase(project: sql.Project, phase: sql.ReleasePhase) -> 
list[sql.Release]:
     """Get the releases for the project by phase."""
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to