diff --git a/crates/caldav/src/principal/mod.rs b/crates/caldav/src/principal/mod.rs index 4a8f860..6595072 100644 --- a/crates/caldav/src/principal/mod.rs +++ b/crates/caldav/src/principal/mod.rs @@ -6,6 +6,7 @@ use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{NamedRoute, Resource, ResourceService}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; +use rustical_store::auth::user::PrincipalType; use rustical_store::auth::User; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; @@ -26,7 +27,7 @@ pub enum PrincipalProp { // Scheduling Extensions to CalDAV (RFC 6638) #[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)] - CalendarUserType(&'static str), + CalendarUserType(PrincipalType), #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] CalendarUserAddressSet(HrefElement), @@ -78,6 +79,7 @@ impl Resource for PrincipalResource { ) -> Result { let principal_url = Self::get_url(rmap, vec![&self.principal]).unwrap(); + // BUG: We need to read the properties of the principal, not the requesting user let home_set = CalendarHomeSet( user.memberships() .into_iter() @@ -93,9 +95,8 @@ impl Resource for PrincipalResource { Ok(match prop { PrincipalPropWrapperName::Principal(prop) => { PrincipalPropWrapper::Principal(match prop { - // TODO: principal types PrincipalPropName::CalendarUserType => { - PrincipalProp::CalendarUserType("INDIVIDUAL") + PrincipalProp::CalendarUserType(user.user_type.to_owned()) } PrincipalPropName::Displayname => { PrincipalProp::Displayname(self.principal.to_owned()) diff --git a/crates/store/src/auth/user.rs b/crates/store/src/auth/user.rs index 9569c3d..5c15b53 100644 --- a/crates/store/src/auth/user.rs +++ b/crates/store/src/auth/user.rs @@ -4,14 +4,44 @@ use actix_web::{ FromRequest, HttpMessage, HttpResponse, ResponseError, }; use derive_more::Display; +use rustical_xml::ValueSerialize; use serde::{Deserialize, Serialize}; use std::future::{ready, Ready}; +/// https://datatracker.ietf.org/doc/html/rfc5545#section-3.2.3 +#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum PrincipalType { + #[default] + Individual, + Group, + Resource, + Room, + Unknown, + // TODO: X-Name, IANA-token +} + +impl ValueSerialize for PrincipalType { + fn serialize(&self) -> String { + match self { + PrincipalType::Individual => "INDIVIDUAL", + PrincipalType::Group => "GROUP", + PrincipalType::Resource => "RESOURCE", + PrincipalType::Room => "ROOM", + PrincipalType::Unknown => "UNKNOWN", + } + .to_owned() + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(deny_unknown_fields)] +// TODO: Rename this to Principal pub struct User { pub id: String, pub displayname: Option, + #[serde(default)] + pub user_type: PrincipalType, pub password: Option, #[serde(default)] pub app_tokens: Vec, diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2dcc442..1f54a45 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -29,6 +29,7 @@ pub fn cmd_gen_config(_args: GenConfigArgs) -> anyhow::Result<()> { users: vec![User { id: "default".to_owned(), displayname: Some("Default user".to_owned()), + user_type: Default::default(), password: Some( "generate a password hash with rustical pwhash --algorithm argon2".to_owned(), ),