rustical_store/auth/
principal.rs1use crate::{Secret, auth::PrincipalType};
2use axum::{
3 body::Body,
4 extract::{FromRequestParts, OptionalFromRequestParts},
5 response::{IntoResponse, Response},
6};
7use chrono::{DateTime, Utc};
8use derive_more::Display;
9use http::{HeaderValue, StatusCode, header};
10use serde::{Deserialize, Serialize};
11use std::convert::Infallible;
12
13#[derive(Debug, Clone, Deserialize, Serialize)]
14pub struct AppToken {
15 pub id: String,
16 pub name: String,
17 pub token: Secret<String>,
18 pub created_at: Option<DateTime<Utc>>,
19}
20
21#[derive(Debug, Clone, Deserialize, Serialize)]
22#[serde(deny_unknown_fields)]
23pub struct Principal {
24 pub id: String,
25 pub displayname: Option<String>,
26 #[serde(default)]
27 pub principal_type: PrincipalType,
28 #[serde(skip_serializing)]
29 pub password: Option<Secret<String>>,
30 #[serde(default)]
31 pub memberships: Vec<String>,
32}
33
34impl Principal {
35 #[must_use]
39 pub fn is_principal(&self, principal: &str) -> bool {
40 if self.id == principal {
41 return true;
42 }
43 self.memberships
44 .iter()
45 .any(|membership| membership == principal)
46 }
47
48 pub fn memberships(&self) -> Vec<&str> {
50 let mut memberships: Vec<_> = self.memberships.iter().map(String::as_ref).collect();
51 memberships.push(self.id.as_str());
52 memberships
53 }
54
55 pub fn memberships_without_self(&self) -> Vec<&str> {
56 self.memberships.iter().map(String::as_str).collect()
57 }
58}
59
60impl rustical_dav::Principal for Principal {
61 fn get_id(&self) -> &str {
62 &self.id
63 }
64}
65
66#[derive(Clone, Debug, Display)]
67pub struct UnauthorizedError;
68
69impl IntoResponse for UnauthorizedError {
70 fn into_response(self) -> axum::response::Response {
71 let mut resp = Response::builder().status(StatusCode::UNAUTHORIZED);
72 resp.headers_mut().unwrap().insert(
73 header::WWW_AUTHENTICATE,
74 HeaderValue::from_static(r#"Basic realm="RustiCal", charset="UTF-8""#),
75 );
76 resp.body(Body::empty()).unwrap()
77 }
78}
79
80impl<S: Send + Sync + Clone> FromRequestParts<S> for Principal {
81 type Rejection = UnauthorizedError;
82
83 async fn from_request_parts(
84 parts: &mut http::request::Parts,
85 _state: &S,
86 ) -> Result<Self, Self::Rejection> {
87 parts
88 .extensions
89 .get::<Self>()
90 .cloned()
91 .ok_or(UnauthorizedError)
92 }
93}
94
95impl<S: Send + Sync + Clone> OptionalFromRequestParts<S> for Principal {
96 type Rejection = Infallible;
97
98 async fn from_request_parts(
99 parts: &mut http::request::Parts,
100 _state: &S,
101 ) -> Result<Option<Self>, Self::Rejection> {
102 Ok(parts.extensions.get::<Self>().cloned())
103 }
104}