[pbs-devel] [PATCH v2 proxmox-backup 08/20] server/rest: extract auth to seperate module
Stefan Reiter
s.reiter at proxmox.com
Wed Mar 24 16:18:15 CET 2021
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
src/server.rs | 2 +
src/server/auth.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++
src/server/rest.rs | 93 +----------------------------------------
3 files changed, 105 insertions(+), 92 deletions(-)
create mode 100644 src/server/auth.rs
diff --git a/src/server.rs b/src/server.rs
index 7c159c23..b6a37b92 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -89,3 +89,5 @@ mod report;
pub use report::*;
pub mod ticket;
+
+pub mod auth;
diff --git a/src/server/auth.rs b/src/server/auth.rs
new file mode 100644
index 00000000..24151886
--- /dev/null
+++ b/src/server/auth.rs
@@ -0,0 +1,102 @@
+//! Provides authentication primitives for the HTTP server
+use anyhow::{bail, format_err, Error};
+
+use crate::tools::ticket::Ticket;
+use crate::auth_helpers::*;
+use crate::tools;
+use crate::config::cached_user_info::CachedUserInfo;
+use crate::api2::types::{Authid, Userid};
+
+use hyper::header;
+use percent_encoding::percent_decode_str;
+
+pub struct UserAuthData {
+ ticket: String,
+ csrf_token: Option<String>,
+}
+
+pub enum AuthData {
+ User(UserAuthData),
+ ApiToken(String),
+}
+
+pub fn extract_auth_data(headers: &http::HeaderMap) -> Option<AuthData> {
+ if let Some(raw_cookie) = headers.get(header::COOKIE) {
+ if let Ok(cookie) = raw_cookie.to_str() {
+ if let Some(ticket) = tools::extract_cookie(cookie, "PBSAuthCookie") {
+ let csrf_token = match headers.get("CSRFPreventionToken").map(|v| v.to_str()) {
+ Some(Ok(v)) => Some(v.to_owned()),
+ _ => None,
+ };
+ return Some(AuthData::User(UserAuthData {
+ ticket,
+ csrf_token,
+ }));
+ }
+ }
+ }
+
+ match headers.get(header::AUTHORIZATION).map(|v| v.to_str()) {
+ Some(Ok(v)) => {
+ if v.starts_with("PBSAPIToken ") || v.starts_with("PBSAPIToken=") {
+ Some(AuthData::ApiToken(v["PBSAPIToken ".len()..].to_owned()))
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
+
+pub fn check_auth(
+ method: &hyper::Method,
+ auth_data: &AuthData,
+ user_info: &CachedUserInfo,
+) -> Result<Authid, Error> {
+ match auth_data {
+ AuthData::User(user_auth_data) => {
+ let ticket = user_auth_data.ticket.clone();
+ let ticket_lifetime = tools::ticket::TICKET_LIFETIME;
+
+ let userid: Userid = Ticket::<super::ticket::ApiTicket>::parse(&ticket)?
+ .verify_with_time_frame(public_auth_key(), "PBS", None, -300..ticket_lifetime)?
+ .require_full()?;
+
+ let auth_id = Authid::from(userid.clone());
+ if !user_info.is_active_auth_id(&auth_id) {
+ bail!("user account disabled or expired.");
+ }
+
+ if method != hyper::Method::GET {
+ if let Some(csrf_token) = &user_auth_data.csrf_token {
+ verify_csrf_prevention_token(csrf_secret(), &userid, &csrf_token, -300, ticket_lifetime)?;
+ } else {
+ bail!("missing CSRF prevention token");
+ }
+ }
+
+ Ok(auth_id)
+ },
+ AuthData::ApiToken(api_token) => {
+ let mut parts = api_token.splitn(2, ':');
+ let tokenid = parts.next()
+ .ok_or_else(|| format_err!("failed to split API token header"))?;
+ let tokenid: Authid = tokenid.parse()?;
+
+ if !user_info.is_active_auth_id(&tokenid) {
+ bail!("user account or token disabled or expired.");
+ }
+
+ let tokensecret = parts.next()
+ .ok_or_else(|| format_err!("failed to split API token header"))?;
+ let tokensecret = percent_decode_str(tokensecret)
+ .decode_utf8()
+ .map_err(|_| format_err!("failed to decode API token header"))?;
+
+ crate::config::token_shadow::verify_secret(&tokenid, &tokensecret)?;
+
+ Ok(tokenid)
+ }
+ }
+}
+
diff --git a/src/server/rest.rs b/src/server/rest.rs
index 44e157fb..2169766a 100644
--- a/src/server/rest.rs
+++ b/src/server/rest.rs
@@ -17,7 +17,6 @@ use lazy_static::lazy_static;
use serde_json::{json, Value};
use tokio::fs::File;
use tokio::time::Instant;
-use percent_encoding::percent_decode_str;
use url::form_urlencoded;
use regex::Regex;
@@ -42,12 +41,12 @@ use proxmox::api::schema::{
use super::environment::RestEnvironment;
use super::formatter::*;
use super::ApiConfig;
+use super::auth::{check_auth, extract_auth_data};
use crate::auth_helpers::*;
use crate::api2::types::{Authid, Userid};
use crate::tools;
use crate::tools::FileLogger;
-use crate::tools::ticket::Ticket;
use crate::config::cached_user_info::CachedUserInfo;
extern "C" { fn tzset(); }
@@ -574,96 +573,6 @@ fn extract_lang_header(headers: &http::HeaderMap) -> Option<String> {
None
}
-struct UserAuthData{
- ticket: String,
- csrf_token: Option<String>,
-}
-
-enum AuthData {
- User(UserAuthData),
- ApiToken(String),
-}
-
-fn extract_auth_data(headers: &http::HeaderMap) -> Option<AuthData> {
- if let Some(raw_cookie) = headers.get(header::COOKIE) {
- if let Ok(cookie) = raw_cookie.to_str() {
- if let Some(ticket) = tools::extract_cookie(cookie, "PBSAuthCookie") {
- let csrf_token = match headers.get("CSRFPreventionToken").map(|v| v.to_str()) {
- Some(Ok(v)) => Some(v.to_owned()),
- _ => None,
- };
- return Some(AuthData::User(UserAuthData {
- ticket,
- csrf_token,
- }));
- }
- }
- }
-
- match headers.get(header::AUTHORIZATION).map(|v| v.to_str()) {
- Some(Ok(v)) => {
- if v.starts_with("PBSAPIToken ") || v.starts_with("PBSAPIToken=") {
- Some(AuthData::ApiToken(v["PBSAPIToken ".len()..].to_owned()))
- } else {
- None
- }
- },
- _ => None,
- }
-}
-
-fn check_auth(
- method: &hyper::Method,
- auth_data: &AuthData,
- user_info: &CachedUserInfo,
-) -> Result<Authid, Error> {
- match auth_data {
- AuthData::User(user_auth_data) => {
- let ticket = user_auth_data.ticket.clone();
- let ticket_lifetime = tools::ticket::TICKET_LIFETIME;
-
- let userid: Userid = Ticket::<super::ticket::ApiTicket>::parse(&ticket)?
- .verify_with_time_frame(public_auth_key(), "PBS", None, -300..ticket_lifetime)?
- .require_full()?;
-
- let auth_id = Authid::from(userid.clone());
- if !user_info.is_active_auth_id(&auth_id) {
- bail!("user account disabled or expired.");
- }
-
- if method != hyper::Method::GET {
- if let Some(csrf_token) = &user_auth_data.csrf_token {
- verify_csrf_prevention_token(csrf_secret(), &userid, &csrf_token, -300, ticket_lifetime)?;
- } else {
- bail!("missing CSRF prevention token");
- }
- }
-
- Ok(auth_id)
- },
- AuthData::ApiToken(api_token) => {
- let mut parts = api_token.splitn(2, ':');
- let tokenid = parts.next()
- .ok_or_else(|| format_err!("failed to split API token header"))?;
- let tokenid: Authid = tokenid.parse()?;
-
- if !user_info.is_active_auth_id(&tokenid) {
- bail!("user account or token disabled or expired.");
- }
-
- let tokensecret = parts.next()
- .ok_or_else(|| format_err!("failed to split API token header"))?;
- let tokensecret = percent_decode_str(tokensecret)
- .decode_utf8()
- .map_err(|_| format_err!("failed to decode API token header"))?;
-
- crate::config::token_shadow::verify_secret(&tokenid, &tokensecret)?;
-
- Ok(tokenid)
- }
- }
-}
-
async fn handle_request(
api: Arc<ApiConfig>,
req: Request<Body>,
--
2.20.1
More information about the pbs-devel
mailing list