From: Stefan Reiter <s.reiter@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH v2 proxmox-backup 08/20] server/rest: extract auth to seperate module
Date: Wed, 24 Mar 2021 16:18:15 +0100 [thread overview]
Message-ID: <20210324151827.26200-9-s.reiter@proxmox.com> (raw)
In-Reply-To: <20210324151827.26200-1-s.reiter@proxmox.com>
Signed-off-by: Stefan Reiter <s.reiter@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
next prev parent reply other threads:[~2021-03-24 15:21 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-03-24 15:18 [pbs-devel] [PATCH v2 00/20] Single file restore for VM images Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 pxar 01/20] decoder/aio: add contents() and content_size() calls Stefan Reiter
2021-03-25 9:08 ` Wolfgang Bumiller
2021-03-25 9:23 ` Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 02/20] vsock_client: remove wrong comment Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 03/20] vsock_client: remove some &mut restrictions and rustfmt Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 04/20] vsock_client: support authorization header Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 05/20] proxmox_client_tools: move common key related functions to key_source.rs Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 06/20] file-restore: add binary and basic commands Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 07/20] file-restore: allow specifying output-format Stefan Reiter
2021-03-24 15:18 ` Stefan Reiter [this message]
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 09/20] server/rest: add ApiAuth trait to make user auth generic Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 10/20] file-restore-daemon: add binary with virtio-vsock API server Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 11/20] file-restore-daemon: add watchdog module Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 12/20] file-restore-daemon: add disk module Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 13/20] add tools/cpio encoding module Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 14/20] file-restore: add qemu-helper setuid binary Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 15/20] file-restore: add basic VM/block device support Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 16/20] debian/client: add postinst hook to rebuild file-restore initramfs Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 17/20] file-restore(-daemon): implement list API Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 18/20] pxar/extract: add sequential variant to extract_sub_dir Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 19/20] tools/zip: add zip_directory helper Stefan Reiter
2021-03-24 15:18 ` [pbs-devel] [PATCH v2 proxmox-backup 20/20] file-restore: add 'extract' command for VM file restore Stefan Reiter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210324151827.26200-9-s.reiter@proxmox.com \
--to=s.reiter@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox