Enforce that the identity may access the session's current database. Called at session bind time (first query on this connection). If the resolved `current_database` is not in `identity.accessible_databases`, the connection is rejected with `ACCESS_DENIED` (SQLSTATE 42501) before any query executes. Superusers bypass this check. This is the single enforcement point. Every query path goes through
(
&self,
identity: &AuthenticatedIdentity,
addr: &std::net::SocketAddr,
)
| 162 | /// `do_query` (simple) or `execute` (extended), both of which call this |
| 163 | /// immediately after identity resolution. |
| 164 | pub(super) fn enforce_database_access( |
| 165 | &self, |
| 166 | identity: &AuthenticatedIdentity, |
| 167 | addr: &std::net::SocketAddr, |
| 168 | ) -> PgWireResult<()> { |
| 169 | if identity.is_superuser { |
| 170 | return Ok(()); |
| 171 | } |
| 172 | let db = self |
| 173 | .sessions |
| 174 | .get_current_database(addr) |
| 175 | .unwrap_or(crate::types::DatabaseId::DEFAULT); |
| 176 | if !identity.can_access_database(db) { |
| 177 | let emitter = crate::control::security::audit::ArcAuditEmitter(std::sync::Arc::clone( |
| 178 | &self.state.audit, |
| 179 | )); |
| 180 | emitter.emit( |
| 181 | AuditEvent::PermissionDenied, |
| 182 | &identity.username, |
| 183 | &format!("database access denied: db={}", db.as_u64()), |
| 184 | AuditEmitContext::new( |
| 185 | Some(identity.tenant_id), |
| 186 | &identity.user_id.to_string(), |
| 187 | &identity.username, |
| 188 | ), |
| 189 | ); |
| 190 | return Err(PgWireError::UserError(Box::new(ErrorInfo::new( |
| 191 | "FATAL".to_owned(), |
| 192 | "42501".to_owned(), |
| 193 | format!( |
| 194 | "permission denied for database: user '{}' does not have access", |
| 195 | identity.username |
| 196 | ), |
| 197 | )))); |
| 198 | } |
| 199 | Ok(()) |
| 200 | } |
| 201 | |
| 202 | /// Check if the identity has permission for the given plan. |
| 203 | /// |
no test coverage detected