Compare commits

...

3 Commits

Author SHA1 Message Date
Lennart K
ee1faa4c20 version 0.4.8 2025-07-01 14:09:58 +02:00
Lennart K
1e999ca0cc feat(frontend): Add bodged field to create group collections 2025-07-01 14:09:32 +02:00
Lennart K
f27245f996 fix(store_sqlite): Principal upsert 2025-07-01 13:49:43 +02:00
9 changed files with 65 additions and 31 deletions

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "\n REPLACE INTO principals\n (id, displayname, principal_type, password_hash)\n VALUES (?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "2f043f62a7c0eae1023e319f0bc8f35dfdcf6a8247e03b1de3e2cabb2d3ab8ae"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO principals\n (id, displayname, principal_type, password_hash) VALUES (?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n (displayname, principal_type, password_hash)\n = (excluded.displayname, excluded.principal_type, excluded.password_hash)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "5c09c2a3c052188435409d4ff076575394e625dd19f00dea2d4c71a9f34a5952"
}

22
Cargo.lock generated
View File

@@ -2999,7 +2999,7 @@ dependencies = [
[[package]]
name = "rustical"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"anyhow",
"argon2",
@@ -3042,7 +3042,7 @@ dependencies = [
[[package]]
name = "rustical_caldav"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"async-std",
"async-trait",
@@ -3080,7 +3080,7 @@ dependencies = [
[[package]]
name = "rustical_carddav"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"async-trait",
"axum",
@@ -3112,7 +3112,7 @@ dependencies = [
[[package]]
name = "rustical_dav"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"async-trait",
"axum",
@@ -3137,7 +3137,7 @@ dependencies = [
[[package]]
name = "rustical_dav_push"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"async-trait",
"axum",
@@ -3163,7 +3163,7 @@ dependencies = [
[[package]]
name = "rustical_frontend"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"askama",
"askama_web",
@@ -3196,7 +3196,7 @@ dependencies = [
[[package]]
name = "rustical_ical"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"axum",
"chrono",
@@ -3214,7 +3214,7 @@ dependencies = [
[[package]]
name = "rustical_oidc"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"async-trait",
"axum",
@@ -3229,7 +3229,7 @@ dependencies = [
[[package]]
name = "rustical_store"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"anyhow",
"async-trait",
@@ -3263,7 +3263,7 @@ dependencies = [
[[package]]
name = "rustical_store_sqlite"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"async-trait",
"chrono",
@@ -3284,7 +3284,7 @@ dependencies = [
[[package]]
name = "rustical_xml"
version = "0.4.7"
version = "0.4.8"
dependencies = [
"quick-xml",
"thiserror 2.0.12",

View File

@@ -2,7 +2,7 @@
members = ["crates/*"]
[workspace.package]
version = "0.4.7"
version = "0.4.8"
edition = "2024"
description = "A CalDAV server"
repository = "https://github.com/lennart-k/rustical"

View File

@@ -19,6 +19,8 @@ export class CreateAddressbookForm extends LitElement {
@property()
user: String = ''
@property()
principal: String = ''
@property()
addr_id: String = ''
@property()
displayname: String = ''
@@ -34,6 +36,11 @@ export class CreateAddressbookForm extends LitElement {
<dialog ${ref(this.dialog)}>
<h3>Create addressbook</h3>
<form @submit=${this.submit} ${ref(this.form)}>
<label>
principal (for group addressbooks)
<input type="text" name="principal" value=${this.user} @change=${e => this.principal = e.target.value} />
</label>
<br>
<label>
id
<input type="text" name="id" @change=${e => this.addr_id = e.target.value} />
@@ -68,7 +75,7 @@ export class CreateAddressbookForm extends LitElement {
return
}
// TODO: Escape user input: There's not really a security risk here but would be nicer
await this.client.createDirectory(`/principal/${this.user}/${this.addr_id}`, {
await this.client.createDirectory(`/principal/${this.principal || this.user}/${this.addr_id}`, {
data: `
<mkcol xmlns="DAV:" xmlns:CARD="urn:ietf:params:xml:ns:carddav">
<set>

View File

@@ -18,6 +18,8 @@ export class CreateCalendarForm extends LitElement {
@property()
user: String = ''
@property()
principal: String = ''
@property()
cal_id: String = ''
@property()
displayname: String = ''
@@ -40,6 +42,11 @@ export class CreateCalendarForm extends LitElement {
<dialog ${ref(this.dialog)}>
<h3>Create calendar</h3>
<form @submit=${this.submit} ${ref(this.form)}>
<label>
principal (for group calendar)
<input type="text" name="principal" value=${this.user} @change=${e => this.principal = e.target.value} />
</label>
<br>
<label>
id
<input type="text" name="id" @change=${e => this.cal_id = e.target.value} />
@@ -94,7 +101,7 @@ export class CreateCalendarForm extends LitElement {
alert("No calendar components selected")
return
}
await this.client.createDirectory(`/principal/${this.user}/${this.cal_id}`, {
await this.client.createDirectory(`/principal/${this.principal || this.user}/${this.cal_id}`, {
data: `
<mkcol xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CS="http://calendarserver.org/ns/" xmlns:ICAL="http://apple.com/ns/ical/">
<set>

View File

@@ -17,6 +17,7 @@ let CreateAddressbookForm = class extends i {
super();
this.client = an("/carddav");
this.user = "";
this.principal = "";
this.addr_id = "";
this.displayname = "";
this.description = "";
@@ -32,6 +33,11 @@ let CreateAddressbookForm = class extends i {
<dialog ${n(this.dialog)}>
<h3>Create addressbook</h3>
<form @submit=${this.submit} ${n(this.form)}>
<label>
principal (for group addressbooks)
<input type="text" name="principal" value=${this.user} @change=${(e2) => this.principal = e2.target.value} />
</label>
<br>
<label>
id
<input type="text" name="id" @change=${(e2) => this.addr_id = e2.target.value} />
@@ -68,7 +74,7 @@ let CreateAddressbookForm = class extends i {
alert("Empty displayname");
return;
}
await this.client.createDirectory(`/principal/${this.user}/${this.addr_id}`, {
await this.client.createDirectory(`/principal/${this.principal || this.user}/${this.addr_id}`, {
data: `
<mkcol xmlns="DAV:" xmlns:CARD="urn:ietf:params:xml:ns:carddav">
<set>
@@ -87,6 +93,9 @@ let CreateAddressbookForm = class extends i {
__decorateClass([
n$1()
], CreateAddressbookForm.prototype, "user", 2);
__decorateClass([
n$1()
], CreateAddressbookForm.prototype, "principal", 2);
__decorateClass([
n$1()
], CreateAddressbookForm.prototype, "addr_id", 2);

View File

@@ -17,6 +17,7 @@ let CreateCalendarForm = class extends i {
super();
this.client = an("/caldav");
this.user = "";
this.principal = "";
this.cal_id = "";
this.displayname = "";
this.description = "";
@@ -35,6 +36,11 @@ let CreateCalendarForm = class extends i {
<dialog ${n(this.dialog)}>
<h3>Create calendar</h3>
<form @submit=${this.submit} ${n(this.form)}>
<label>
principal (for group calendar)
<input type="text" name="principal" value=${this.user} @change=${(e2) => this.principal = e2.target.value} />
</label>
<br>
<label>
id
<input type="text" name="id" @change=${(e2) => this.cal_id = e2.target.value} />
@@ -92,7 +98,7 @@ let CreateCalendarForm = class extends i {
alert("No calendar components selected");
return;
}
await this.client.createDirectory(`/principal/${this.user}/${this.cal_id}`, {
await this.client.createDirectory(`/principal/${this.principal || this.user}/${this.cal_id}`, {
data: `
<mkcol xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CS="http://calendarserver.org/ns/" xmlns:ICAL="http://apple.com/ns/ical/">
<set>
@@ -116,6 +122,9 @@ let CreateCalendarForm = class extends i {
__decorateClass([
n$1()
], CreateCalendarForm.prototype, "user", 2);
__decorateClass([
n$1()
], CreateCalendarForm.prototype, "principal", 2);
__decorateClass([
n$1()
], CreateCalendarForm.prototype, "cal_id", 2);

View File

@@ -114,9 +114,11 @@ impl AuthenticationProvider for SqlitePrincipalStore {
let password = user.password.map(Secret::into_inner);
sqlx::query!(
r#"
REPLACE INTO principals
(id, displayname, principal_type, password_hash)
VALUES (?, ?, ?, ?)
INSERT INTO principals
(id, displayname, principal_type, password_hash) VALUES (?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
(displayname, principal_type, password_hash)
= (excluded.displayname, excluded.principal_type, excluded.password_hash)
"#,
user.id,
user.displayname,