|
| 1 | +use std::sync::Arc; |
1 | 2 | use std::time::Duration; |
2 | 3 |
|
| 4 | +use aide::NoApi; |
| 5 | +use axum::extract::State; |
3 | 6 | use axum::{Extension, Json}; |
| 7 | +use hyper::StatusCode; |
| 8 | +use parking_lot::RwLock; |
4 | 9 | use schemars::JsonSchema; |
5 | 10 | use serde::{Deserialize, Serialize}; |
6 | | -use tracing::info; |
7 | 11 |
|
8 | | -use crate::error::Error; |
9 | | -use crate::{elevations, policy}; |
| 12 | +use crate::elevations; |
| 13 | +use crate::policy::Policy; |
10 | 14 |
|
| 15 | +use super::err::HandlerError; |
| 16 | +use super::state::Db; |
11 | 17 | use super::NamedPipeConnectInfo; |
12 | 18 |
|
13 | 19 | #[derive(Deserialize, Serialize, JsonSchema, Debug)] |
14 | 20 | #[serde(rename_all = "PascalCase")] |
15 | 21 | pub(crate) struct ElevateTemporaryPayload { |
| 22 | + /// The number of seconds to elevate the user for. |
| 23 | + /// |
| 24 | + /// This must be between 1 and `i32::MAX`. |
16 | 25 | pub(crate) seconds: u64, |
17 | 26 | } |
18 | 27 |
|
19 | | -pub(crate) async fn post_elevate_temporary( |
20 | | - Extension(named_pipe_info): Extension<NamedPipeConnectInfo>, |
| 28 | +/// Temporarily elevates the user's session. |
| 29 | +pub(crate) async fn elevate_temporary( |
| 30 | + Extension(req_id): Extension<i32>, |
| 31 | + Extension(info): Extension<NamedPipeConnectInfo>, |
| 32 | + NoApi(Db(db)): NoApi<Db>, |
| 33 | + NoApi(State(policy)): NoApi<State<Arc<RwLock<Policy>>>>, |
21 | 34 | Json(payload): Json<ElevateTemporaryPayload>, |
22 | | -) -> Result<(), Error> { |
23 | | - let policy = policy::policy().read(); |
| 35 | +) -> Result<(), HandlerError> { |
| 36 | + // validate input |
| 37 | + fn invalid_secs_err() -> HandlerError { |
| 38 | + HandlerError::new( |
| 39 | + StatusCode::BAD_REQUEST, |
| 40 | + Some("number of seconds must be between 1 and 2,147,483,647"), |
| 41 | + ) |
| 42 | + } |
| 43 | + let seconds = i32::try_from(payload.seconds).map_err(|_| invalid_secs_err())?; |
| 44 | + if seconds < 1 { |
| 45 | + return Err(invalid_secs_err()); |
| 46 | + } |
| 47 | + |
| 48 | + db.insert_elevate_tmp_request(req_id, seconds).await?; |
24 | 49 |
|
25 | | - let profile = policy.user_current_profile(&named_pipe_info.user); |
26 | | - if profile.is_none() { |
27 | | - info!(user = ?named_pipe_info.user, "User tried to elevate temporarily, but wasn't assigned to profile"); |
28 | | - return Err(Error::AccessDenied); |
| 50 | + let policy = policy.read(); |
| 51 | + if policy.user_current_profile(&info.user).is_none() { |
| 52 | + return Err(HandlerError::new( |
| 53 | + StatusCode::FORBIDDEN, |
| 54 | + Some("user not assigned to profile"), |
| 55 | + )); |
29 | 56 | } |
30 | 57 |
|
31 | 58 | let settings = policy |
32 | | - .user_current_profile(&named_pipe_info.user) |
| 59 | + .user_current_profile(&info.user) |
33 | 60 | .map(|p| &p.elevation_settings.temporary) |
34 | | - .ok_or(Error::AccessDenied)?; |
| 61 | + .ok_or_else(|| { |
| 62 | + HandlerError::new( |
| 63 | + StatusCode::FORBIDDEN, |
| 64 | + Some("could not get temporary elevation configuration"), |
| 65 | + ) |
| 66 | + })?; |
35 | 67 |
|
36 | 68 | if !settings.enabled { |
37 | | - info!( |
38 | | - user = ?named_pipe_info.user, |
39 | | - "User tried to elevate temporarily, but wasn't allowed", |
40 | | - ); |
41 | | - return Err(Error::AccessDenied); |
| 69 | + return Err(HandlerError::new( |
| 70 | + StatusCode::FORBIDDEN, |
| 71 | + Some("temporary elevation is not permitted"), |
| 72 | + )); |
42 | 73 | } |
43 | 74 |
|
44 | | - let req_duration = Duration::from_secs(payload.seconds); |
45 | | - |
46 | | - if Duration::from_secs(settings.maximum_seconds) < req_duration { |
47 | | - info!( |
48 | | - user = ?named_pipe_info.user, |
49 | | - seconds = req_duration.as_secs(), |
50 | | - "User tried to elevate temporarily for too long" |
51 | | - ); |
52 | | - return Err(Error::AccessDenied); |
| 75 | + let duration = Duration::from_secs(payload.seconds); |
| 76 | + if Duration::from_secs(settings.maximum_seconds) < duration { |
| 77 | + return Err(HandlerError::new( |
| 78 | + StatusCode::FORBIDDEN, |
| 79 | + Some("requested duration exceeds maximum"), |
| 80 | + )); |
53 | 81 | } |
54 | | - |
55 | | - info!( |
56 | | - user = ?named_pipe_info.user, |
57 | | - seconds = req_duration.as_secs(), |
58 | | - "Elevating user" |
59 | | - ); |
60 | | - |
61 | | - elevations::elevate_temporary(named_pipe_info.user, &req_duration); |
| 82 | + elevations::elevate_temporary(info.user, &duration); |
62 | 83 |
|
63 | 84 | Ok(()) |
64 | 85 | } |
0 commit comments