1 //! Provides authentication primitives for the HTTP server
3 use anyhow
::format_err
;
5 use proxmox
::api
::UserInformation
;
7 use pbs_tools
::ticket
::{self, Ticket}
;
8 use pbs_config
::{token_shadow, CachedUserInfo}
;
9 use pbs_api_types
::{Authid, Userid}
;
10 use proxmox_rest_server
::{AuthError, extract_cookie}
;
12 use crate::auth_helpers
::*;
15 use percent_encoding
::percent_decode_str
;
19 csrf_token
: Option
<String
>,
27 fn extract_auth_data(headers
: &http
::HeaderMap
) -> Option
<AuthData
> {
28 if let Some(raw_cookie
) = headers
.get(header
::COOKIE
) {
29 if let Ok(cookie
) = raw_cookie
.to_str() {
30 if let Some(ticket
) = extract_cookie(cookie
, "PBSAuthCookie") {
31 let csrf_token
= match headers
.get("CSRFPreventionToken").map(|v
| v
.to_str()) {
32 Some(Ok(v
)) => Some(v
.to_owned()),
35 return Some(AuthData
::User(UserAuthData { ticket, csrf_token }
));
40 match headers
.get(header
::AUTHORIZATION
).map(|v
| v
.to_str()) {
42 if v
.starts_with("PBSAPIToken ") || v
.starts_with("PBSAPIToken=") {
43 Some(AuthData
::ApiToken(v
["PBSAPIToken ".len()..].to_owned()))
52 pub async
fn check_pbs_auth(
53 headers
: &http
::HeaderMap
,
54 method
: &hyper
::Method
,
55 ) -> Result
<(String
, Box
<dyn UserInformation
+ Sync
+ Send
>), AuthError
> {
57 // fixme: make all IO async
59 let user_info
= CachedUserInfo
::new()?
;
61 let auth_data
= extract_auth_data(headers
);
63 Some(AuthData
::User(user_auth_data
)) => {
64 let ticket
= user_auth_data
.ticket
.clone();
65 let ticket_lifetime
= ticket
::TICKET_LIFETIME
;
67 let userid
: Userid
= Ticket
::<super::ticket
::ApiTicket
>::parse(&ticket
)?
68 .verify_with_time_frame(public_auth_key(), "PBS", None
, -300..ticket_lifetime
)?
71 let auth_id
= Authid
::from(userid
.clone());
72 if !user_info
.is_active_auth_id(&auth_id
) {
73 return Err(format_err
!("user account disabled or expired.").into());
76 if method
!= hyper
::Method
::GET
{
77 if let Some(csrf_token
) = &user_auth_data
.csrf_token
{
78 verify_csrf_prevention_token(
86 return Err(format_err
!("missing CSRF prevention token").into());
90 Ok((auth_id
.to_string(), Box
::new(user_info
)))
92 Some(AuthData
::ApiToken(api_token
)) => {
93 let mut parts
= api_token
.splitn(2, '
:'
);
96 .ok_or_else(|| format_err
!("failed to split API token header"))?
;
97 let tokenid
: Authid
= tokenid
.parse()?
;
99 if !user_info
.is_active_auth_id(&tokenid
) {
100 return Err(format_err
!("user account or token disabled or expired.").into());
103 let tokensecret
= parts
105 .ok_or_else(|| format_err
!("failed to split API token header"))?
;
106 let tokensecret
= percent_decode_str(tokensecret
)
108 .map_err(|_
| format_err
!("failed to decode API token header"))?
;
110 token_shadow
::verify_secret(&tokenid
, &tokensecret
)?
;
112 Ok((tokenid
.to_string(), Box
::new(user_info
)))
114 None
=> Err(AuthError
::NoData
),