From 949d1d8e143521d6c4b59e05532400af23261005 Mon Sep 17 00:00:00 2001 From: Olivier 'reivilibre Date: Tue, 17 Jun 2025 22:08:27 +0100 Subject: [PATCH] Update grammar of roles and reserve `idcoop/` prefix --- src/store.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/store.rs b/src/store.rs index 94be615..6fe2a44 100644 --- a/src/store.rs +++ b/src/store.rs @@ -18,6 +18,23 @@ use crate::web::login::{ }; use crate::web::oauth_openid::application_session_access_token::ApplicationSession; +/// Prefix for roles that are reserved by idCoop usage. +pub const IDCOOP_RESERVED_ROLE_PREFIX: &str = "idcoop/"; + +/// Checks if the given role ID is valid. +/// +/// Does NOT check whether the role ID is reserved for use by idCoop or not! +/// +/// Valid role IDs are those whose characters are all: +/// - ASCII alphanumeric; or +/// - `_`; or` +/// - `/`. +pub fn is_valid_role_id(role_id: &str) -> bool { + role_id + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '/') +} + /// Postgres-backed storage for IdCoop /// A connection pool is in use. pub struct IdCoopStore { @@ -460,11 +477,16 @@ impl IdCoopStoreTxn<'_, '_> { /// Creates a role. pub async fn create_role(&mut self, cr: CreateRole) -> eyre::Result<()> { ensure!( - cr.role_id.chars().all(|c| c.is_ascii_alphanumeric()), - "attempted to create role {} with non-alphanum ID", + is_valid_role_id(&cr.role_id), + "attempted to create role {} with ID containing reserved characters", cr.role_id ); + ensure!( + !cr.role_id.starts_with(IDCOOP_RESERVED_ROLE_PREFIX), + "attempted to create a role with the reserved idCoop role prefix: {IDCOOP_RESERVED_ROLE_PREFIX}" + ); + // TODO(prettiness): handle the case where the role already exists // in a nicer way sqlx::query!( @@ -481,6 +503,11 @@ impl IdCoopStoreTxn<'_, '_> { /// Deletes a role. Silently no-ops if the role doesn't exist. pub async fn delete_role(&mut self, role_id: &str) -> eyre::Result<()> { + ensure!( + !role_id.starts_with(IDCOOP_RESERVED_ROLE_PREFIX), + "attempted to delete a role with the reserved idCoop role prefix: {IDCOOP_RESERVED_ROLE_PREFIX}" + ); + sqlx::query!("DELETE FROM roles WHERE role_id = $1", role_id) .execute(&mut **self.txn) .await