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]