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-atr-experiments.git


The following commit(s) were added to refs/heads/main by this push:
     new 78e8ecc  Allow users to associate keys with specific PMCs
78e8ecc is described below

commit 78e8ecc37e9276782e1a781a2d4707f8ba139b44
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Feb 18 17:27:58 2025 +0200

    Allow users to associate keys with specific PMCs
---
 atr/routes.py                    | 46 ++++++++++++++++++++++++++-----
 atr/templates/user-keys-add.html | 58 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 7 deletions(-)

diff --git a/atr/routes.py b/atr/routes.py
index 25fca27..7c00d5a 100644
--- a/atr/routes.py
+++ b/atr/routes.py
@@ -351,7 +351,8 @@ async def root_user_keys_add() -> str:
 
     # Get all existing keys for the user
     async with get_session() as db_session:
-        statement = select(PublicSigningKey).where(PublicSigningKey.apache_uid 
== session.uid)
+        pmcs_loader = selectinload(cast(InstrumentedAttribute[list[PMC]], 
PublicSigningKey.pmcs))
+        statement = 
select(PublicSigningKey).options(pmcs_loader).where(PublicSigningKey.apache_uid 
== session.uid)
         user_keys = (await db_session.execute(statement)).scalars().all()
 
     if request.method == "POST":
@@ -360,7 +361,34 @@ async def root_user_keys_add() -> str:
         if not public_key:
             # Shouldn't happen, so we can raise an exception
             raise ASFQuartException("Public key is required", errorcode=400)
-        error, key_info = await user_keys_add(session, public_key)
+
+        # Get selected PMCs from form
+        selected_pmcs = form.getlist("selected_pmcs")
+        if not selected_pmcs:
+            return await render_template(
+                "user-keys-add.html",
+                asf_id=session.uid,
+                pmc_memberships=session.committees,
+                error="You must select at least one PMC",
+                key_info=None,
+                user_keys=user_keys,
+                algorithms=algorithms,
+            )
+
+        # Ensure that the selected PMCs are ones of which the user is actually 
a member
+        invalid_pmcs = [pmc for pmc in selected_pmcs if pmc not in 
session.committees]
+        if invalid_pmcs:
+            return await render_template(
+                "user-keys-add.html",
+                asf_id=session.uid,
+                pmc_memberships=session.committees,
+                error=f"Invalid PMC selection: {', '.join(invalid_pmcs)}",
+                key_info=None,
+                user_keys=user_keys,
+                algorithms=algorithms,
+            )
+
+        error, key_info = await user_keys_add(session, public_key, 
selected_pmcs)
 
     return await render_template(
         "user-keys-add.html",
@@ -467,7 +495,7 @@ async def save_file_by_hash(base_dir: Path, file: 
FileStorage) -> str:
         raise e
 
 
-async def user_keys_add(session: ClientSession, public_key: str) -> tuple[str, 
dict | None]:
+async def user_keys_add(session: ClientSession, public_key: str, 
selected_pmcs: list[str]) -> tuple[str, dict | None]:
     if not public_key:
         return ("Public key is required", None)
 
@@ -500,11 +528,15 @@ async def user_keys_add(session: ClientSession, 
public_key: str) -> tuple[str, d
 
     # Store key in database
     async with get_session() as db_session:
-        return await user_keys_add_session(session, public_key, key, 
db_session)
+        return await user_keys_add_session(session, public_key, key, 
selected_pmcs, db_session)
 
 
 async def user_keys_add_session(
-    session: ClientSession, public_key: str, key: dict, db_session: 
AsyncSession
+    session: ClientSession,
+    public_key: str,
+    key: dict,
+    selected_pmcs: list[str],
+    db_session: AsyncSession,
 ) -> tuple[str, dict | None]:
     # Check if key already exists
     statement = select(PublicSigningKey).where(PublicSigningKey.apache_uid == 
session.uid)
@@ -532,8 +564,8 @@ async def user_keys_add_session(
         )
         db_session.add(key_record)
 
-        # Link key to user's PMCs
-        for pmc_name in session.committees:
+        # Link key to selected PMCs
+        for pmc_name in selected_pmcs:
             statement = select(PMC).where(PMC.project_name == pmc_name)
             pmc = (await db_session.execute(statement)).scalar_one_or_none()
             if pmc and pmc.id:
diff --git a/atr/templates/user-keys-add.html b/atr/templates/user-keys-add.html
index bf78133..1ad838c 100644
--- a/atr/templates/user-keys-add.html
+++ b/atr/templates/user-keys-add.html
@@ -116,6 +116,31 @@
       .delete-button:hover {
           background: #c82333;
       }
+
+      .pmc-checkboxes {
+          display: grid;
+          grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+          gap: 0.5rem;
+          margin-top: 0.5rem;
+          margin-bottom: 0.5rem;
+      }
+
+      .checkbox-item {
+          display: flex;
+          align-items: center;
+          gap: 0.5rem;
+      }
+
+      .checkbox-item label {
+          border-bottom: none;
+          margin-bottom: 0;
+          font-weight: normal;
+      }
+
+      .checkbox-item input[type="checkbox"] {
+          width: 1rem;
+          height: 1rem;
+      }
   </style>
 {% endblock stylesheets %}
 
@@ -204,6 +229,16 @@
                   <th>User ID</th>
                   <td>{{ key.declared_uid or 'Not specified' }}</td>
                 </tr>
+                <tr>
+                  <th>Associated PMCs</th>
+                  <td>
+                    {% if key.pmcs %}
+                      {{ key.pmcs|map(attribute='project_name') |join(', ') }}
+                    {% else %}
+                      No PMCs associated
+                    {% endif %}
+                  </td>
+                </tr>
               </tbody>
             </table>
           </div>
@@ -225,6 +260,29 @@
       </small>
     </div>
 
+    {% if pmc_memberships %}
+      <div class="form-group">
+        <label>Associate with PMCs:</label>
+        <div class="pmc-checkboxes">
+          {% for pmc in pmc_memberships %}
+            <div class="checkbox-item">
+              <input type="checkbox"
+                     id="pmc_{{ pmc }}"
+                     name="selected_pmcs"
+                     value="{{ pmc }}"
+                     required />
+              <label for="pmc_{{ pmc }}">{{ pmc }}</label>
+            </div>
+          {% endfor %}
+        </div>
+        <small>You must associate your key with at least one PMC of which you 
are a member.</small>
+      </div>
+    {% else %}
+      <div class="error-message">
+        <p>You must be a member of at least one PMC to add a signing key.</p>
+      </div>
+    {% endif %}
+
     <button type="submit">Add Key</button>
   </form>
 


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

Reply via email to