diff --git a/README.md b/README.md
index 567df58..151ba5d 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ a CalDAV/CardDAV server
- adequately fast (I'd love to say blazingly fastâ„¢ :fire: but I don't have any benchmarks)
- deleted calendars are recoverable
- Nextcloud login flow (In DAVx5 you can login through the Nextcloud flow and automatically generate an app token)
+- Apple configuration profiles (skip copy-pasting passwords and instead generate the configuration in the frontend)
- OpenID Connect support (with option to disable password login)
## Getting Started
diff --git a/crates/frontend/public/templates/apple_configuration/template.xml b/crates/frontend/public/templates/apple_configuration/template.xml
new file mode 100644
index 0000000..68fc243
--- /dev/null
+++ b/crates/frontend/public/templates/apple_configuration/template.xml
@@ -0,0 +1,115 @@
+
+
+
+
+ PayloadContent
+
+
+ CalDAVAccountDescription
+ {{ account_description }}
+
+ CalDAVHostName
+ {{ hostname }}
+
+ CalDAVPrincipalURL
+ {{ caldav_principal_url }}
+
+ CalDAVUseSSL
+
+
+ CalDAVUsername
+ {{ user }}
+
+ CalDAVPassword
+ {{ token }}
+
+ PayloadDescription
+ {{ user }} RustiCal CalDAV profile
+
+ PayloadDisplayName
+ {{ user }} RustiCal CalDAV profile
+
+ PayloadIdentifier
+ com.github.lennart-k.rustical.cal.{{ user }}
+
+ PayloadOrganization
+ RustiCal
+
+ PayloadType
+ com.apple.caldav.account
+
+ PayloadUUID
+ {{ caldav_profile_uuid }}
+
+ PayloadVersion
+ 1
+
+
+
+ CardDAVAccountDescription
+ {{ account_description }}
+
+ CardDAVHostName
+ {{ hostname }}
+
+ CardDAVPrincipalURL
+ {{ carddav_principal_url }}
+
+ CardDAVUseSSL
+
+
+ CardDAVUsername
+ {{ user }}
+
+ CardDAVPassword
+ {{ token }}
+
+ PayloadDescription
+ {{ user }} RustiCal CardDAV profile
+
+ PayloadDisplayName
+ {{ user }} RustiCal CardDAV profile
+
+ PayloadIdentifier
+ com.github.lennart-k.rustical.card.{{ user }}
+
+ PayloadOrganization
+ RustiCal
+
+ PayloadType
+ com.apple.carddav.account
+
+ PayloadUUID
+ {{ carddav_profile_uuid }}
+
+ PayloadVersion
+ 1
+
+
+
+
+ PayloadDescription
+ Set up your RustiCal CalDAV/CardDAV account for {{ user }}@{{ hostname }} with app token {{ token_name }}
+
+ PayloadDisplayName
+ RustiCal CalDAV/CardDAV
+
+ PayloadIdentifier
+ com.github.lennart-k.rustical.{{ user }}
+
+ PayloadOrganization
+ {{ hostname }}
+
+ PayloadRemovalDisallowed
+
+
+ PayloadType
+ Configuration
+
+ PayloadUUID
+ {{ plist_uuid }}
+
+ PayloadVersion
+ 1
+
+
diff --git a/crates/frontend/public/templates/pages/user.html b/crates/frontend/public/templates/pages/user.html
index 38f0344..1416d53 100644
--- a/crates/frontend/public/templates/pages/user.html
+++ b/crates/frontend/public/templates/pages/user.html
@@ -29,7 +29,6 @@
{% if let Some(created_at) = app_token.created_at %}
{{ chrono_humanize::HumanTime::from(created_at.to_owned()) }}
-
{% endif %}
@@ -49,6 +48,9 @@
+ {% if is_apple %}
+
+ {% endif %}
diff --git a/crates/frontend/src/lib.rs b/crates/frontend/src/lib.rs
index 9d4ca68..c4d94aa 100644
--- a/crates/frontend/src/lib.rs
+++ b/crates/frontend/src/lib.rs
@@ -7,7 +7,7 @@ use actix_web::{
HttpRequest, HttpResponse, Responder,
cookie::{Key, SameSite},
dev::ServiceResponse,
- http::{Method, StatusCode},
+ http::{Method, StatusCode, header},
middleware::{ErrorHandlerResponse, ErrorHandlers},
web::{self, Data, Form, Path, Redirect},
};
@@ -28,6 +28,7 @@ use rustical_store::{
};
use serde::Deserialize;
use std::sync::Arc;
+use uuid::Uuid;
mod assets;
mod config;
@@ -56,6 +57,7 @@ struct UserPage {
pub deleted_calendars: Vec,
pub addressbooks: Vec,
pub deleted_addressbooks: Vec,
+ pub is_apple: bool,
}
async fn route_user_named(
@@ -66,7 +68,6 @@ async fn route_user_named impl Responder {
- // TODO: Check for authorization
let user_id = path.into_inner();
if user_id != user.id {
return actix_web::HttpResponse::Unauthorized().body("Unauthorized");
@@ -92,6 +93,13 @@ async fn route_user_named, req: HttpRequest) -> impl Responder {
web::Redirect::to(redirect_url.to_string()).permanent()
}
+#[derive(Template)]
+#[template(path = "apple_configuration/template.xml")]
+pub struct AppleConfig {
+ token_name: String,
+ account_description: String,
+ hostname: String,
+ caldav_principal_url: String,
+ carddav_principal_url: String,
+ user: String,
+ token: String,
+ caldav_profile_uuid: Uuid,
+ carddav_profile_uuid: Uuid,
+ plist_uuid: Uuid,
+}
+
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct PostAppTokenForm {
name: String,
+ #[serde(default)]
+ apple: bool,
}
async fn route_post_app_token(
user: User,
auth_provider: Data,
path: Path,
- Form(PostAppTokenForm { name }): Form,
-) -> Result {
+ Form(PostAppTokenForm { apple, name }): Form,
+ req: HttpRequest,
+) -> Result {
assert!(!name.is_empty());
assert_eq!(path.into_inner(), user.id);
let token = generate_app_token();
auth_provider
- .add_app_token(&user.id, name, token.clone())
+ .add_app_token(&user.id, name.to_owned(), token.clone())
.await?;
- Ok(token)
+ if apple {
+ let hostname = req.full_url().host_str().unwrap().to_owned();
+ let profile = AppleConfig {
+ token_name: name,
+ account_description: format!("{}@{}", &user.id, &hostname),
+ hostname,
+ caldav_principal_url: req
+ .url_for("caldav_principal", [&user.id])
+ .unwrap()
+ .to_string(),
+ carddav_principal_url: req
+ .url_for("carddav_principal", [&user.id])
+ .unwrap()
+ .to_string(),
+ user: user.id.to_owned(),
+ token,
+ caldav_profile_uuid: Uuid::new_v4(),
+ carddav_profile_uuid: Uuid::new_v4(),
+ plist_uuid: Uuid::new_v4(),
+ }
+ .render()
+ .unwrap();
+ Ok(HttpResponse::Ok()
+ .insert_header(header::ContentDisposition::attachment(format!(
+ "rustical-{}.mobileconfig",
+ user.id
+ )))
+ .insert_header((
+ header::CONTENT_TYPE,
+ "application/x-apple-aspen-config; charset=utf-8",
+ ))
+ .body(profile))
+ } else {
+ Ok(HttpResponse::Ok().body(token))
+ }
}
async fn route_delete_app_token(
diff --git a/docs/index.md b/docs/index.md
index bc33b35..ff65cbd 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,10 +3,10 @@
a CalDAV/CardDAV server
!!! warning
- RustiCal is **not production-ready!**
- I'm just starting to use it myself so I cannot guarantee that everything will be working smoothly just yet.
- I hope there won't be any manual migrations anymore but if you want to be an early adopter some SQL knowledge might be useful just in case.
- If you still want to play around with it in its current state, absolutely feel free to do so and to open up an issue if something is not working. :)
+RustiCal is **not production-ready!**
+I'm just starting to use it myself so I cannot guarantee that everything will be working smoothly just yet.
+I hope there won't be any manual migrations anymore but if you want to be an early adopter some SQL knowledge might be useful just in case.
+If you still want to play around with it in its current state, absolutely feel free to do so and to open up an issue if something is not working. :)
## Features
@@ -16,4 +16,5 @@ a CalDAV/CardDAV server
- adequately fast (I'd love to say blazingly fastâ„¢ :fire: but I don't have any benchmarks)
- deleted calendars are recoverable
- Nextcloud login flow (In DAVx5 you can login through the Nextcloud flow and automatically generate an app token)
+- Apple configuration profiles (skip copy-pasting passwords and instead generate the configuration in the frontend)
- [OpenID Connect](setup/oidc.md) support (with option to disable password login)